Skip to content

Commit d91ffcf

Browse files
committed
Begin porting the command line interface to use Thor. #11.
This commit ports the `encrypt` submethod and the actual bin.
1 parent 5494f52 commit d91ffcf

File tree

7 files changed

+160
-72
lines changed

7 files changed

+160
-72
lines changed

bcdatabase.gemspec

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
2020
s.add_dependency "activesupport", ">= 2.0"
2121
s.add_dependency "highline", "~> 1.5"
2222
s.add_dependency "i18n"
23+
s.add_dependency 'thor', '~> 0.14.6'
2324

2425
s.add_development_dependency 'bundler', '~> 1.0.15'
2526
s.add_development_dependency 'rake', '~> 0.9.2'

bin/bcdatabase

+15-21
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,20 @@
1-
#!/usr/bin/env ruby -W
1+
#!/usr/bin/env ruby
22

3-
require 'rubygems'
4-
require 'bcdatabase/commands'
5-
6-
module Bcdatabase::Commands
7-
UTILITY_NAME = File.basename(__FILE__)
3+
# Allow this executable to be run directly from the source as well as
4+
# from an installed gem.
5+
begin
6+
lib = File.expand_path('../../lib', __FILE__)
7+
unless $LOAD_PATH.include?(lib)
8+
$LOAD_PATH << lib
9+
require 'rubygems'
10+
end
811
end
912

10-
###### MAIN
11-
12-
command = ARGV.shift
13-
unless command
14-
$stderr.puts "Please specify a command."
15-
$stderr.puts Bcdatabase::Commands.help
16-
exit(1)
17-
end
13+
require 'bcdatabase'
1814

19-
klass = Bcdatabase::Commands[command]
20-
unless klass
21-
$stderr.puts "Unknown command #{command}."
22-
$stderr.puts Bcdatabase::Commands.help
23-
exit(2)
15+
begin
16+
Bcdatabase::CLI.start
17+
rescue Interrupt => e
18+
$stderr.puts "Interrupted"
19+
exit 1
2420
end
25-
26-
exit(klass.new(ARGV).main)

lib/bcdatabase.rb

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
module Bcdatabase
1010
autoload :VERSION, 'bcdatabase/version'
11+
autoload :CLI, 'bcdatabase/cli'
1112
autoload :Commands, 'bcdatabase/commands'
1213

1314
DEFAULT_BASE_PATH = File.join('/', 'etc', 'nubic', 'db')

lib/bcdatabase/cli.rb

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
require 'bcdatabase'
2+
require 'thor'
3+
4+
module Bcdatabase
5+
class CLI < Thor
6+
desc "encrypt [INPUT [OUTPUT]]",
7+
"Encrypts every password in a bcdatabase YAML file."
8+
long_desc <<-DESC
9+
This command finds all the keys named 'password' in the input
10+
YAML and substitutes appropriate 'epassword' keys.
11+
12+
If inputfile is specified, the source will be that file. If not,
13+
the source will be standard in.
14+
15+
If inputfile and outputfile are specified, the new file will be
16+
written to the output file. Otherwise the output will go to
17+
standard out. Input and output may be the same file.
18+
19+
You can't read from standard in and write to a file directly;
20+
use shell file redirection if you need to do that.
21+
DESC
22+
def encrypt(inputfile=nil, outputfile=nil)
23+
Commands::Encrypt.new(inputfile, outputfile).run
24+
end
25+
end
26+
end

lib/bcdatabase/commands.rb

+2-51
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
HL = HighLine.new
99

1010
module Bcdatabase::Commands
11+
autoload :Encrypt, 'bcdatabase/commands/encrypt'
12+
1113
class Base
1214
protected
1315

@@ -70,57 +72,6 @@ def interactive
7072
end
7173
end
7274

