configliere 0.3.4 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/.document +3 -0
  2. data/.watchr +20 -0
  3. data/CHANGELOG.textile +99 -3
  4. data/Gemfile +26 -0
  5. data/Gemfile.lock +54 -0
  6. data/README.textile +162 -138
  7. data/Rakefile +30 -21
  8. data/VERSION +1 -1
  9. data/bin/configliere +77 -77
  10. data/bin/configliere-decrypt +85 -0
  11. data/bin/configliere-delete +85 -0
  12. data/bin/configliere-dump +85 -0
  13. data/bin/configliere-encrypt +85 -0
  14. data/bin/configliere-list +85 -0
  15. data/bin/configliere-set +85 -0
  16. data/configliere.gemspec +53 -23
  17. data/examples/config_block_script.rb +9 -2
  18. data/examples/encrypted_script.rb +28 -16
  19. data/examples/env_var_script.rb +2 -2
  20. data/examples/help_message_demo.rb +16 -0
  21. data/examples/independent_config.rb +28 -0
  22. data/examples/prompt.rb +23 -0
  23. data/examples/simple_script.rb +28 -15
  24. data/examples/simple_script.yaml +1 -1
  25. data/lib/configliere.rb +22 -24
  26. data/lib/configliere/commandline.rb +135 -116
  27. data/lib/configliere/commands.rb +38 -54
  28. data/lib/configliere/config_block.rb +4 -2
  29. data/lib/configliere/config_file.rb +30 -52
  30. data/lib/configliere/crypter.rb +8 -5
  31. data/lib/configliere/deep_hash.rb +368 -0
  32. data/lib/configliere/define.rb +83 -89
  33. data/lib/configliere/encrypted.rb +17 -18
  34. data/lib/configliere/env_var.rb +5 -7
  35. data/lib/configliere/param.rb +37 -64
  36. data/lib/configliere/prompt.rb +23 -0
  37. data/spec/configliere/commandline_spec.rb +156 -57
  38. data/spec/configliere/commands_spec.rb +75 -30
  39. data/spec/configliere/config_block_spec.rb +10 -1
  40. data/spec/configliere/config_file_spec.rb +83 -55
  41. data/spec/configliere/crypter_spec.rb +3 -2
  42. data/spec/configliere/deep_hash_spec.rb +401 -0
  43. data/spec/configliere/define_spec.rb +121 -42
  44. data/spec/configliere/encrypted_spec.rb +53 -20
  45. data/spec/configliere/env_var_spec.rb +24 -4
  46. data/spec/configliere/param_spec.rb +25 -27
  47. data/spec/configliere/prompt_spec.rb +50 -0
  48. data/spec/configliere_spec.rb +3 -9
  49. data/spec/spec_helper.rb +17 -6
  50. metadata +110 -35
  51. data/lib/configliere/core_ext.rb +0 -2
  52. data/lib/configliere/core_ext/blank.rb +0 -93
  53. data/lib/configliere/core_ext/hash.rb +0 -108
  54. data/lib/configliere/core_ext/sash.rb +0 -170
  55. data/spec/configliere/core_ext/hash_spec.rb +0 -78
  56. data/spec/configliere/core_ext/sash_spec.rb +0 -312
@@ -0,0 +1,23 @@
1
+ # Settings.use :prompt
2
+ # you must install the highline gem
3
+
4
+ require 'highline/import'
5
+ module Configliere
6
+ #
7
+ # Method to prompt for
8
+ #
9
+ module Prompt
10
+
11
+ # Retrieve the given param, or prompt for it
12
+ def prompt_for attr, hint=nil
13
+ return self[attr] if has_key?(attr)
14
+ hint ||= definition_of(attr, :description)
15
+ hint = " (#{hint})" if hint
16
+ self[attr] = ask("#{attr}#{hint}? ")
17
+ end
18
+ end
19
+
20
+ Param.on_use(:prompt) do
21
+ extend Configliere::Prompt
22
+ end
23
+ end
@@ -1,116 +1,215 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
- Configliere.use :commandline
3
2
 
4
3
  describe "Configliere::Commandline" do
