choosy 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. data/Gemfile +7 -0
  2. data/Gemfile.lock +25 -0
  3. data/LICENSE +23 -0
  4. data/README.markdown +393 -0
  5. data/Rakefile +57 -0
  6. data/lib/VERSION +1 -0
  7. data/lib/choosy/base_command.rb +59 -0
  8. data/lib/choosy/command.rb +32 -0
  9. data/lib/choosy/converter.rb +115 -0
  10. data/lib/choosy/dsl/base_command_builder.rb +172 -0
  11. data/lib/choosy/dsl/command_builder.rb +43 -0
  12. data/lib/choosy/dsl/option_builder.rb +155 -0
  13. data/lib/choosy/dsl/super_command_builder.rb +41 -0
  14. data/lib/choosy/errors.rb +11 -0
  15. data/lib/choosy/option.rb +22 -0
  16. data/lib/choosy/parse_result.rb +64 -0
  17. data/lib/choosy/parser.rb +184 -0
  18. data/lib/choosy/printing/color.rb +101 -0
  19. data/lib/choosy/printing/erb_printer.rb +23 -0
  20. data/lib/choosy/printing/help_printer.rb +174 -0
  21. data/lib/choosy/super_command.rb +77 -0
  22. data/lib/choosy/super_parser.rb +81 -0
  23. data/lib/choosy/verifier.rb +62 -0
  24. data/lib/choosy/version.rb +12 -0
  25. data/lib/choosy.rb +16 -0
  26. data/spec/choosy/base_command_spec.rb +11 -0
  27. data/spec/choosy/command_spec.rb +51 -0
  28. data/spec/choosy/converter_spec.rb +145 -0
  29. data/spec/choosy/dsl/base_command_builder_spec.rb +328 -0
  30. data/spec/choosy/dsl/commmand_builder_spec.rb +80 -0
  31. data/spec/choosy/dsl/option_builder_spec.rb +386 -0
  32. data/spec/choosy/dsl/super_command_builder_spec.rb +83 -0
  33. data/spec/choosy/parser_spec.rb +275 -0
  34. data/spec/choosy/printing/color_spec.rb +74 -0
  35. data/spec/choosy/printing/help_printer_spec.rb +117 -0
  36. data/spec/choosy/super_command_spec.rb +80 -0
  37. data/spec/choosy/super_parser_spec.rb +106 -0
  38. data/spec/choosy/verifier_spec.rb +180 -0
  39. data/spec/integration/command-A_spec.rb +37 -0
  40. data/spec/integration/supercommand-A_spec.rb +61 -0
  41. data/spec/spec_helpers.rb +30 -0
  42. metadata +150 -0