73-
class Encrypt < Base
74-
def initialize(argv)
75-
@input = argv.shift
76-
@output = argv.shift
77-
end
78-
79-
def self.summary
80-
"Encrypts all the password entries in a bcdatabase YAML file"
81-
end
82-
83-
def self.help
84-
help_message("encrypt [inputfile [outputfile]]") do |msg|
85-
msg << "Specifically, this command finds all the keys named 'password'"
86-
msg << " in the input YAML and substitutes appropriate 'epassword'"
87-
msg << " keys."
88-
msg << ""
89-
msg << "If inputfile is specified, the source will be that file."
90-
msg << " If not, the source will be standard in."
91-
msg << ""
92-
msg << "If inputfile and outputfile are specified, the new file"
93-
msg << " will be written to the output file. Otherwise the output"
94-
msg << " will go to standard out. Input and output may be the same"
95-
msg << " file."
96-
msg << ""
97-
msg << "You can't read from standard in and write to a file directly; "
98-
msg << " use shell file redirection if you need to do that."
99-
end
100-
end
101-
102-
def main
103-
inio =
104-
if @input
105-
open(@input, "r")
106-
else
107-
$stdin
108-
end
109-
# try to preserve the order by replacing everything using regexes
110-
contents = inio.read
111-
contents.gsub!(/\bpassword:(\s*)(\S+)\s*?$/) { "epassword:#{$1}#{Bcdatabase.encrypt($2)}" }
112-
outio =
113-
if @output
114-
open(@output, "w")
115-
else
116-
$stdout
117-
end
118-
outio.write(contents)
119-
outio.close
120-
0
121-
end
122-
end
123-
12475
class Help < Base
12576
def initialize(argv)
12677
@cmd = argv.shift

lib/bcdatabase/commands/encrypt.rb

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
require 'bcdatabase/commands'
2+
3+
module Bcdatabase::Commands
4+
class Encrypt
5+
def initialize(inputfile=nil, outputfile=nil)
6+
@input = inputfile
7+
@output = outputfile
8+
end
9+
10+
def run
11+
inio =
12+
if @input
13+
open(@input, "r")
14+
else
15+
$stdin
16+
end
17+
# try to preserve the order by replacing everything using regexes
18+
contents = inio.read
19+
contents.gsub!(/\bpassword:(\s*)(\S+)\s*?$/) { "epassword:#{$1}#{Bcdatabase.encrypt($2)}" }
20+
outio =
21+
if @output
22+
open(@output, "w")
23+
else
24+
$stdout
25+
end
26+
outio.write(contents)
27+
outio.close
28+
end
29+
end
30+
end
+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
require File.expand_path('../../../spec_helper', __FILE__)
2+
3+
module Bcdatabase::Commands
4+
describe Encrypt do
5+
let(:pprod_path) { File.join(ENV['BCDATABASE_PATH'], 'pprod.yaml') }
6+
7+
before do
8+
enable_fake_cipherment
9+
ENV['BCDATABASE_PATH'] = (tmpdir + 'base').tap { |d| d.mkpath }
10+
11+
temporary_yaml 'pprod', {
12+
'wh' => {
13+
'password' => 'zanzibar'
14+
},
15+
'app' => {
16+
'epassword' => 'etalocohc'
17+
}
18+
}
19+
end
20+
21+
after do
22+
ENV['BCDATABASE_PATH'] = nil
23+
disable_fake_cipherment
24+
end
25+
26+
def run_with(inputfile, outputfile=nil)
27+
capture_std do
28+
Encrypt.new(inputfile, outputfile).run
29+
end
30+
end
31+
32+
def run_with_input(*lines)
33+
replace_stdin(*lines) do
34+
capture_std do
35+
Encrypt.new.run
36+
end
37+
end
38+
end
39+
40+
shared_examples 'general behavior' do
41+
let(:yaml_output) { YAML.load(output) }
42+
43+
it 'reads from the appropriate input' do
44+
output.should =~ /wh:/
45+
end
46+
47+
it 'encrypts the password' do
48+
yaml_output['wh']['epassword'].should == 'rabiznaz'
49+
end
50+
51+
it 'removes the unencrypted password' do
52+
yaml_output['wh'].should_not have_key('password')
53+
end
54+
55+
it 'leaves any existing epasswords alone' do
56+
yaml_output['app']['epassword'].should == 'etalocohc'
57+
end
58+
end
59+
60+
describe 'with zero arguments' do
61+
subject { run_with_input('wh:', ' password: zanzibar', 'app:', ' epassword: etalocohc') }
62+
63+
let(:output) { subject[:out] }
64+
65+
include_examples 'general behavior'
66+
end
67+
68+
describe 'with one argument' do
69+
subject { run_with(pprod_path) }
70+
71+
let(:output) { subject[:out] }
72+
73+
include_examples 'general behavior'
74+
end
75+
76+
describe 'with two arguments' do
77+
subject { run_with(pprod_path, out_path) }
78+
79+
let(:out_path) { tmpdir + 'encrypted.yml' }
80+
let(:output) { subject; File.read(out_path) }
81+
82+
include_examples 'general behavior'
83+
end
84+
end
85+
end

0 commit comments

Comments
 (0)