5
-
4
+ before do
5
+ @config = Configliere::Param.new :date => '11-05-1955', :cat => :hat
6
+ @config.use :commandline
7
+ end
6
8
  after do
7
9
  ::ARGV.replace []
8
10
  end
9
-
10
- describe "processing long-format flags" do
11
- before do
12
- @config = Configliere::Param.new :param_1 => 'val 1', :cat => :hat
13
- end
14
11
 
15
- it 'should handle --param=val pairs' do
16
- ::ARGV.replace ['--my_param=my_val']
12
+ describe "with long-format argvs" do
13
+ it 'accepts --param=val pairs' do
14
+ ::ARGV.replace ['--enchantment=under_sea']
17
15
  @config.resolve!
18
- @config.should == { :my_param => 'my_val', :param_1 => 'val 1', :cat => :hat}
16
+ @config.should == { :enchantment => 'under_sea', :date => '11-05-1955', :cat => :hat}
19
17
  end
20
- it 'should handle --dotted.param.name=val pairs' do
18
+ it 'accepts --dotted.param.name=val pairs as deep keys' do
21
19
  ::ARGV.replace ['--dotted.param.name=my_val']
22
20
  @config.resolve!
23
21
  @config.rest.should be_empty
24
- @config.should == { :dotted => { :param => { :name => 'my_val' }}, :param_1 => 'val 1', :cat => :hat}
22
+ @config.should == { :dotted => { :param => { :name => 'my_val' }}, :date => '11-05-1955', :cat => :hat }
25
23
  end
26
- it 'should handle --dashed-param-name=val pairs' do
24
+ it 'NO LONGER accepts --dashed-param-name=val pairs as deep keys' do
27
25
  ::ARGV.replace ['--dashed-param-name=my_val']
26
+ @config.should_receive(:warn).with("Configliere uses _underscores not dashes for params")
28
27
  @config.resolve!
29
28
  @config.rest.should be_empty
30
- @config.should == { :dashed => { :param => { :name => 'my_val' }}, :param_1 => 'val 1', :cat => :hat}
29
+ @config.should == { :'dashed-param-name' => 'my_val', :date => '11-05-1955', :cat => :hat }
31
30
  end
32
- it 'should handle the last-seen of the commandline values' do
33
- ::ARGV.replace ['--param_1=A', '--param_1=B']
31
+ it 'adopts only the last-seen of duplicate commandline flags' do
32
+ ::ARGV.replace ['--date=A', '--date=B']
34
33
  @config.resolve!
35
34
  @config.rest.should be_empty
36
- @config.should == { :param_1 => 'B', :cat => :hat}
35
+ @config.should == { :date => 'B', :cat => :hat}
36
+ end
37
+ it 'does NOT set a bare parameter (no "=") followed by a non-param to that value' do
38
+ ::ARGV.replace ['--date', '11-05-1985', '--heavy', '--power.source', 'household waste', 'go']
39
+ @config.resolve!
40
+ @config.rest.should == ['11-05-1985', 'household waste', 'go']
41
+ @config.should == { :date => true, :heavy => true, :power => { :source => true }, :cat => :hat }
37
42
  end
38
- it 'should set a bare parameter (no "=") to true' do
39
- ::ARGV.replace ['--param_1', '--deep.param']
43
+ it 'sets a bare parameter (no "=") to true' do
44
+ ::ARGV.replace ['--date', '--deep.param']
40
45
  @config.resolve!
41
46
  @config.rest.should be_empty
42
- @config.should == { :param_1 => true, :deep => { :param => true }, :cat => :hat}
47
+ @config.should == { :date => true, :deep => { :param => true }, :cat => :hat}
43
48
  end
44
- it 'should set an explicit blank to nil' do
45
- ::ARGV.replace ['--param_1=', '--deep.param=']
49
+ it 'sets an explicit blank to nil' do
50
+ ::ARGV.replace ['--date=', '--deep.param=']
46
51
  @config.resolve!
47
- @config.should == { :param_1 => nil, :deep => { :param => nil }, :cat => :hat}
52
+ @config.should == { :date => nil, :deep => { :param => nil }, :cat => :hat}
48
53
  end
49
54
 