@@ -0,0 +1,386 @@
1
+ require 'spec_helpers'
2
+ require 'choosy'
3
+
4
+ module Choosy::DSL
5
+ describe OptionBuilder do
6
+ before :each do
7
+ @builder = OptionBuilder.new(:stub)
8
+ @option = @builder.option
9
+ end
10
+
11
+ describe :name do
12
+ it "should set the name" do
13
+ @option.name.should eql(:stub)
14
+ end
15
+ end
16
+
17
+ describe :short do
18
+ it "should set the option" do
19
+ @builder.short '-s'
20
+ @option.short_flag.should eql('-s')
21
+ end
22
+
23
+ describe "and the arity" do
24
+ it "should not be set without a parameter" do
25
+ @builder.short '-s'
26
+ @option.short_flag.should eql('-s')
27
+ @option.arity.should be(nil)
28
+ end
29
+
30
+ it "should be set on STD+ parameter" do
31
+ @builder.short '-s', 'STD+'
32
+ @option.arity.should eql(1..1000)
33
+ end
34
+
35
+ it "should set single arity on STD parameter" do
36
+ @builder.short '-s', 'STD'
37
+ @option.arity.should eql(1..1)
38
+ end
39
+
40
+ it "should not set the arity if :count has already been called" do
41
+ @builder.count 3
42
+ @builder.short '-s', 'STD'
43
+ @option.arity.should eql(3..3)
44
+ end
45
+ end
46
+
47
+ describe "and the flag parameter" do
48
+ it "on STD parameters" do
49
+ @builder.short '-s', 'STD'
50
+ @option.flag_parameter.should eql('STD')
51
+ end
52
+
53
+ it "on STD+ parameters" do
54
+ @builder.short '-s', 'STD+'
55
+ @option.flag_parameter.should eql('STD+')
56
+ end
57
+ end
58
+ end#short
59
+
60
+ describe :long do
61
+ it "should set the flag correctly" do
62
+ @builder.long '--short'
63
+ @option.long_flag.should eql('--short')
64
+ end
65
+
66
+ describe "while setting arity" do
67
+ it "should not set arity on empty" do
68
+ @builder.long '--short'
69
+ @option.arity.should be(nil)
70
+ end
71
+
72
+ it "should set the arity on STD to 1" do
73
+ @builder.long '--short', 'STD'
74
+ @option.arity.should eql(1..1)
75
+ end
76
+
77
+ it "should set the arity on STD+ to 1+" do
78
+ @builder.long '--short', 'STD+'
79
+ @option.arity.should eql(1..1000)
80
+ end
81
+ end#arity
82
+
83
+ describe "and the flag parameter" do
84
+ it "on STD parameters" do
85
+ @builder.long '-s', 'STD'
86
+ @option.flag_parameter.should eql('STD')
87
+ end
88
+
89
+ it "on STD+ parameters" do
90
+ @builder.long '-s', 'STD+'
91
+ @option.flag_parameter.should eql('STD+')
92
+ end
93
+ end
94
+ end#long
95
+
96
+ describe :flags do
97
+ it "should be able to set the short flag" do
98
+ @builder.flags '-s'
99
+ @option.short_flag.should eql('-s')
100
+ end
101
+
102
+ it "should the long flag unset when just the short flag is given" do
103
+ @builder.flags '-s'
104
+ @option.long_flag.should be(nil)
105
+ end
106
+
107
+ it "should leave the param nil when just the short flag is given" do
108
+ @builder.flags '-s'
109
+ @option.flag_parameter.should be(nil)
110
+ end
111
+
112
+ it "should be able to set the short when the long flag is given" do
113
+ @builder.flags '-s', '--short'
114
+ @option.short_flag.should eql('-s')
115
+ end
116
+
117
+ it "should be able to set the long flag when given" do
118
+ @builder.flags '-s', '--short'
119
+ @option.long_flag.should eql('--short')
120
+ end
121
+
122
+ it "should leave the parameter empty when not given" do
123
+ @builder.flags '-s', '--short'
124
+ @option.flag_parameter.should be(nil)
125
+ end
126
+
127
+ it "should set the parameter if given" do
128
+ @builder.flags '-s', '--short', 'SHORT'
129
+ @option.flag_parameter.should eql('SHORT')
130
+ end
131
+ end#flags
132
+
133
+ describe :desc do
134
+ it "should set the option correctly" do
135
+ @builder.desc "This is an option"
136
+ @option.description.should =~ /^This/
137
+ end
138
+ end#desc
139
+
140
+ describe :default do
141
+ it "should set the option" do
142
+ @builder.default :clear
143
+ @option.default_value.should eql(:clear)
144
+ end
145
+ end#default
146
+
147
+ describe :required do
148
+ it "should set the option" do
149
+ @builder.required
150
+ @option.required?.should be(true)
151
+ end
152
+
153
+ it "should set the option on non-nil/non-true" do
154
+ @builder.required 1
155
+ @option.required?.should be(false)
156
+ end
157
+
158
+ it "should set the option on false" do
159
+ @builder.required false
160
+ @option.required?.should be(false)
161
+ end
162
+ end#required
163
+
164
+ describe :param do
165
+ it "should be able to set the name of the parameter" do
166
+ @builder.param 'PARAM'
167
+ @option.flag_parameter.should eql('PARAM')
168
+ end
169
+
170
+ it "should set the arity on STD+ to 1+" do
171
+ @builder.param 'STD+'
172
+ @option.arity.should eql(1..1000)
173
+ end
174
+
175
+ it "should set the arity on STD to 1" do
176
+ @builder.param 'STD'
177
+ @option.arity.should eql(1..1)
178
+ end
179
+ end#param
180
+
181
+ describe :count do
182
+ describe "when welformed" do
183
+ it "should set :at_least the right arity" do
184
+ @builder.count :at_least => 32
185
+ @option.arity.should eql(32..1000)
186
+ end
187
+
188
+ it "should set :at_most the right arity" do
189
+ @builder.count :at_most => 31
190
+ @option.arity.should eql(1..31)
191
+ end
192
+
193
+ it "should set :once to the right arity" do
194
+ @builder.count :once
195
+ @option.arity.should eql(1..1)
196
+ end
197
+
198
+ it "should set :zero to the right arity" do
199
+ @builder.count :zero
200
+ @option.arity.should eql(0..0)
201
+ end
202
+
203
+ it "should set :none to the right arity" do
204
+ @builder.count :none
205
+ @option.arity.should eql(0..0)
206
+ end
207
+
208
+ it "should set a number exactly" do
209
+ @builder.count 3
210
+ @option.arity.should eql(3..3)
211
+ end
212
+ end
213
+
214
+ describe "when malformed" do
215
+ it "should fail when the :exactly isn't a number" do
216
+ attempting {
217
+ @builder.count :exactly => 'p'
218
+ }.should raise_error(Choosy::ConfigurationError, /number/)
219
+ end
220
+
221
+ it "should fail when the :at_most isn't a number" do
222
+ attempting {
223
+ @builder.count :at_most => 'p'
224
+ }.should raise_error(Choosy::ConfigurationError, /number/)
225
+ end
226
+
227
+ it "should fail when the :at_least isn't a number" do
228
+ attempting {
229
+ @builder.count :at_least => 'p'
230
+ }.should raise_error(Choosy::ConfigurationError, /number/)
231
+ end
232
+
233
+ it "should fail when the :count isn't a number" do
234
+ attempting {
235
+ @builder.count 'p'
236
+ }.should raise_error(Choosy::ConfigurationError, /number/)
237
+ end
238
+
239
+ it "should fail when the :at_least is greater than :at_most" do
240
+ attempting {
241
+ @builder.count :at_least => 3, :at_most => 2
242
+ }.should raise_error(Choosy::ConfigurationError, /lower bound/)
243
+ end
244
+ end
245
+ end#count
246
+
247
+ describe :cast do
248
+ it "should allow symbol casts" do
249
+ @builder.cast :int
250
+ @option.cast_to.should eql(:integer)
251
+ end
252
+
253
+ class CustomConverter
254
+ def convert(value)
255
+ end
256
+ end
257
+
258
+ it "should allow for custom conversions" do
259
+ conv = CustomConverter.new
260
+ @builder.cast conv
261
+ @option.cast_to.should be(conv)
262
+ end
263
+
264
+ it "should fail if it doesn't know about a Type" do
265
+ attempting {
266
+ @builder.cast Choosy::Error
267
+ }.should raise_error(Choosy::ConfigurationError, /Unknown conversion/)
268
+ end
269
+
270
+ it "should fail if it doesn't know about a symbol" do
271
+ attempting {
272
+ @builder.cast :unknown_type
273
+ }.should raise_error(Choosy::ConfigurationError, /Unknown conversion/)
274
+ end
275
+ end#cast
276
+
277
+ describe :fail do
278
+ it "should format the error message with both flags" do
279
+ @builder.short '-k'
280
+ @builder.long '--keep', 'KEEP'
281
+ attempting {
282
+ @builder.fail "Didn't keep anything"
283
+ }.should raise_error(Choosy::ValidationError, /-k\/--keep KEEP: Didn't keep anything/)
284
+ end
285
+
286
+ it "should set the format of the error with the short flag" do
287
+ @builder.short '-k'
288
+ attempting {
289
+ @builder.fail "Didn't keep anything"
290
+ }.should raise_error(Choosy::ValidationError, /-k: Didn't keep anything/)
291
+ end
292
+
293
+ it "should set the format of the long flag alone" do
294
+ @builder.long '--keep'
295
+ attempting {
296
+ @builder.fail "Didn't keep anything"
297
+ }.should raise_error(Choosy::ValidationError, /--keep: Didn't keep anything/)
298
+ end
299
+ end#fail
300
+
301
+ describe :validate do
302
+ it "should save the context of the validation in a Proc to call later" do
303
+ @builder.validate do
304
+ puts "Hi!"
305
+ end
306
+ @option.validation_step.should be_a(Proc)
307
+ end
308
+
309
+ it "should have access to the larger context when called" do
310
+ value = nil
311
+ @builder.validate do
312
+ value = 'here'
313
+ end
314
+ @option.validation_step.call
315
+ value.should eql('here')
316
+ end
317
+ end#validate
318
+
319
+ describe :finalize! do
320
+ it "should set the arity if not already set" do
321
+ @builder.short '-s'
322
+ @builder.finalize!
323
+ @option.arity.should eql(0..0)
324
+ end
325
+
326
+ it "should set the cast to :string on regular arguments" do
327
+ @builder.short '-s', 'SHORT'
328
+ @builder.finalize!
329
+ @option.cast_to.should eql(:string)
330
+ end
331
+
332
+ it "should set the cast to :boolean on single flags" do
333
+ @builder.short '-s'
334
+ @builder.finalize!
335
+ @option.cast_to.should eql(:boolean)
336
+ end
337
+ end#finalize!
338
+
339
+ describe :dependencies do
340
+ it "should be able to process multiple arguments" do
341
+ @builder.dependencies :a, :b
342
+ @option.dependent_options.should eql([:a, :b])
343
+ end
344
+
345
+ it "should be able to process Array arguments" do
346
+ @builder.dependencies [:a, :b]
347
+ @option.dependent_options.should eql([:a, :b])
348
+ end
349
+ end#dependencies
350
+
351
+ describe :from_hash do
352
+ it "should fail on unrecognized methods" do
353
+ attempting {
354
+ @builder.from_hash :not_a_method => "ha!"
355
+ }.should raise_error(Choosy::ConfigurationError, /Not a recognized option/)
356
+ end
357
+
358
+ it "should handle the short option" do
359
+ @builder.from_hash :short => ['-s', 'SHORT+']
360
+ @option.short_flag.should eql('-s')
361
+ end
362
+
363
+ it "should handle the desc option" do
364
+ @builder.from_hash :desc => "description"
365
+ @option.description.should eql("description")
366
+ end
367
+
368
+ it "should be able to handle multiple options" do
369
+ @builder.from_hash({:short => '-s', :desc => 'description'})
370
+ @option.short_flag.should eql('-s')
371
+ @option.description.should eql('description')
372
+ end
373
+
374
+ it "should be able to handle complicated arguments like :count" do
375
+ @builder.from_hash({:count => {:at_least => 3, :at_most => 5}})
376
+ @option.arity.should eql(3..5)
377
+ end
378
+
379
+ it "should fail when the argument isn't a hash" do
380
+ attempting {
381
+ @builder.from_hash("")
382
+ }.should raise_error(Choosy::ConfigurationError, /Only hash arguments allowed/)
383
+ end
384
+ end#from_hash
385
+ end
386
+ end
@@ -0,0 +1,83 @@
1
+ require 'spec_helpers'
2
+ require 'choosy/super_command'
3
+ require 'choosy/dsl/super_command_builder'
4
+
5
+ module Choosy::DSL
6
+ describe SuperCommandBuilder do
7
+ before :each do
8
+ @command = Choosy::SuperCommand.new :superfoo
9
+ @builder = @command.builder
10
+ end
11
+
12
+ describe :command do
13
+ it "should add the command to the listing" do
14
+ @builder.command :foo do |foo|
15
+ foo.boolean :count, "The count"
16
+ end
17
+
18
+ @command.listing.should have(1).item
19
+ end
20
+
21
+ it "should add the command builder to the command_builders" do
22
+ o = @builder.command :foo do |foo|
23
+ foo.integer :size, "The size"
24
+ end
25
+
26
+ @command.command_builders[:foo].should be(o.builder)
27
+ end
28
+
29
+ it "should finalize the builder for the command" do
30
+ o = @builder.command :foo
31
+ o.printer.should_not be(nil)
32
+ end
33
+
34
+ it "should be able to accept a new command as an argument" do
35
+ cmd = Choosy::Command.new :cmd do |c|
36
+ c.float :float, "Float"
37
+ end
38
+ @builder.command cmd
39
+ @command.listing[0].should be(cmd)
40
+ end
41
+ end
42
+
43
+ describe "standard options" do
44
+ it "should also be able to set flags" do
45
+ o = @builder.boolean :count, "The count"
46
+ @command.option_builders[:count].option.name.should eql(:count)
47
+ end
48
+ end
49
+
50
+ describe :help do
51
+ it "should create a help command when asked" do
52
+ h = @builder.help
53
+ @command.listing[0].should be(h)
54
+ end
55
+
56
+ it "should set the default summary of the help command" do
57
+ h = @builder.help
58
+ h.summary.should match(/Show the info/)
59
+ end
60
+
61
+ it "should st the summary of the command when given" do
62
+ h = @builder.help "Show this help message"
63
+ h.summary.should match(/Show this/)
64
+ end
65
+
66
+ describe "when validated" do
67
+ it "should return the super command name when called without arguments" do
68
+ h = @builder.help
69
+ attempting {
70
+ h.argument_validation.call([])
71
+ }.should raise_error(Choosy::HelpCalled, :superfoo)
72
+ end
73
+
74
+ it "should return the name of the first argument when called, as a symbol" do
75
+ h = @builder.help
76
+ attempting {
77
+ h.argument_validation.call(['foo'])
78
+ }.should raise_error(Choosy::HelpCalled, :foo)
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end