jls-clamp 0.3.1
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.
- data/.gitignore +8 -0
- data/.travis.yml +5 -0
- data/Gemfile +9 -0
- data/README.markdown +274 -0
- data/Rakefile +12 -0
- data/clamp.gemspec +24 -0
- data/examples/flipflop +31 -0
- data/examples/fubar +23 -0
- data/examples/gitdown +61 -0
- data/examples/speak +33 -0
- data/lib/clamp/attribute.rb +40 -0
- data/lib/clamp/attribute_declaration.rb +40 -0
- data/lib/clamp/command.rb +142 -0
- data/lib/clamp/errors.rb +26 -0
- data/lib/clamp/help.rb +100 -0
- data/lib/clamp/option/declaration.rb +57 -0
- data/lib/clamp/option/parsing.rb +59 -0
- data/lib/clamp/option.rb +80 -0
- data/lib/clamp/parameter/declaration.rb +28 -0
- data/lib/clamp/parameter/parsing.rb +24 -0
- data/lib/clamp/parameter.rb +80 -0
- data/lib/clamp/subcommand/declaration.rb +44 -0
- data/lib/clamp/subcommand/parsing.rb +41 -0
- data/lib/clamp/subcommand.rb +23 -0
- data/lib/clamp/version.rb +3 -0
- data/lib/clamp.rb +3 -0
- data/spec/clamp/command_group_spec.rb +267 -0
- data/spec/clamp/command_spec.rb +766 -0
- data/spec/clamp/option_module_spec.rb +37 -0
- data/spec/clamp/option_spec.rb +149 -0
- data/spec/clamp/parameter_spec.rb +201 -0
- data/spec/spec_helper.rb +45 -0
- metadata +84 -0
@@ -0,0 +1,766 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Clamp::Command do
|
4
|
+
|
5
|
+
extend CommandFactory
|
6
|
+
include OutputCapture
|
7
|
+
|
8
|
+
given_command("cmd") do
|
9
|
+
|
10
|
+
def execute
|
11
|
+
puts "Hello, world"
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#help" do
|
17
|
+
|
18
|
+
it "describes usage" do
|
19
|
+
@command.help.should =~ /^Usage:\n cmd.*\n/
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#run" do
|
25
|
+
|
26
|
+
before do
|
27
|
+
@command.run([])
|
28
|
+
end
|
29
|
+
|
30
|
+
it "executes the #execute method" do
|
31
|
+
stdout.should_not be_empty
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
describe ".option" do
|
37
|
+
|
38
|
+
it "declares option argument accessors" do
|
39
|
+
@command.class.option "--flavour", "FLAVOUR", "Flavour of the month"
|
40
|
+
@command.flavour.should == nil
|
41
|
+
@command.flavour = "chocolate"
|
42
|
+
@command.flavour.should == "chocolate"
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "with type :flag" do
|
46
|
+
|
47
|
+
before do
|
48
|
+
@command.class.option "--verbose", :flag, "Be heartier"
|
49
|
+
end
|
50
|
+
|
51
|
+
it "declares a predicate-style reader" do
|
52
|
+
@command.should respond_to(:verbose?)
|
53
|
+
@command.should_not respond_to(:verbose)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "with explicit :attribute_name" do
|
59
|
+
|
60
|
+
before do
|
61
|
+
@command.class.option "--foo", "FOO", "A foo", :attribute_name => :bar
|
62
|
+
end
|
63
|
+
|
64
|
+
it "uses the specified attribute_name name to name accessors" do
|
65
|
+
@command.bar = "chocolate"
|
66
|
+
@command.bar.should == "chocolate"
|
67
|
+
end
|
68
|
+
|
69
|
+
it "does not attempt to create the default accessors" do
|
70
|
+
@command.should_not respond_to(:foo)
|
71
|
+
@command.should_not respond_to(:foo=)
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "with default method" do
|
77
|
+
|
78
|
+
before do
|
79
|
+
@command.class.option "--port", "PORT", "port"
|
80
|
+
@command.class.class_eval do
|
81
|
+
def default_port
|
82
|
+
4321
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
it "sets the specified default value" do
|
88
|
+
@command.port.should == 4321
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "with :default value" do
|
94
|
+
|
95
|
+
before do
|
96
|
+
@command.class.option "--port", "PORT", "port to listen on", :default => 4321
|
97
|
+
end
|
98
|
+
|
99
|
+
it "declares default method" do
|
100
|
+
@command.default_port.should == 4321
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "#help" do
|
104
|
+
|
105
|
+
it "describes the default value" do
|
106
|
+
@command.help.should include("port to listen on (default: 4321)")
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "with :env value" do
|
114
|
+
|
115
|
+
before do
|
116
|
+
@command.class.option "--port", "PORT", "port to listen on", :default => 4321, :env => "PORT" do |value|
|
117
|
+
value.to_i
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should use the default if neither flag nor env var are present" do
|
122
|
+
@command.parse([])
|
123
|
+
@command.port.should == 4321
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should use the env value if present (instead of default)" do
|
127
|
+
ENV["PORT"] = rand(10000).to_s
|
128
|
+
@command.parse([])
|
129
|
+
@command.port.should == ENV["PORT"].to_i
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should use the the flag value if present (instead of env)" do
|
133
|
+
ENV["PORT"] = "12345"
|
134
|
+
@command.parse(%w(--port 1500))
|
135
|
+
@command.port.should == 1500
|
136
|
+
end
|
137
|
+
|
138
|
+
describe "#help" do
|
139
|
+
|
140
|
+
it "describes the default value and env usage" do
|
141
|
+
@command.help.should include("port to listen on (default: 4321) (env: \"PORT\")")
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
describe "with :env value on a :flag option" do
|
149
|
+
|
150
|
+
before do
|
151
|
+
@command.class.option "--[no-]enable", :flag, "enable?", :default => false, :env => "ENABLE"
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should use the default if neither flag nor env var are present" do
|
155
|
+
@command.parse([])
|
156
|
+
@command.enable?.should == false
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should use an env value of '1' to mean truth" do
|
160
|
+
ENV["ENABLE"] = "1"
|
161
|
+
@command.parse([])
|
162
|
+
@command.enable?.should == true
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should use an env value other than '1' to mean false" do
|
166
|
+
ENV["ENABLE"] = "0"
|
167
|
+
@command.parse([])
|
168
|
+
@command.enable?.should == false
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should use the the flag value if present (instead of env)" do
|
172
|
+
ENV["ENABLE"] = "1"
|
173
|
+
@command.parse(%w(--no-enable))
|
174
|
+
@command.enable?.should == false
|
175
|
+
end
|
176
|
+
|
177
|
+
describe "#help" do
|
178
|
+
|
179
|
+
it "describes the default value and env usage" do
|
180
|
+
@command.help.should include("enable? (default: false) (env: \"ENABLE\")")
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
describe "with a block" do
|
188
|
+
|
189
|
+
before do
|
190
|
+
@command.class.option "--port", "PORT", "Port to listen on" do |port|
|
191
|
+
Integer(port)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
it "uses the block to validate and convert the option argument" do
|
196
|
+
lambda do
|
197
|
+
@command.port = "blah"
|
198
|
+
end.should raise_error(ArgumentError)
|
199
|
+
@command.port = "1234"
|
200
|
+
@command.port.should == 1234
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
describe "with options declared" do
|
208
|
+
|
209
|
+
before do
|
210
|
+
@command.class.option ["-f", "--flavour"], "FLAVOUR", "Flavour of the month"
|
211
|
+
@command.class.option ["-c", "--color"], "COLOR", "Preferred hue"
|
212
|
+
@command.class.option ["-n", "--[no-]nuts"], :flag, "Nuts (or not)\nMay include nuts"
|
213
|
+
@command.class.parameter "[ARG] ...", "extra arguments", :attribute_name => :arguments
|
214
|
+
end
|
215
|
+
|
216
|
+
describe "#parse" do
|
217
|
+
|
218
|
+
describe "with an unrecognised option" do
|
219
|
+
|
220
|
+
it "raises a UsageError" do
|
221
|
+
lambda do
|
222
|
+
@command.parse(%w(--foo bar))
|
223
|
+
end.should raise_error(Clamp::UsageError)
|
224
|
+
end
|
225
|
+
|
226
|
+
end
|
227
|
+
|
228
|
+
describe "with options" do
|
229
|
+
|
230
|
+
before do
|
231
|
+
@command.parse(%w(--flavour strawberry --nuts --color blue))
|
232
|
+
end
|
233
|
+
|
234
|
+
it "maps the option values onto the command object" do
|
235
|
+
@command.flavour.should == "strawberry"
|
236
|
+
@command.color.should == "blue"
|
237
|
+
@command.nuts?.should == true
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
|
242
|
+
describe "with short options" do
|
243
|
+
|
244
|
+
before do
|
245
|
+
@command.parse(%w(-f strawberry -c blue))
|
246
|
+
end
|
247
|
+
|
248
|
+
it "recognises short options as aliases" do
|
249
|
+
@command.flavour.should == "strawberry"
|
250
|
+
@command.color.should == "blue"
|
251
|
+
end
|
252
|
+
|
253
|
+
end
|
254
|
+
|
255
|
+
describe "with combined short options" do
|
256
|
+
|
257
|
+
before do
|
258
|
+
@command.parse(%w(-nf strawberry))
|
259
|
+
end
|
260
|
+
|
261
|
+
it "works as though the options were separate" do
|
262
|
+
@command.flavour.should == "strawberry"
|
263
|
+
@command.nuts?.should == true
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
267
|
+
|
268
|
+
describe "with option arguments attached using equals sign" do
|
269
|
+
|
270
|
+
before do
|
271
|
+
@command.parse(%w(--flavour=strawberry --color=blue))
|
272
|
+
end
|
273
|
+
|
274
|
+
it "works as though the option arguments were separate" do
|
275
|
+
@command.flavour.should == "strawberry"
|
276
|
+
@command.color.should == "blue"
|
277
|
+
end
|
278
|
+
|
279
|
+
end
|
280
|
+
|
281
|
+
describe "with option-like things beyond the arguments" do
|
282
|
+
|
283
|
+
it "treats them as positional arguments" do
|
284
|
+
@command.parse(%w(a b c --flavour strawberry))
|
285
|
+
@command.arguments.should == %w(a b c --flavour strawberry)
|
286
|
+
end
|
287
|
+
|
288
|
+
end
|
289
|
+
|
290
|
+
describe "with an option terminator" do
|
291
|
+
|
292
|
+
it "considers everything after the terminator to be an argument" do
|
293
|
+
@command.parse(%w(--color blue -- --flavour strawberry))
|
294
|
+
@command.arguments.should == %w(--flavour strawberry)
|
295
|
+
end
|
296
|
+
|
297
|
+
end
|
298
|
+
|
299
|
+
describe "with --flag" do
|
300
|
+
|
301
|
+
before do
|
302
|
+
@command.parse(%w(--nuts))
|
303
|
+
end
|
304
|
+
|
305
|
+
it "sets the flag" do
|
306
|
+
@command.nuts?.should be_true
|
307
|
+
end
|
308
|
+
|
309
|
+
end
|
310
|
+
|
311
|
+
describe "with --no-flag" do
|
312
|
+
|
313
|
+
before do
|
314
|
+
@command.nuts = true
|
315
|
+
@command.parse(%w(--no-nuts))
|
316
|
+
end
|
317
|
+
|
318
|
+
it "clears the flag" do
|
319
|
+
@command.nuts?.should be_false
|
320
|
+
end
|
321
|
+
|
322
|
+
end
|
323
|
+
|
324
|
+
describe "with --help" do
|
325
|
+
|
326
|
+
it "requests help" do
|
327
|
+
lambda do
|
328
|
+
@command.parse(%w(--help))
|
329
|
+
end.should raise_error(Clamp::HelpWanted)
|
330
|
+
end
|
331
|
+
|
332
|
+
end
|
333
|
+
|
334
|
+
describe "with -h" do
|
335
|
+
|
336
|
+
it "requests help" do
|
337
|
+
lambda do
|
338
|
+
@command.parse(%w(-h))
|
339
|
+
end.should raise_error(Clamp::HelpWanted)
|
340
|
+
end
|
341
|
+
|
342
|
+
end
|
343
|
+
|
344
|
+
describe "when option-writer raises an ArgumentError" do
|
345
|
+
|
346
|
+
before do
|
347
|
+
@command.class.class_eval do
|
348
|
+
|
349
|
+
def color=(c)
|
350
|
+
unless c == "black"
|
351
|
+
raise ArgumentError, "sorry, we're out of #{c}"
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
it "re-raises it as a UsageError" do
|
359
|
+
lambda do
|
360
|
+
@command.parse(%w(--color red))
|
361
|
+
end.should raise_error(Clamp::UsageError, /^option '--color': sorry, we're out of red/)
|
362
|
+
end
|
363
|
+
|
364
|
+
end
|
365
|
+
|
366
|
+
end
|
367
|
+
|
368
|
+
describe "#help" do
|
369
|
+
|
370
|
+
it "indicates that there are options" do
|
371
|
+
@command.help.should include("cmd [OPTIONS]")
|
372
|
+
end
|
373
|
+
|
374
|
+
it "includes option details" do
|
375
|
+
@command.help.should =~ %r(--flavour FLAVOUR +Flavour of the month)
|
376
|
+
@command.help.should =~ %r(--color COLOR +Preferred hue)
|
377
|
+
end
|
378
|
+
|
379
|
+
it "handles new lines in option descriptions" do
|
380
|
+
@command.help.should =~ %r(--\[no-\]nuts +Nuts \(or not\)\n +May include nuts)
|
381
|
+
end
|
382
|
+
|
383
|
+
end
|
384
|
+
|
385
|
+
end
|
386
|
+
|
387
|
+
describe "with an explicit --help option declared" do
|
388
|
+
|
389
|
+
before do
|
390
|
+
@command.class.option ["--help"], :flag, "help wanted"
|
391
|
+
end
|
392
|
+
|
393
|
+
it "does not generate implicit help option" do
|
394
|
+
lambda do
|
395
|
+
@command.parse(%w(--help))
|
396
|
+
end.should_not raise_error
|
397
|
+
@command.help.should be_true
|
398
|
+
end
|
399
|
+
|
400
|
+
it "does not recognise -h" do
|
401
|
+
lambda do
|
402
|
+
@command.parse(%w(-h))
|
403
|
+
end.should raise_error(Clamp::UsageError)
|
404
|
+
end
|
405
|
+
|
406
|
+
end
|
407
|
+
|
408
|
+
describe "with an explicit -h option declared" do
|
409
|
+
|
410
|
+
before do
|
411
|
+
@command.class.option ["-h", "--humidity"], "PERCENT", "relative humidity" do |n|
|
412
|
+
Integer(n)
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
it "does not map -h to help" do
|
417
|
+
@command.help.should_not =~ %r( -h[, ].*help)
|
418
|
+
end
|
419
|
+
|
420
|
+
it "still recognises --help" do
|
421
|
+
lambda do
|
422
|
+
@command.parse(%w(--help))
|
423
|
+
end.should raise_error(Clamp::HelpWanted)
|
424
|
+
end
|
425
|
+
|
426
|
+
end
|
427
|
+
|
428
|
+
describe ".parameter" do
|
429
|
+
|
430
|
+
it "declares option argument accessors" do
|
431
|
+
@command.class.parameter "FLAVOUR", "flavour of the month"
|
432
|
+
@command.flavour.should == nil
|
433
|
+
@command.flavour = "chocolate"
|
434
|
+
@command.flavour.should == "chocolate"
|
435
|
+
end
|
436
|
+
|
437
|
+
describe "with explicit :attribute_name" do
|
438
|
+
|
439
|
+
before do
|
440
|
+
@command.class.parameter "FOO", "a foo", :attribute_name => :bar
|
441
|
+
end
|
442
|
+
|
443
|
+
it "uses the specified attribute_name name to name accessors" do
|
444
|
+
@command.bar = "chocolate"
|
445
|
+
@command.bar.should == "chocolate"
|
446
|
+
end
|
447
|
+
|
448
|
+
end
|
449
|
+
|
450
|
+
describe "with :default value" do
|
451
|
+
|
452
|
+
before do
|
453
|
+
@command.class.parameter "[ORIENTATION]", "direction", :default => "west"
|
454
|
+
end
|
455
|
+
|
456
|
+
it "sets the specified default value" do
|
457
|
+
@command.orientation.should == "west"
|
458
|
+
end
|
459
|
+
|
460
|
+
describe "#help" do
|
461
|
+
|
462
|
+
it "describes the default value" do
|
463
|
+
@command.help.should include("direction (default: \"west\")")
|
464
|
+
end
|
465
|
+
|
466
|
+
end
|
467
|
+
|
468
|
+
end
|
469
|
+
|
470
|
+
describe "with a block" do
|
471
|
+
|
472
|
+
before do
|
473
|
+
@command.class.parameter "PORT", "port to listen on" do |port|
|
474
|
+
Integer(port)
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
it "uses the block to validate and convert the argument" do
|
479
|
+
lambda do
|
480
|
+
@command.port = "blah"
|
481
|
+
end.should raise_error(ArgumentError)
|
482
|
+
@command.port = "1234"
|
483
|
+
@command.port.should == 1234
|
484
|
+
end
|
485
|
+
|
486
|
+
end
|
487
|
+
|
488
|
+
describe "with ellipsis" do
|
489
|
+
|
490
|
+
before do
|
491
|
+
@command.class.parameter "FILE ...", "files"
|
492
|
+
end
|
493
|
+
|
494
|
+
it "accepts multiple arguments" do
|
495
|
+
@command.parse(%w(X Y Z))
|
496
|
+
@command.file_list.should == %w(X Y Z)
|
497
|
+
end
|
498
|
+
|
499
|
+
end
|
500
|
+
|
501
|
+
describe "optional, with ellipsis" do
|
502
|
+
|
503
|
+
before do
|
504
|
+
@command.class.parameter "[FILE] ...", "files"
|
505
|
+
end
|
506
|
+
|
507
|
+
it "default to an empty list" do
|
508
|
+
@command.parse([])
|
509
|
+
@command.default_file_list.should == []
|
510
|
+
@command.file_list.should == []
|
511
|
+
end
|
512
|
+
|
513
|
+
end
|
514
|
+
|
515
|
+
end
|
516
|
+
|
517
|
+
describe "with no parameters declared" do
|
518
|
+
|
519
|
+
describe "#parse" do
|
520
|
+
|
521
|
+
describe "with arguments" do
|
522
|
+
|
523
|
+
it "raises a UsageError" do
|
524
|
+
lambda do
|
525
|
+
@command.parse(["crash"])
|
526
|
+
end.should raise_error(Clamp::UsageError, "too many arguments")
|
527
|
+
end
|
528
|
+
|
529
|
+
end
|
530
|
+
|
531
|
+
end
|
532
|
+
|
533
|
+
end
|
534
|
+
|
535
|
+
describe "with parameters declared" do
|
536
|
+
|
537
|
+
before do
|
538
|
+
@command.class.parameter "X", "x\nxx"
|
539
|
+
@command.class.parameter "Y", "y"
|
540
|
+
@command.class.parameter "[Z]", "z", :default => "ZZZ"
|
541
|
+
end
|
542
|
+
|
543
|
+
describe "#parse" do
|
544
|
+
|
545
|
+
describe "with arguments for all parameters" do
|
546
|
+
|
547
|
+
before do
|
548
|
+
@command.parse(["crash", "bang", "wallop"])
|
549
|
+
end
|
550
|
+
|
551
|
+
it "maps arguments onto the command object" do
|
552
|
+
@command.x.should == "crash"
|
553
|
+
@command.y.should == "bang"
|
554
|
+
@command.z.should == "wallop"
|
555
|
+
end
|
556
|
+
|
557
|
+
end
|
558
|
+
|
559
|
+
describe "with insufficient arguments" do
|
560
|
+
|
561
|
+
it "raises a UsageError" do
|
562
|
+
lambda do
|
563
|
+
@command.parse(["crash"])
|
564
|
+
end.should raise_error(Clamp::UsageError, "parameter 'Y': no value provided")
|
565
|
+
end
|
566
|
+
|
567
|
+
end
|
568
|
+
|
569
|
+
describe "with optional argument omitted" do
|
570
|
+
|
571
|
+
it "defaults the optional argument" do
|
572
|
+
@command.parse(["crash", "bang"])
|
573
|
+
@command.x.should == "crash"
|
574
|
+
@command.y.should == "bang"
|
575
|
+
@command.z.should == "ZZZ"
|
576
|
+
end
|
577
|
+
|
578
|
+
end
|
579
|
+
|
580
|
+
describe "with too many arguments" do
|
581
|
+
|
582
|
+
it "raises a UsageError" do
|
583
|
+
lambda do
|
584
|
+
@command.parse(["crash", "bang", "wallop", "kapow"])
|
585
|
+
end.should raise_error(Clamp::UsageError, "too many arguments")
|
586
|
+
end
|
587
|
+
|
588
|
+
end
|
589
|
+
|
590
|
+
end
|
591
|
+
|
592
|
+
describe "#help" do
|
593
|
+
|
594
|
+
it "indicates that there are parameters" do
|
595
|
+
@command.help.should include("cmd [OPTIONS] X Y [Z]")
|
596
|
+
end
|
597
|
+
|
598
|
+
it "includes parameter details" do
|
599
|
+
@command.help.should =~ %r(X +x)
|
600
|
+
@command.help.should =~ %r(Y +y)
|
601
|
+
@command.help.should =~ %r(\[Z\] +z \(default: "ZZZ"\))
|
602
|
+
end
|
603
|
+
|
604
|
+
it "handles new lines in option descriptions" do
|
605
|
+
@command.help.should =~ %r(X +x\n +xx)
|
606
|
+
end
|
607
|
+
|
608
|
+
end
|
609
|
+
|
610
|
+
|
611
|
+
end
|
612
|
+
|
613
|
+
describe "with explicit usage" do
|
614
|
+
|
615
|
+
given_command("blah") do
|
616
|
+
|
617
|
+
usage "FOO BAR ..."
|
618
|
+
|
619
|
+
end
|
620
|
+
|
621
|
+
describe "#help" do
|
622
|
+
|
623
|
+
it "includes the explicit usage" do
|
624
|
+
@command.help.should include("blah FOO BAR ...\n")
|
625
|
+
end
|
626
|
+
|
627
|
+
end
|
628
|
+
|
629
|
+
end
|
630
|
+
|
631
|
+
describe "with multiple usages" do
|
632
|
+
|
633
|
+
given_command("put") do
|
634
|
+
|
635
|
+
usage "THIS HERE"
|
636
|
+
usage "THAT THERE"
|
637
|
+
|
638
|
+
end
|
639
|
+
|
640
|
+
describe "#help" do
|
641
|
+
|
642
|
+
it "includes both potential usages" do
|
643
|
+
@command.help.should include("put THIS HERE\n")
|
644
|
+
@command.help.should include("put THAT THERE\n")
|
645
|
+
end
|
646
|
+
|
647
|
+
end
|
648
|
+
|
649
|
+
end
|
650
|
+
|
651
|
+
describe "with a description" do
|
652
|
+
|
653
|
+
given_command("punt") do
|
654
|
+
|
655
|
+
self.description = <<-EOF
|
656
|
+
Punt is an example command. It doesn't do much, really.
|
657
|
+
|
658
|
+
The prefix at the beginning of this description should be normalised
|
659
|
+
to two spaces.
|
660
|
+
EOF
|
661
|
+
|
662
|
+
end
|
663
|
+
|
664
|
+
describe "#help" do
|
665
|
+
|
666
|
+
it "includes the description" do
|
667
|
+
@command.help.should =~ /^ Punt is an example command/
|
668
|
+
@command.help.should =~ /^ The prefix/
|
669
|
+
end
|
670
|
+
|
671
|
+
end
|
672
|
+
|
673
|
+
end
|
674
|
+
|
675
|
+
describe ".run" do
|
676
|
+
|
677
|
+
it "creates a new Command instance and runs it" do
|
678
|
+
@command.class.class_eval do
|
679
|
+
parameter "WORD ...", "words"
|
680
|
+
def execute
|
681
|
+
print word_list.inspect
|
682
|
+
end
|
683
|
+
end
|
684
|
+
@xyz = %w(x y z)
|
685
|
+
@command.class.run("cmd", @xyz)
|
686
|
+
stdout.should == @xyz.inspect
|
687
|
+
end
|
688
|
+
|
689
|
+
describe "invoked with a context hash" do
|
690
|
+
|
691
|
+
it "makes the context available within the command" do
|
692
|
+
@command.class.class_eval do
|
693
|
+
def execute
|
694
|
+
print context[:foo]
|
695
|
+
end
|
696
|
+
end
|
697
|
+
@command.class.run("xyz", [], :foo => "bar")
|
698
|
+
stdout.should == "bar"
|
699
|
+
end
|
700
|
+
|
701
|
+
end
|
702
|
+
|
703
|
+
describe "when there's a UsageError" do
|
704
|
+
|
705
|
+
before do
|
706
|
+
|
707
|
+
@command.class.class_eval do
|
708
|
+
def execute
|
709
|
+
signal_usage_error "bad dog!"
|
710
|
+
end
|
711
|
+
end
|
712
|
+
|
713
|
+
begin
|
714
|
+
@command.class.run("cmd", [])
|
715
|
+
rescue SystemExit => e
|
716
|
+
@system_exit = e
|
717
|
+
end
|
718
|
+
|
719
|
+
end
|
720
|
+
|
721
|
+
it "outputs the error message" do
|
722
|
+
stderr.should include "ERROR: bad dog!"
|
723
|
+
end
|
724
|
+
|
725
|
+
it "outputs help" do
|
726
|
+
stderr.should include "See: 'cmd --help'"
|
727
|
+
end
|
728
|
+
|
729
|
+
it "exits with a non-zero status" do
|
730
|
+
@system_exit.should_not be_nil
|
731
|
+
@system_exit.status.should == 1
|
732
|
+
end
|
733
|
+
|
734
|
+
end
|
735
|
+
|
736
|
+
describe "when help is requested" do
|
737
|
+
|
738
|
+
it "outputs help" do
|
739
|
+
@command.class.run("cmd", ["--help"])
|
740
|
+
stdout.should include "Usage:"
|
741
|
+
end
|
742
|
+
|
743
|
+
end
|
744
|
+
|
745
|
+
end
|
746
|
+
|
747
|
+
describe "subclass" do
|
748
|
+
|
749
|
+
before do
|
750
|
+
@parent_command_class = Class.new(Clamp::Command) do
|
751
|
+
option "--verbose", :flag, "be louder"
|
752
|
+
end
|
753
|
+
@derived_command_class = Class.new(@parent_command_class) do
|
754
|
+
option "--iterations", "N", "number of times to go around"
|
755
|
+
end
|
756
|
+
@command = @derived_command_class.new("cmd")
|
757
|
+
end
|
758
|
+
|
759
|
+
it "inherits options from it's superclass" do
|
760
|
+
@command.parse(["--verbose"])
|
761
|
+
@command.should be_verbose
|
762
|
+
end
|
763
|
+
|
764
|
+
end
|
765
|
+
|
766
|
+
end
|