50
- it 'should save non --param args into rest' do
51
- ::ARGV.replace ['--param_1', 'file1', 'file2']
55
+ it 'captures non --param args into Settings.rest' do
56
+ ::ARGV.replace ['--date', 'file1', 'file2']
52
57
  @config.resolve!
53
- @config.should == { :param_1 => true, :cat => :hat}
58
+ @config.should == { :date => true, :cat => :hat}
54
59
  @config.rest.should == ['file1', 'file2']
55
60
  end
56
61
 
57
- it 'should stop processing on "--"' do
58
- ::ARGV.replace ['--param_1=A', '--', '--param_1=B']
62
+ it 'stops processing args on "--"' do
63
+ ::ARGV.replace ['--date=A', '--', '--date=B']
59
64
  @config.resolve!
60
- @config.rest.should == ['--param_1=B']
61
- @config.should == { :param_1 => 'A', :cat => :hat}
65
+ @config.rest.should == ['--date=B']
66
+ @config.should == { :date => 'A', :cat => :hat}
62
67
  end
63
- end
64
68
 
65
- describe "processing single-letter flags" do
69
+ it 'places undefined argvs into #unknown_argvs' do
70
+ @config.define :raven, :description => 'squawk'
71
+ ::ARGV.replace ['--never=more', '--lenore', '--raven=ray_lewis']
72
+ @config.resolve!
73
+ @config.unknown_argvs.should == [:never, :lenore]
74
+ @config.should == { :date => '11-05-1955', :cat => :hat, :never => 'more', :lenore => true, :raven => 'ray_lewis' }
75
+ end
76
+ end
66
77
 
78
+ describe "with single-letter flags" do
67
79
  before do
68
- @config = Configliere::Param.new :param_1 => 'val 1', :cat => nil, :foo => nil
69
- @config.param_definitions = { :param_1 => { :flag => :p }, :cat => { :flag => 'c' } }
80
+ @config.define :date, :flag => :d
81
+ @config.define :cat, :flag => 'c'
82
+ @config.define :process, :flag => :p
70
83
  end
71
84
 
72
- it 'should parse flags given separately' do
85
+ it 'accepts them separately' do
73
86
  ::ARGV.replace ['-p', '-c']
74
87
  @config.resolve!
75
88
  @config.rest.should == []
76
- @config.should == { :param_1 => true, :cat => true, :foo => nil}
89
+ @config.should == { :date => '11-05-1955', :cat => true, :process => true}
77
90
  end
78
91
 
79
- it 'should parse flags given together' do
92
+ it 'accepts them as a group ("-abc")' do
80
93
  ::ARGV.replace ['-pc']
81
94
  @config.resolve!
82
95
  @config.rest.should == []
83
- @config.should == { :param_1 => true, :cat => true, :foo => nil}
96
+ @config.should == { :date => '11-05-1955', :cat => true, :process => true}
84
97
  end
85
98
 
86
- # it 'should parse a single-letter flag with a value' do
87
- # ::ARGV.replace ['-p=new_val', '-c']
88
- # @config.resolve!
89
- # @config.rest.should == []
90
- # @config.should == { :param_1 => 'new_val', :cat => true, :foo => nil }
91
- # end
99
+ it 'accepts a value with -d=new_val' do
100
+ ::ARGV.replace ['-d=new_val', '-c']
101
+ @config.resolve!
102
+ @config.rest.should == []
103
+ @config.should == { :date => 'new_val', :cat => true }
104
+ end
92
105
 
93
- it 'should not complain about bad single-letter flags by default' do
94
- ::ARGV.replace ['-pcz']
106
+ it 'accepts a space-separated value (-d new_val)' do
107
+ ::ARGV.replace ['-d', 'new_val', '-c', '-p']
95
108
  @config.resolve!
96
109
  @config.rest.should == []
97
- @config.should == { :param_1 => true, :cat => true, :foo => nil}
110
+ @config.should == { :date => 'new_val', :cat => true, :process => true }
111
+ end
112
+
113
+ it 'accepts a space-separated value only if the next arg is not a flag' do
114
+ ::ARGV.replace ['-d', 'new_val', '-c', '-p', 'vigorously']
115
+ @config.resolve!
116
+ @config.rest.should == []
117
+ @config.should == { :date => 'new_val', :cat => true, :process => 'vigorously' }
118
+ end
119
+
120
+ it 'stores unknown single-letter flags in unknown_argvs' do
121
+ ::ARGV.replace ['-dcz']
122
+ lambda{ @config.resolve! }.should_not raise_error(Configliere::Error)
123
+ @config.should == { :date => true, :cat => true }
124
+ @config.unknown_argvs.should == ['z']
98
125
  end
126
+ end
99
127
 
100
- it 'should raise an error about bad single-letter flags if asked' do
101
- ::ARGV.replace ['-pcz']
102
- @config.complain_about_bad_flags!
103
- lambda { @config.resolve! }.should raise_error(Configliere::Error)
128
+ def capture_help_message
129
+ stderr_output = ''
130
+ @config.should_receive(:warn){|str| stderr_output << str }
131
+ begin
132
+ yield
133
+ fail('should exit via system exit')
134
+ rescue SystemExit
135
+ true # pass
104
136
  end
105
-
137
+ stderr_output
106
138
  end
107
139
 
108
- describe "constructing help messages" do
109
- it "should not display a help message about environment variables if no environment variables exist with documentation" do
110
- @config = Configliere::Param.new :param_1 => 'val 1', :cat => :hat
111
- @config.env_var_help.should be_nil
140
+ describe "the help message" do
141
+ it 'displays help' do
142
+ ::ARGV.replace ['--help']
143
+ stderr_output = capture_help_message{ @config.resolve! }
144
+ stderr_output.should_not be_nil
145
+ stderr_output.should_not be_empty
146
+
147
+ @config.help.should_not be_nil
148
+ @config.help.should_not be_empty
149
+ end
150
+
151
+ it "displays the single-letter flags" do
152
+ @config.define :cat, :flag => :c, :description => "I like single-letter commands."
153
+ ::ARGV.replace ['--help']
154
+ stderr_output = capture_help_message{ @config.resolve! }
155
+ stderr_output.should match(/-c,/m)
156
+ end
157
+
158
+ it "displays command line options" do
159
+ ::ARGV.replace ['--help']
160
+
161
+ @config.define :logfile, :type => String, :description => "Log file name", :default => 'myapp.log', :required => false
162
+ @config.define :debug, :type => :boolean, :description => "Log debug messages to console?", :required => false
163
+ @config.define :dest_time, :type => DateTime, :description => "Arrival time", :required => true
164
+ @config.define :takes_opt, :flag => 't', :description => "Takes a single-letter flag '-t'"
165
+ @config.define :foobaz, :internal => true, :description => "You won't see me"
166
+ @config.define 'delorean.power_source', :env_var => 'POWER_SOURCE', :description => 'Delorean subsytem supplying power to the Flux Capacitor.'
167
+ @config.define :password, :required => true, :encrypted => true
168
+ @config.description = 'This is a sample script to demonstrate the help message. Notice how pretty everything lines up YAY'
169
+
170
+ stderr_output = capture_help_message{ @config.resolve! }
171
+ stderr_output.should_not be_nil
172
+ stderr_output.should_not be_empty
173
+
174
+ stderr_output.should =~ %r{--debug\s}s # type :boolean
175
+ stderr_output.should =~ %r{--logfile=String\s}s # type String
176
+ stderr_output.should =~ %r{--dest_time=DateTime[^\n]+\[Required\]}s # shows required params
177
+ stderr_output.should =~ %r{--password=String[^\n]+\[Encrypted\]}s # shows encrypted params
178
+ stderr_output.should =~ %r{--delorean.power_source=String\s}s # undefined type
179
+ stderr_output.should =~ %r{--password=String\s*password}s # uses name as dummy description
180
+ stderr_output.should =~ %r{-t, --takes_opt}s # single-letter flags
181
+
182
+ stderr_output.should =~ %r{delorean\.power_source[^\n]+Env Var: POWER_SOURCE}s # environment variable
183
+ stderr_output.should =~ %r{This is a sample script}s # extra description
184
+ end
185
+
186
+ it 'lets me die' do
187
+ stderr_output = ''
188
+ @config.should_receive(:dump_help).with("****\nhi mom\n****")
189
+ @config.should_receive(:exit).with(3)
190
+ @config.die("hi mom", 3)
191
+ end
192
+ end
193
+
194
+ describe 'recycling a commandline' do
195
+ it 'exports dashed flags' do
196
+ @config.define :has_underbar, :type => Integer, :default => 1
197
+ @config.define :not_here, :type => Integer
198
+ @config.define :is_truthy, :type => :boolean, :default => true
199
+ @config.define :is_falsehood, :type => :boolean, :default => false
200
+ @config.dashed_flags(:has_underbar, :not_here, :is_truthy, :is_falsehood, :date, :cat
201
+ ).should == ["--has-underbar=1", "--is-truthy", "--date=11-05-1955", "--cat=hat"]
112
202
  end
113
203
  end
114
-
204
+
205
+ describe '#resolve!' do
206
+ it 'calls super and returns self' do
207
+ Configliere::ParamParent.class_eval do def resolve!() dummy ; end ; end
208
+ @config.should_receive(:dummy)
209
+ @config.resolve!.should equal(@config)
210
+ Configliere::ParamParent.class_eval do def resolve!() self ; end ; end
211
+ end
212
+ end
213
+
115
214
  end
116
215
 
@@ -1,64 +1,65 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
- Configliere.use :commands
3
2
 
4
3
  describe "Configliere::Commands" do
4
+ before do
5
+ @config = Configliere::Param.new
6
+ @config.use :commands
7
+ end
5
8
 
6
9
  after do
7
10
  ::ARGV.replace []
8
11
  end
9
12
 
10
13
  describe "when no commands are defined" do
11
- before do
12
- @config = Configliere::Param.new
13
- end
14
-
15
14
  it "should know that no commands are defined" do
16
- @config.commands?.should be_false
15
+ @config.commands.should be_empty
17
16
  end
18
17
 
19
18
  it "should not shift the ARGV when resolving" do
20
19
  ::ARGV.replace ['not_command_but_arg', 'another_arg']
21
20
  @config.resolve!
22
21
  @config.rest.should == ['not_command_but_arg', 'another_arg']
23
- @config.command.should be_nil
22
+ @config.command_name.should be_nil
23
+ @config.command_info.should be_nil
24
24
  end
25
25
 
26
- it "should still recognize a git-style binary command" do
26
+ it "should still recognize a git-style-binary command" do
27
27
  ::ARGV.replace ['not_command_but_arg', 'another_arg']
28
28
  File.should_receive(:basename).and_return('prog-subcommand')
29
29
  @config.resolve!
30
30
  @config.rest.should == ['not_command_but_arg', 'another_arg']
31
- @config.command_name.should == 'subcommand'
32
- @config.command.should be_nil
31
+ @config.command_name.should == :subcommand
32
+ @config.command_info.should be_nil
33
33
  end
34
34
  end
35
-
35
+
36
36
  describe "a simple command" do
37
37
  before do
38
- @config = Configliere::Param.new :param => 'val 1'
39
- @config.define_command "the_command", :description => "foobar"
38
+ @config.defaults :fuzziness => 'smooth'
39
+ @config.define_command :the_command, :description => "foobar"
40
40
  end
41
41
 
42
42
  it "should continue to parse flags when the command is given" do
43
- ::ARGV.replace ['the_command', '--param=wuzz', 'an_arg']
43
+ ::ARGV.replace ['the_command', '--fuzziness=wuzzy', 'an_arg']
44
44
  @config.resolve!
45
- @config.should == { :param => 'wuzz' }
45
+ @config.should == { :fuzziness => 'wuzzy' }
46
46
  end
47
47
 
48
48
  it "should continue to set args when the command is given" do
49
- ::ARGV.replace ['the_command', '--param=wuzz', 'an_arg']
49
+ ::ARGV.replace ['the_command', '--fuzziness=wuzzy', 'an_arg']
50
50
  @config.resolve!
51
51
  @config.rest.should == ['an_arg']
52
52
  end
53
-
53
+
54
54
  it "should recognize the command when given" do
55
- ::ARGV.replace ['the_command', '--param=wuzz', 'an_arg']
55
+ ::ARGV.replace ['the_command', '--fuzziness=wuzzy', 'an_arg']
56
56
  @config.resolve!
57
- @config.command_name.should == 'the_command'
57
+ @config.command_name.should == :the_command
58
+ @config.command_info.should == { :description => "foobar", :config => { :fuzziness => 'wuzzy' } }
58
59
  end
59
60
 
60
61
  it "should recognize when the command is not given" do
61
- ::ARGV.replace ['bogus_command', '--param=wuzz', 'an_arg']
62
+ ::ARGV.replace ['bogus_command', '--fuzziness=wuzzy', 'an_arg']
62
63
  @config.resolve!
63
64
  @config.rest.should == ['bogus_command', 'an_arg']
64
65
  @config.command_name.should be_nil
@@ -67,27 +68,71 @@ describe "Configliere::Commands" do
67
68
 
68
69
  describe "a complex command" do
69
70
  before do
70
- @config = Configliere::Param.new :outer_param => 'val 1'
71
- @config.define_command "the_command", :description => "the command" do |command|
72
- command.define :inner_param, :description => "inside"
71
+ @config.defaults :outer => 'val 1'
72
+ @config.define_command "the_command", :description => "the command" do |cmd|
73
+ cmd.define :inner, :description => "inside"
73
74
  end
74
75
  end
75
76
 
76
77
  it "should still recognize the outer param and the args" do
77
- ::ARGV.replace ['the_command', '--outer_param=wuzz', 'an_arg', '--inner_param=buzz']
78
+ ::ARGV.replace ['the_command', '--outer=wuzzy', 'an_arg', '--inner=buzz']
78
79
  @config.resolve!
79
80
  @config.rest.should == ['an_arg']
80
- @config.command_name.should == 'the_command'
81
- @config[:outer_param].should == 'wuzz'
81
+ @config.command_name.should == :the_command
82
+ @config[:outer].should == 'wuzzy'
82
83
  end
83
84
 
84
85
  it "should recognize the inner param" do
85
- ::ARGV.replace ['the_command', '--outer_param=wuzz', 'an_arg', '--inner_param=buzz']
86
+ ::ARGV.replace ['the_command', '--outer=wuzzy', 'an_arg', '--inner=buzz']
86
87
  @config.resolve!
87
- @config[:inner_param].should == 'buzz'
88
- @config.command[:config][:inner_param].should == 'buzz'
88
+ @config[:inner].should == 'buzz'
89
+ @config.command_info[:config][:inner].should == 'buzz'
90
+ end
91
+ end
92
+
93
+
94
+ def capture_help_message
95
+ stderr_output = ''
96
+ @config.should_receive(:warn){|str| stderr_output << str }
97
+ begin
98
+ yield
99
+ fail('should exit via system exit')
100
+ rescue SystemExit
101
+ true # pass
102
+ end
103
+ stderr_output
104
+ end
105
+
106
+ describe "the help message" do
107
+ before do
108
+ @config.define_command :run, :description => "forrest"
109
+ @config.define_command :stop, :description => "hammertime"
110
+ @config.define :reel, :type => Integer
111
+ end
112
+
113
+ it "displays a modified usage" do
114
+ ::ARGV.replace ['--help']
115
+ stderr_output = capture_help_message{ @config.resolve! }
116
+ stderr_output.should =~ %r{usage:.*\[command\]}m
117
+ end
118
+
119
+ it "displays the commands and their descriptions" do
120
+ ::ARGV.replace ['--help']
121
+ stderr_output = capture_help_message{ @config.resolve! }
122
+ stderr_output.should =~ %r{Available commands:\s+run\s*forrest\s+stop\s+hammertime}m
123
+ stderr_output.should =~ %r{Params:.*--reel=Integer\s+reel}m
124
+ end
125
+ end
126
+
127
+
128
+ describe '#resolve!' do
129
+ it 'calls super and returns self' do
130
+ Configliere::ParamParent.class_eval do def resolve!() dummy ; end ; end
131
+ @config.should_receive(:dummy)
132
+ @config.resolve!.should equal(@config)
133
+ Configliere::ParamParent.class_eval do def resolve!() self ; end ; end
89
134
  end
90
-
91
135
  end
136
+
92
137
  end
93
138