clamp 1.4.0 → 1.5.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.
- checksums.yaml +4 -4
- data/CHANGES.md +8 -0
- data/README.md +44 -0
- data/examples/gitdown +1 -0
- data/lib/clamp/completion/bash_generator.rb +207 -0
- data/lib/clamp/completion/fish_generator.rb +176 -0
- data/lib/clamp/completion/zsh_generator.rb +123 -0
- data/lib/clamp/completion.rb +187 -0
- data/lib/clamp/version.rb +1 -1
- metadata +5 -21
- data/.autotest +0 -11
- data/.editorconfig +0 -10
- data/.github/workflows/ci.yml +0 -31
- data/.gitignore +0 -9
- data/.rspec +0 -2
- data/.rubocop.yml +0 -74
- data/CODEOWNERS +0 -1
- data/Gemfile +0 -20
- data/Guardfile +0 -45
- data/Rakefile +0 -18
- data/clamp.gemspec +0 -28
- data/spec/clamp/command_group_spec.rb +0 -438
- data/spec/clamp/command_option_module_spec.rb +0 -40
- data/spec/clamp/command_option_reordering_spec.rb +0 -58
- data/spec/clamp/command_spec.rb +0 -1280
- data/spec/clamp/help/builder_spec.rb +0 -81
- data/spec/clamp/messages_spec.rb +0 -50
- data/spec/clamp/option/definition_spec.rb +0 -343
- data/spec/clamp/parameter/definition_spec.rb +0 -314
- data/spec/spec_helper.rb +0 -65
data/spec/clamp/command_spec.rb
DELETED
|
@@ -1,1280 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "spec_helper"
|
|
4
|
-
|
|
5
|
-
describe Clamp::Command do
|
|
6
|
-
|
|
7
|
-
extend CommandFactory
|
|
8
|
-
include OutputCapture
|
|
9
|
-
include SetEnv
|
|
10
|
-
|
|
11
|
-
given_command("cmd") do
|
|
12
|
-
|
|
13
|
-
def execute
|
|
14
|
-
puts "Hello, world"
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
describe "#help" do
|
|
20
|
-
|
|
21
|
-
it "describes usage" do
|
|
22
|
-
expect(command.help).to match(/^Usage:\n cmd.*\n/)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
describe "#run" do
|
|
28
|
-
|
|
29
|
-
before do
|
|
30
|
-
command.run([])
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
it "executes the #execute method" do
|
|
34
|
-
expect(stdout).not_to be_empty
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
describe ".option" do
|
|
40
|
-
|
|
41
|
-
context "when regular" do
|
|
42
|
-
|
|
43
|
-
before do
|
|
44
|
-
command.class.option "--flavour", "FLAVOUR", "Flavour of the month"
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
context "when not set" do
|
|
48
|
-
it "equals nil" do
|
|
49
|
-
expect(command.flavour).to be_nil
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
context "when set" do
|
|
54
|
-
before do
|
|
55
|
-
command.flavour = "chocolate"
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
it "equals the value" do
|
|
59
|
-
expect(command.flavour).to eq "chocolate"
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
context "with type :flag" do
|
|
66
|
-
|
|
67
|
-
before do
|
|
68
|
-
command.class.option "--verbose", :flag, "Be heartier"
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
describe "predicate-style reader" do
|
|
72
|
-
|
|
73
|
-
it "exists" do
|
|
74
|
-
expect(command).to respond_to(:verbose?)
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
describe "regular reader" do
|
|
80
|
-
|
|
81
|
-
it "does not exist" do
|
|
82
|
-
expect(command).not_to respond_to(:verbose)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
context "with explicit :attribute_name" do
|
|
90
|
-
|
|
91
|
-
before do
|
|
92
|
-
command.class.option "--foo", "FOO", "A foo", attribute_name: :bar
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
it "uses the specified attribute_name name to name accessors" do
|
|
96
|
-
command.bar = "chocolate"
|
|
97
|
-
expect(command.bar).to eq "chocolate"
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
describe "default reader" do
|
|
101
|
-
it "does not exist" do
|
|
102
|
-
expect(command).not_to respond_to(:foo)
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
describe "default writer" do
|
|
107
|
-
it "does not exist" do
|
|
108
|
-
expect(command).not_to respond_to(:foo=)
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
context "with default method" do
|
|
115
|
-
|
|
116
|
-
before do
|
|
117
|
-
command.class.option "--port", "PORT", "port"
|
|
118
|
-
command.class.class_eval do
|
|
119
|
-
def default_port
|
|
120
|
-
4321
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
it "sets the specified default value" do
|
|
126
|
-
expect(command.port).to eq 4321
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
context "with :default value" do
|
|
132
|
-
|
|
133
|
-
before do
|
|
134
|
-
command.class.option "--port", "PORT", "port to listen on", default: 4321
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
it "declares default method" do
|
|
138
|
-
expect(command.default_port).to eq 4321
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
describe "#help" do
|
|
142
|
-
|
|
143
|
-
it "describes the default value" do
|
|
144
|
-
expect(command.help).to include("port to listen on (default: 4321)")
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
context "without :default value" do
|
|
152
|
-
|
|
153
|
-
before do
|
|
154
|
-
command.class.option "--port", "PORT", "port to listen on"
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
it "does not declare default method" do
|
|
158
|
-
expect(command).not_to respond_to(:default_port)
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
context "with :multivalued" do
|
|
164
|
-
|
|
165
|
-
before do
|
|
166
|
-
command.class.option "--flavour", "FLAVOUR", "flavour(s)", multivalued: true, attribute_name: :flavours
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
it "defaults to empty array" do
|
|
170
|
-
expect(command.flavours).to be_empty
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
it "supports multiple values" do
|
|
174
|
-
command.parse(%w[--flavour chocolate --flavour vanilla])
|
|
175
|
-
expect(command.flavours).to eq %w[chocolate vanilla]
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
it "generates a single-value appender method" do
|
|
179
|
-
command.append_to_flavours("mud")
|
|
180
|
-
command.append_to_flavours("pie")
|
|
181
|
-
expect(command.flavours).to eq %w[mud pie]
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
it "generates a multi-value setter method" do
|
|
185
|
-
command.append_to_flavours("replaceme")
|
|
186
|
-
command.flavours = %w[mud pie]
|
|
187
|
-
expect(command.flavours).to eq %w[mud pie]
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
it "does not require a value" do
|
|
191
|
-
expect do
|
|
192
|
-
command.parse([])
|
|
193
|
-
end.not_to raise_error
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
context "with :environment_variable" do
|
|
199
|
-
|
|
200
|
-
let(:environment_value) { nil }
|
|
201
|
-
let(:args) { [] }
|
|
202
|
-
|
|
203
|
-
before do
|
|
204
|
-
command.class.option "--port", "PORT", "port to listen on",
|
|
205
|
-
default: 4321,
|
|
206
|
-
environment_variable: "PORT",
|
|
207
|
-
&:to_i
|
|
208
|
-
set_env("PORT", environment_value)
|
|
209
|
-
command.parse(args)
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
context "when no environment variable is present" do
|
|
213
|
-
|
|
214
|
-
it "uses the default" do
|
|
215
|
-
expect(command.port).to eq 4321
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
context "when environment variable is present" do
|
|
221
|
-
|
|
222
|
-
let(:environment_value) { "12345" }
|
|
223
|
-
|
|
224
|
-
it "uses the environment variable" do
|
|
225
|
-
expect(command.port).to eq 12_345
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
context "when a value is specified on the command-line" do
|
|
229
|
-
|
|
230
|
-
let(:args) { %w[--port 1500] }
|
|
231
|
-
|
|
232
|
-
it "uses command-line value" do
|
|
233
|
-
expect(command.port).to eq 1500
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
describe "#help" do
|
|
241
|
-
|
|
242
|
-
it "describes the default value and env usage" do
|
|
243
|
-
expect(command.help).to include("port to listen on (default: $PORT, or 4321)")
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
end
|
|
249
|
-
|
|
250
|
-
context "with :environment_variable and type :flag" do
|
|
251
|
-
|
|
252
|
-
let(:environment_value) { nil }
|
|
253
|
-
|
|
254
|
-
before do
|
|
255
|
-
command.class.option "--[no-]enable", :flag, "enable?", default: false, environment_variable: "ENABLE"
|
|
256
|
-
set_env("ENABLE", environment_value)
|
|
257
|
-
command.parse([])
|
|
258
|
-
end
|
|
259
|
-
|
|
260
|
-
context "when no environment variable is present" do
|
|
261
|
-
|
|
262
|
-
it "uses the default" do
|
|
263
|
-
expect(command.enable?).to be false
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
end
|
|
267
|
-
|
|
268
|
-
%w[1 yes enable on true].each do |truthy_value|
|
|
269
|
-
|
|
270
|
-
context "when environment variable is #{truthy_value.inspect}" do
|
|
271
|
-
|
|
272
|
-
let(:environment_value) { truthy_value }
|
|
273
|
-
|
|
274
|
-
it "sets the flag" do
|
|
275
|
-
expect(command.enable?).to be true
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
end
|
|
279
|
-
|
|
280
|
-
end
|
|
281
|
-
|
|
282
|
-
%w[0 no disable off false].each do |falsey_value|
|
|
283
|
-
|
|
284
|
-
context "when environment variable is #{falsey_value.inspect}" do
|
|
285
|
-
|
|
286
|
-
let(:environment_value) { falsey_value }
|
|
287
|
-
|
|
288
|
-
it "clears the flag" do
|
|
289
|
-
expect(command.enable?).to be false
|
|
290
|
-
end
|
|
291
|
-
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
end
|
|
295
|
-
|
|
296
|
-
end
|
|
297
|
-
|
|
298
|
-
context "with :required" do
|
|
299
|
-
|
|
300
|
-
before do
|
|
301
|
-
command.class.option "--port", "PORT", "port to listen on", required: true
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
describe "#help" do
|
|
305
|
-
|
|
306
|
-
it "marks it as required" do
|
|
307
|
-
expect(command.help).to include("port to listen on (required)")
|
|
308
|
-
end
|
|
309
|
-
|
|
310
|
-
end
|
|
311
|
-
|
|
312
|
-
context "when no value is provided" do
|
|
313
|
-
|
|
314
|
-
it "raises a UsageError" do
|
|
315
|
-
expect do
|
|
316
|
-
command.parse([])
|
|
317
|
-
end.to raise_error(Clamp::UsageError)
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
end
|
|
321
|
-
|
|
322
|
-
context "when a value is provided" do
|
|
323
|
-
|
|
324
|
-
it "does not raise an error" do
|
|
325
|
-
expect do
|
|
326
|
-
command.parse(["--port", "12345"])
|
|
327
|
-
end.not_to raise_error
|
|
328
|
-
end
|
|
329
|
-
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
end
|
|
333
|
-
|
|
334
|
-
context "with :required and :multivalued" do
|
|
335
|
-
|
|
336
|
-
before do
|
|
337
|
-
command.class.option "--port", "PORT", "port to listen on", required: true, multivalued: true
|
|
338
|
-
end
|
|
339
|
-
|
|
340
|
-
context "when no value is provided" do
|
|
341
|
-
|
|
342
|
-
it "raises a UsageError" do
|
|
343
|
-
expect do
|
|
344
|
-
command.parse([])
|
|
345
|
-
end.to raise_error(Clamp::UsageError)
|
|
346
|
-
end
|
|
347
|
-
|
|
348
|
-
end
|
|
349
|
-
|
|
350
|
-
context "when a value is provided" do
|
|
351
|
-
|
|
352
|
-
it "does not raise an error" do
|
|
353
|
-
expect do
|
|
354
|
-
command.parse(["--port", "12345"])
|
|
355
|
-
end.not_to raise_error
|
|
356
|
-
end
|
|
357
|
-
|
|
358
|
-
end
|
|
359
|
-
|
|
360
|
-
end
|
|
361
|
-
|
|
362
|
-
context "with a block" do
|
|
363
|
-
|
|
364
|
-
before do
|
|
365
|
-
command.class.option "--port", "PORT", "Port to listen on" do |port|
|
|
366
|
-
Integer(port)
|
|
367
|
-
end
|
|
368
|
-
end
|
|
369
|
-
|
|
370
|
-
context "when value is incorrect" do
|
|
371
|
-
|
|
372
|
-
it "raises an error" do
|
|
373
|
-
expect do
|
|
374
|
-
command.port = "blah"
|
|
375
|
-
end.to raise_error(ArgumentError)
|
|
376
|
-
end
|
|
377
|
-
|
|
378
|
-
end
|
|
379
|
-
|
|
380
|
-
context "when value is convertible" do
|
|
381
|
-
|
|
382
|
-
it "uses the block to validate and convert the option argument" do
|
|
383
|
-
command.port = "1234"
|
|
384
|
-
expect(command.port).to eq 1234
|
|
385
|
-
end
|
|
386
|
-
|
|
387
|
-
end
|
|
388
|
-
|
|
389
|
-
end
|
|
390
|
-
|
|
391
|
-
end
|
|
392
|
-
|
|
393
|
-
context "with options declared" do
|
|
394
|
-
|
|
395
|
-
before do
|
|
396
|
-
command.class.option ["-f", "--flavour"], "FLAVOUR", "Flavour of the month"
|
|
397
|
-
command.class.option ["-c", "--color"], "COLOR", "Preferred hue"
|
|
398
|
-
command.class.option ["--scoops"], "N", "Number of scoops",
|
|
399
|
-
default: 1,
|
|
400
|
-
environment_variable: "DEFAULT_SCOOPS" do |arg|
|
|
401
|
-
Integer(arg)
|
|
402
|
-
end
|
|
403
|
-
command.class.option ["-n", "--[no-]nuts"], :flag, "Nuts (or not)\nMay include nuts"
|
|
404
|
-
command.class.parameter "[ARG] ...", "extra arguments", attribute_name: :arguments
|
|
405
|
-
end
|
|
406
|
-
|
|
407
|
-
describe "#parse" do
|
|
408
|
-
|
|
409
|
-
context "with an unrecognised option" do
|
|
410
|
-
|
|
411
|
-
it "raises a UsageError" do
|
|
412
|
-
expect do
|
|
413
|
-
command.parse(%w[--foo bar])
|
|
414
|
-
end.to raise_error(Clamp::UsageError)
|
|
415
|
-
end
|
|
416
|
-
|
|
417
|
-
end
|
|
418
|
-
|
|
419
|
-
context "with options" do
|
|
420
|
-
|
|
421
|
-
before do
|
|
422
|
-
command.parse(%w[--flavour strawberry --nuts --color blue])
|
|
423
|
-
end
|
|
424
|
-
|
|
425
|
-
describe "flavour" do
|
|
426
|
-
it "maps the option value onto the command object" do
|
|
427
|
-
expect(command.flavour).to eq "strawberry"
|
|
428
|
-
end
|
|
429
|
-
end
|
|
430
|
-
|
|
431
|
-
describe "color" do
|
|
432
|
-
it "maps the option value onto the command object" do
|
|
433
|
-
expect(command.color).to eq "blue"
|
|
434
|
-
end
|
|
435
|
-
end
|
|
436
|
-
|
|
437
|
-
describe "nuts?" do
|
|
438
|
-
it "maps the option value onto the command object" do
|
|
439
|
-
expect(command.nuts?).to be true
|
|
440
|
-
end
|
|
441
|
-
end
|
|
442
|
-
|
|
443
|
-
end
|
|
444
|
-
|
|
445
|
-
context "with short options" do
|
|
446
|
-
|
|
447
|
-
before do
|
|
448
|
-
command.parse(%w[-f strawberry -c blue])
|
|
449
|
-
end
|
|
450
|
-
|
|
451
|
-
describe "flavour" do
|
|
452
|
-
it "recognizes short options as aliases" do
|
|
453
|
-
expect(command.flavour).to eq "strawberry"
|
|
454
|
-
end
|
|
455
|
-
end
|
|
456
|
-
|
|
457
|
-
describe "color" do
|
|
458
|
-
it "recognizes short options as aliases" do
|
|
459
|
-
expect(command.color).to eq "blue"
|
|
460
|
-
end
|
|
461
|
-
end
|
|
462
|
-
|
|
463
|
-
end
|
|
464
|
-
|
|
465
|
-
context "with a value appended to a short option" do
|
|
466
|
-
|
|
467
|
-
before do
|
|
468
|
-
command.parse(%w[-fstrawberry])
|
|
469
|
-
end
|
|
470
|
-
|
|
471
|
-
it "works as though the value were separated" do
|
|
472
|
-
expect(command.flavour).to eq "strawberry"
|
|
473
|
-
end
|
|
474
|
-
|
|
475
|
-
end
|
|
476
|
-
|
|
477
|
-
context "with combined short options" do
|
|
478
|
-
|
|
479
|
-
before do
|
|
480
|
-
command.parse(%w[-nf strawberry])
|
|
481
|
-
end
|
|
482
|
-
|
|
483
|
-
describe "flavour" do
|
|
484
|
-
it "works as though the options were separate" do
|
|
485
|
-
expect(command.flavour).to eq "strawberry"
|
|
486
|
-
end
|
|
487
|
-
end
|
|
488
|
-
|
|
489
|
-
describe "nuts?" do
|
|
490
|
-
it "works as though the options were separate" do
|
|
491
|
-
expect(command.nuts?).to be true
|
|
492
|
-
end
|
|
493
|
-
end
|
|
494
|
-
|
|
495
|
-
end
|
|
496
|
-
|
|
497
|
-
context "with option arguments attached using equals sign" do
|
|
498
|
-
|
|
499
|
-
before do
|
|
500
|
-
command.parse(%w[--flavour=strawberry --color=blue])
|
|
501
|
-
end
|
|
502
|
-
|
|
503
|
-
describe "flavour" do
|
|
504
|
-
it "maps the option value onto the command object" do
|
|
505
|
-
expect(command.flavour).to eq "strawberry"
|
|
506
|
-
end
|
|
507
|
-
end
|
|
508
|
-
|
|
509
|
-
describe "color" do
|
|
510
|
-
it "maps the option value onto the command object" do
|
|
511
|
-
expect(command.color).to eq "blue"
|
|
512
|
-
end
|
|
513
|
-
end
|
|
514
|
-
|
|
515
|
-
end
|
|
516
|
-
|
|
517
|
-
context "with option arguments that look like options" do
|
|
518
|
-
|
|
519
|
-
before do
|
|
520
|
-
command.parse(%w[--flavour=-dashing- --scoops -1])
|
|
521
|
-
end
|
|
522
|
-
|
|
523
|
-
describe "flavour" do
|
|
524
|
-
it "sets the options" do
|
|
525
|
-
expect(command.flavour).to eq("-dashing-")
|
|
526
|
-
end
|
|
527
|
-
end
|
|
528
|
-
|
|
529
|
-
describe "scoops" do
|
|
530
|
-
it "sets the options" do
|
|
531
|
-
expect(command.scoops).to eq(-1)
|
|
532
|
-
end
|
|
533
|
-
end
|
|
534
|
-
|
|
535
|
-
end
|
|
536
|
-
|
|
537
|
-
context "with option-like things beyond the arguments" do
|
|
538
|
-
|
|
539
|
-
it "treats them as positional arguments" do
|
|
540
|
-
command.parse(%w[a b c --flavour strawberry])
|
|
541
|
-
expect(command.arguments).to eq %w[a b c --flavour strawberry]
|
|
542
|
-
end
|
|
543
|
-
|
|
544
|
-
end
|
|
545
|
-
|
|
546
|
-
context "with multi-line arguments that look like options" do
|
|
547
|
-
|
|
548
|
-
before do
|
|
549
|
-
command.parse(["foo\n--flavour=strawberry", "bar\n-cblue"])
|
|
550
|
-
end
|
|
551
|
-
|
|
552
|
-
describe "arguments" do
|
|
553
|
-
it "treats them as positional arguments" do
|
|
554
|
-
expect(command.arguments).to eq ["foo\n--flavour=strawberry", "bar\n-cblue"]
|
|
555
|
-
end
|
|
556
|
-
end
|
|
557
|
-
|
|
558
|
-
describe "flavour" do
|
|
559
|
-
it "treats them as positional arguments" do
|
|
560
|
-
expect(command.flavour).to be_nil
|
|
561
|
-
end
|
|
562
|
-
end
|
|
563
|
-
|
|
564
|
-
describe "color" do
|
|
565
|
-
it "treats them as positional arguments" do
|
|
566
|
-
expect(command.color).to be_nil
|
|
567
|
-
end
|
|
568
|
-
end
|
|
569
|
-
|
|
570
|
-
end
|
|
571
|
-
|
|
572
|
-
context "with an option terminator" do
|
|
573
|
-
|
|
574
|
-
it "considers everything after the terminator to be an argument" do
|
|
575
|
-
command.parse(%w[--color blue -- --flavour strawberry])
|
|
576
|
-
expect(command.arguments).to eq %w[--flavour strawberry]
|
|
577
|
-
end
|
|
578
|
-
|
|
579
|
-
end
|
|
580
|
-
|
|
581
|
-
context "with --flag" do
|
|
582
|
-
|
|
583
|
-
before do
|
|
584
|
-
command.parse(%w[--nuts])
|
|
585
|
-
end
|
|
586
|
-
|
|
587
|
-
it "sets the flag" do
|
|
588
|
-
expect(command.nuts?).to be true
|
|
589
|
-
end
|
|
590
|
-
|
|
591
|
-
end
|
|
592
|
-
|
|
593
|
-
context "with --no-flag" do
|
|
594
|
-
|
|
595
|
-
before do
|
|
596
|
-
command.nuts = true
|
|
597
|
-
command.parse(%w[--no-nuts])
|
|
598
|
-
end
|
|
599
|
-
|
|
600
|
-
it "clears the flag" do
|
|
601
|
-
expect(command.nuts?).to be false
|
|
602
|
-
end
|
|
603
|
-
|
|
604
|
-
end
|
|
605
|
-
|
|
606
|
-
context "with --help" do
|
|
607
|
-
|
|
608
|
-
it "requests help" do
|
|
609
|
-
expect do
|
|
610
|
-
command.parse(%w[--help])
|
|
611
|
-
end.to raise_error(Clamp::HelpWanted)
|
|
612
|
-
end
|
|
613
|
-
|
|
614
|
-
end
|
|
615
|
-
|
|
616
|
-
context "with -h" do
|
|
617
|
-
|
|
618
|
-
it "requests help" do
|
|
619
|
-
expect do
|
|
620
|
-
command.parse(%w[-h])
|
|
621
|
-
end.to raise_error(Clamp::HelpWanted)
|
|
622
|
-
end
|
|
623
|
-
|
|
624
|
-
end
|
|
625
|
-
|
|
626
|
-
context "when no option value is provided" do
|
|
627
|
-
|
|
628
|
-
it "signals a UsageError" do
|
|
629
|
-
expect do
|
|
630
|
-
command.parse(%w[--flavour])
|
|
631
|
-
end.to raise_error(Clamp::UsageError, /^option '--flavour': no value provided/)
|
|
632
|
-
end
|
|
633
|
-
|
|
634
|
-
end
|
|
635
|
-
|
|
636
|
-
context "when a bad option value is specified on the command-line" do
|
|
637
|
-
|
|
638
|
-
it "signals a UsageError" do
|
|
639
|
-
expect do
|
|
640
|
-
command.parse(%w[--scoops reginald])
|
|
641
|
-
end.to raise_error(Clamp::UsageError, /^option '--scoops': invalid value for Integer/)
|
|
642
|
-
end
|
|
643
|
-
|
|
644
|
-
end
|
|
645
|
-
|
|
646
|
-
context "when a bad option value is specified in the environment" do
|
|
647
|
-
|
|
648
|
-
it "signals a UsageError" do
|
|
649
|
-
ENV["DEFAULT_SCOOPS"] = "marjorie"
|
|
650
|
-
expect do
|
|
651
|
-
command.parse([])
|
|
652
|
-
end.to raise_error(Clamp::UsageError, /^\$DEFAULT_SCOOPS: invalid value for Integer/)
|
|
653
|
-
end
|
|
654
|
-
|
|
655
|
-
end
|
|
656
|
-
|
|
657
|
-
end
|
|
658
|
-
|
|
659
|
-
describe "#help" do
|
|
660
|
-
|
|
661
|
-
it "indicates that there are options" do
|
|
662
|
-
expect(command.help).to include("cmd [OPTIONS]")
|
|
663
|
-
end
|
|
664
|
-
|
|
665
|
-
it "includes option details" do
|
|
666
|
-
flavour_help = "-f, --flavour FLAVOUR +Flavour of the month"
|
|
667
|
-
color_help = "-c, --color COLOR +Preferred hue"
|
|
668
|
-
|
|
669
|
-
expect(command.help).to match(/#{flavour_help}\n +#{color_help}/)
|
|
670
|
-
end
|
|
671
|
-
|
|
672
|
-
it "handles new lines in option descriptions" do
|
|
673
|
-
expect(command.help).to match(/--\[no-\]nuts +Nuts \(or not\)\n +May include nuts/)
|
|
674
|
-
end
|
|
675
|
-
|
|
676
|
-
end
|
|
677
|
-
|
|
678
|
-
end
|
|
679
|
-
|
|
680
|
-
context "with an explicit --help option declared" do
|
|
681
|
-
|
|
682
|
-
before do
|
|
683
|
-
command.class.option ["--help"], :flag, "help wanted"
|
|
684
|
-
end
|
|
685
|
-
|
|
686
|
-
describe "parsing" do
|
|
687
|
-
it "does not generate implicit help option" do
|
|
688
|
-
expect do
|
|
689
|
-
command.parse(%w[--help])
|
|
690
|
-
end.not_to raise_error
|
|
691
|
-
end
|
|
692
|
-
end
|
|
693
|
-
|
|
694
|
-
describe "help?" do
|
|
695
|
-
before do
|
|
696
|
-
command.parse(%w[--help])
|
|
697
|
-
end
|
|
698
|
-
|
|
699
|
-
it "generates custom help option" do
|
|
700
|
-
expect(command.help?).to be true
|
|
701
|
-
end
|
|
702
|
-
end
|
|
703
|
-
|
|
704
|
-
it "does not recognise -h" do
|
|
705
|
-
expect do
|
|
706
|
-
command.parse(%w[-h])
|
|
707
|
-
end.to raise_error(Clamp::UsageError)
|
|
708
|
-
end
|
|
709
|
-
|
|
710
|
-
end
|
|
711
|
-
|
|
712
|
-
context "with an explicit -h option declared" do
|
|
713
|
-
|
|
714
|
-
before do
|
|
715
|
-
command.class.option ["-h", "--humidity"], "PERCENT", "relative humidity" do |n|
|
|
716
|
-
Integer(n)
|
|
717
|
-
end
|
|
718
|
-
end
|
|
719
|
-
|
|
720
|
-
it "does not map -h to help" do
|
|
721
|
-
expect(command.help).not_to match(/-h[, ].*help/)
|
|
722
|
-
end
|
|
723
|
-
|
|
724
|
-
it "still recognizes --help" do
|
|
725
|
-
expect do
|
|
726
|
-
command.parse(%w[--help])
|
|
727
|
-
end.to raise_error(Clamp::HelpWanted)
|
|
728
|
-
end
|
|
729
|
-
|
|
730
|
-
end
|
|
731
|
-
|
|
732
|
-
describe ".parameter" do
|
|
733
|
-
|
|
734
|
-
context "when regular" do
|
|
735
|
-
|
|
736
|
-
before do
|
|
737
|
-
command.class.parameter "FLAVOUR", "flavour of the month"
|
|
738
|
-
end
|
|
739
|
-
|
|
740
|
-
context "when not set" do
|
|
741
|
-
it "equals nil" do
|
|
742
|
-
expect(command.flavour).to be_nil
|
|
743
|
-
end
|
|
744
|
-
end
|
|
745
|
-
|
|
746
|
-
context "when set" do
|
|
747
|
-
before do
|
|
748
|
-
command.flavour = "chocolate"
|
|
749
|
-
end
|
|
750
|
-
|
|
751
|
-
it "equals the value" do
|
|
752
|
-
expect(command.flavour).to eq "chocolate"
|
|
753
|
-
end
|
|
754
|
-
end
|
|
755
|
-
|
|
756
|
-
end
|
|
757
|
-
|
|
758
|
-
context "with explicit :attribute_name" do
|
|
759
|
-
|
|
760
|
-
before do
|
|
761
|
-
command.class.parameter "FOO", "a foo", attribute_name: :bar
|
|
762
|
-
end
|
|
763
|
-
|
|
764
|
-
it "uses the specified attribute_name name to name accessors" do
|
|
765
|
-
command.bar = "chocolate"
|
|
766
|
-
expect(command.bar).to eq "chocolate"
|
|
767
|
-
end
|
|
768
|
-
|
|
769
|
-
end
|
|
770
|
-
|
|
771
|
-
context "with :default value" do
|
|
772
|
-
|
|
773
|
-
before do
|
|
774
|
-
command.class.parameter "[ORIENTATION]", "direction", default: "west"
|
|
775
|
-
end
|
|
776
|
-
|
|
777
|
-
it "sets the specified default value" do
|
|
778
|
-
expect(command.orientation).to eq "west"
|
|
779
|
-
end
|
|
780
|
-
|
|
781
|
-
describe "#help" do
|
|
782
|
-
|
|
783
|
-
it "describes the default value" do
|
|
784
|
-
expect(command.help).to include("direction (default: \"west\")")
|
|
785
|
-
end
|
|
786
|
-
|
|
787
|
-
end
|
|
788
|
-
|
|
789
|
-
end
|
|
790
|
-
|
|
791
|
-
context "with a block" do
|
|
792
|
-
|
|
793
|
-
before do
|
|
794
|
-
command.class.parameter "PORT", "port to listen on" do |port|
|
|
795
|
-
Integer(port)
|
|
796
|
-
end
|
|
797
|
-
end
|
|
798
|
-
|
|
799
|
-
context "when value is incorrect" do
|
|
800
|
-
|
|
801
|
-
it "raises an error" do
|
|
802
|
-
expect do
|
|
803
|
-
command.port = "blah"
|
|
804
|
-
end.to raise_error(ArgumentError)
|
|
805
|
-
end
|
|
806
|
-
|
|
807
|
-
end
|
|
808
|
-
|
|
809
|
-
context "when value is convertible" do
|
|
810
|
-
|
|
811
|
-
it "uses the block to validate and convert the argument" do
|
|
812
|
-
command.port = "1234"
|
|
813
|
-
expect(command.port).to eq 1234
|
|
814
|
-
end
|
|
815
|
-
|
|
816
|
-
end
|
|
817
|
-
|
|
818
|
-
end
|
|
819
|
-
|
|
820
|
-
context "with ellipsis" do
|
|
821
|
-
|
|
822
|
-
before do
|
|
823
|
-
command.class.parameter "FILE ...", "files"
|
|
824
|
-
end
|
|
825
|
-
|
|
826
|
-
it "accepts multiple arguments" do
|
|
827
|
-
command.parse(%w[X Y Z])
|
|
828
|
-
expect(command.file_list).to eq %w[X Y Z]
|
|
829
|
-
end
|
|
830
|
-
|
|
831
|
-
end
|
|
832
|
-
|
|
833
|
-
context "when optional, with ellipsis" do
|
|
834
|
-
|
|
835
|
-
before do
|
|
836
|
-
command.class.parameter "[FILE] ...", "files"
|
|
837
|
-
|
|
838
|
-
command.parse([])
|
|
839
|
-
end
|
|
840
|
-
|
|
841
|
-
describe "default_file_list" do
|
|
842
|
-
|
|
843
|
-
it "defaults to an empty list" do
|
|
844
|
-
expect(command.default_file_list).to be_empty
|
|
845
|
-
end
|
|
846
|
-
|
|
847
|
-
end
|
|
848
|
-
|
|
849
|
-
describe "file_list" do
|
|
850
|
-
|
|
851
|
-
it "defaults to an empty list" do
|
|
852
|
-
expect(command.file_list).to be_empty
|
|
853
|
-
end
|
|
854
|
-
|
|
855
|
-
end
|
|
856
|
-
|
|
857
|
-
describe "mutation" do
|
|
858
|
-
|
|
859
|
-
before do
|
|
860
|
-
command.file_list << "treasure"
|
|
861
|
-
end
|
|
862
|
-
|
|
863
|
-
it "is mutable" do
|
|
864
|
-
expect(command.file_list).to eq ["treasure"]
|
|
865
|
-
end
|
|
866
|
-
|
|
867
|
-
end
|
|
868
|
-
|
|
869
|
-
end
|
|
870
|
-
|
|
871
|
-
context "with :environment_variable" do
|
|
872
|
-
|
|
873
|
-
before do
|
|
874
|
-
command.class.parameter "[FILE]", "a file", environment_variable: "FILE",
|
|
875
|
-
default: "/dev/null"
|
|
876
|
-
|
|
877
|
-
set_env("FILE", environment_value)
|
|
878
|
-
command.parse(args)
|
|
879
|
-
end
|
|
880
|
-
|
|
881
|
-
let(:args) { [] }
|
|
882
|
-
let(:environment_value) { nil }
|
|
883
|
-
|
|
884
|
-
context "when neither argument nor environment variable are present" do
|
|
885
|
-
|
|
886
|
-
it "uses the default" do
|
|
887
|
-
expect(command.file).to eq File::NULL
|
|
888
|
-
end
|
|
889
|
-
|
|
890
|
-
end
|
|
891
|
-
|
|
892
|
-
context "when environment variable is present" do
|
|
893
|
-
|
|
894
|
-
let(:environment_value) { "/etc/motd" }
|
|
895
|
-
|
|
896
|
-
describe "and no argument is provided" do
|
|
897
|
-
|
|
898
|
-
it "uses the environment variable" do
|
|
899
|
-
expect(command.file).to eq "/etc/motd"
|
|
900
|
-
end
|
|
901
|
-
|
|
902
|
-
end
|
|
903
|
-
|
|
904
|
-
describe "and an argument is provided" do
|
|
905
|
-
|
|
906
|
-
let(:args) { ["/dev/null"] }
|
|
907
|
-
|
|
908
|
-
it "uses the argument" do
|
|
909
|
-
expect(command.file).to eq File::NULL
|
|
910
|
-
end
|
|
911
|
-
|
|
912
|
-
end
|
|
913
|
-
|
|
914
|
-
end
|
|
915
|
-
|
|
916
|
-
describe "#help" do
|
|
917
|
-
|
|
918
|
-
it "describes the default value and env usage" do
|
|
919
|
-
expect(command.help).to include(%{ (default: $FILE, or "/dev/null")})
|
|
920
|
-
end
|
|
921
|
-
|
|
922
|
-
end
|
|
923
|
-
|
|
924
|
-
end
|
|
925
|
-
|
|
926
|
-
end
|
|
927
|
-
|
|
928
|
-
context "with no parameters declared" do
|
|
929
|
-
|
|
930
|
-
describe "#parse" do
|
|
931
|
-
|
|
932
|
-
context "with arguments" do
|
|
933
|
-
|
|
934
|
-
it "raises a UsageError" do
|
|
935
|
-
expect do
|
|
936
|
-
command.parse(["crash"])
|
|
937
|
-
end.to raise_error(Clamp::UsageError, "too many arguments")
|
|
938
|
-
end
|
|
939
|
-
|
|
940
|
-
end
|
|
941
|
-
|
|
942
|
-
end
|
|
943
|
-
|
|
944
|
-
end
|
|
945
|
-
|
|
946
|
-
context "with parameters declared" do
|
|
947
|
-
|
|
948
|
-
before do
|
|
949
|
-
command.class.parameter "X", "x\nxx"
|
|
950
|
-
command.class.parameter "Y", "y"
|
|
951
|
-
command.class.parameter "[Z]", "z", default: "ZZZ"
|
|
952
|
-
end
|
|
953
|
-
|
|
954
|
-
describe "#parse" do
|
|
955
|
-
|
|
956
|
-
context "with arguments for all parameters" do
|
|
957
|
-
|
|
958
|
-
before do
|
|
959
|
-
command.parse(["crash", "bang", "wallop"])
|
|
960
|
-
end
|
|
961
|
-
|
|
962
|
-
describe "x" do
|
|
963
|
-
it "maps arguments onto the command object" do
|
|
964
|
-
expect(command.x).to eq "crash"
|
|
965
|
-
end
|
|
966
|
-
end
|
|
967
|
-
|
|
968
|
-
describe "y" do
|
|
969
|
-
it "maps arguments onto the command object" do
|
|
970
|
-
expect(command.y).to eq "bang"
|
|
971
|
-
end
|
|
972
|
-
end
|
|
973
|
-
|
|
974
|
-
describe "z" do
|
|
975
|
-
it "maps arguments onto the command object" do
|
|
976
|
-
expect(command.z).to eq "wallop"
|
|
977
|
-
end
|
|
978
|
-
end
|
|
979
|
-
|
|
980
|
-
end
|
|
981
|
-
|
|
982
|
-
context "with insufficient arguments" do
|
|
983
|
-
|
|
984
|
-
it "raises a UsageError" do
|
|
985
|
-
expect do
|
|
986
|
-
command.parse(["crash"])
|
|
987
|
-
end.to raise_error(Clamp::UsageError, "parameter 'Y': no value provided")
|
|
988
|
-
end
|
|
989
|
-
|
|
990
|
-
end
|
|
991
|
-
|
|
992
|
-
context "with optional argument omitted" do
|
|
993
|
-
|
|
994
|
-
before do
|
|
995
|
-
command.parse(["crash", "bang"])
|
|
996
|
-
end
|
|
997
|
-
|
|
998
|
-
describe "x" do
|
|
999
|
-
it "defaults the optional argument" do
|
|
1000
|
-
expect(command.x).to eq "crash"
|
|
1001
|
-
end
|
|
1002
|
-
end
|
|
1003
|
-
|
|
1004
|
-
describe "y" do
|
|
1005
|
-
it "defaults the optional argument" do
|
|
1006
|
-
expect(command.y).to eq "bang"
|
|
1007
|
-
end
|
|
1008
|
-
end
|
|
1009
|
-
|
|
1010
|
-
describe "z" do
|
|
1011
|
-
it "defaults the optional argument" do
|
|
1012
|
-
expect(command.z).to eq "ZZZ"
|
|
1013
|
-
end
|
|
1014
|
-
end
|
|
1015
|
-
|
|
1016
|
-
end
|
|
1017
|
-
|
|
1018
|
-
context "with multi-line arguments" do
|
|
1019
|
-
|
|
1020
|
-
before do
|
|
1021
|
-
command.parse(["foo\nhi", "bar", "baz"])
|
|
1022
|
-
end
|
|
1023
|
-
|
|
1024
|
-
describe "x" do
|
|
1025
|
-
it "parses them correctly" do
|
|
1026
|
-
expect(command.x).to eq "foo\nhi"
|
|
1027
|
-
end
|
|
1028
|
-
end
|
|
1029
|
-
|
|
1030
|
-
describe "y" do
|
|
1031
|
-
it "parses them correctly" do
|
|
1032
|
-
expect(command.y).to eq "bar"
|
|
1033
|
-
end
|
|
1034
|
-
end
|
|
1035
|
-
|
|
1036
|
-
describe "z" do
|
|
1037
|
-
it "parses them correctly" do
|
|
1038
|
-
expect(command.z).to eq "baz"
|
|
1039
|
-
end
|
|
1040
|
-
end
|
|
1041
|
-
|
|
1042
|
-
end
|
|
1043
|
-
|
|
1044
|
-
context "with too many arguments" do
|
|
1045
|
-
|
|
1046
|
-
it "raises a UsageError" do
|
|
1047
|
-
expect do
|
|
1048
|
-
command.parse(["crash", "bang", "wallop", "kapow"])
|
|
1049
|
-
end.to raise_error(Clamp::UsageError, "too many arguments")
|
|
1050
|
-
end
|
|
1051
|
-
|
|
1052
|
-
end
|
|
1053
|
-
|
|
1054
|
-
end
|
|
1055
|
-
|
|
1056
|
-
describe "#help" do
|
|
1057
|
-
|
|
1058
|
-
it "indicates that there are parameters" do
|
|
1059
|
-
expect(command.help).to include("cmd [OPTIONS] X Y [Z]")
|
|
1060
|
-
end
|
|
1061
|
-
|
|
1062
|
-
it "includes parameter details" do
|
|
1063
|
-
expect(command.help).to match(/X +x\n[\s\S]* +Y +y\n[\s\S]* +\[Z\] +z \(default: "ZZZ"\)/)
|
|
1064
|
-
end
|
|
1065
|
-
|
|
1066
|
-
it "handles new lines in option descriptions" do
|
|
1067
|
-
expect(command.help).to match(/X +x\n +xx/)
|
|
1068
|
-
end
|
|
1069
|
-
|
|
1070
|
-
end
|
|
1071
|
-
|
|
1072
|
-
end
|
|
1073
|
-
|
|
1074
|
-
describe ".execute" do
|
|
1075
|
-
|
|
1076
|
-
before do
|
|
1077
|
-
|
|
1078
|
-
command_class.class_eval do
|
|
1079
|
-
execute do
|
|
1080
|
-
puts "using execute DSL"
|
|
1081
|
-
end
|
|
1082
|
-
end
|
|
1083
|
-
|
|
1084
|
-
command.run([])
|
|
1085
|
-
|
|
1086
|
-
end
|
|
1087
|
-
|
|
1088
|
-
it "provides an alternative way to declare execute method" do
|
|
1089
|
-
expect(stdout).to eq("using execute DSL\n")
|
|
1090
|
-
end
|
|
1091
|
-
|
|
1092
|
-
end
|
|
1093
|
-
|
|
1094
|
-
context "with explicit usage" do
|
|
1095
|
-
|
|
1096
|
-
given_command("blah") do
|
|
1097
|
-
|
|
1098
|
-
usage "FOO BAR ..."
|
|
1099
|
-
|
|
1100
|
-
end
|
|
1101
|
-
|
|
1102
|
-
describe "#help" do
|
|
1103
|
-
|
|
1104
|
-
it "includes the explicit usage" do
|
|
1105
|
-
expect(command.help).to include("blah FOO BAR ...\n")
|
|
1106
|
-
end
|
|
1107
|
-
|
|
1108
|
-
end
|
|
1109
|
-
|
|
1110
|
-
end
|
|
1111
|
-
|
|
1112
|
-
context "with multiple usages" do
|
|
1113
|
-
|
|
1114
|
-
given_command("put") do
|
|
1115
|
-
|
|
1116
|
-
usage "THIS HERE"
|
|
1117
|
-
usage "THAT THERE"
|
|
1118
|
-
|
|
1119
|
-
end
|
|
1120
|
-
|
|
1121
|
-
describe "#help" do
|
|
1122
|
-
|
|
1123
|
-
it "includes both potential usages" do
|
|
1124
|
-
expect(command.help).to match(/put THIS HERE\n +put THAT THERE/)
|
|
1125
|
-
end
|
|
1126
|
-
|
|
1127
|
-
end
|
|
1128
|
-
|
|
1129
|
-
end
|
|
1130
|
-
|
|
1131
|
-
context "with a banner" do
|
|
1132
|
-
|
|
1133
|
-
given_command("punt") do
|
|
1134
|
-
|
|
1135
|
-
banner <<-TEXT
|
|
1136
|
-
Punt is an example command. It doesn't do much, really.
|
|
1137
|
-
|
|
1138
|
-
The prefix at the beginning of this description should be normalised
|
|
1139
|
-
to two spaces.
|
|
1140
|
-
TEXT
|
|
1141
|
-
|
|
1142
|
-
end
|
|
1143
|
-
|
|
1144
|
-
describe "#help" do
|
|
1145
|
-
|
|
1146
|
-
it "includes the banner" do
|
|
1147
|
-
expect(command.help).to match(/^ Punt is an example command.+\n^ \n^ The prefix/)
|
|
1148
|
-
end
|
|
1149
|
-
|
|
1150
|
-
end
|
|
1151
|
-
|
|
1152
|
-
end
|
|
1153
|
-
|
|
1154
|
-
describe ".run" do
|
|
1155
|
-
|
|
1156
|
-
context "when regular" do
|
|
1157
|
-
|
|
1158
|
-
before do
|
|
1159
|
-
|
|
1160
|
-
command.class.class_eval do
|
|
1161
|
-
parameter "WORD ...", "words"
|
|
1162
|
-
def execute
|
|
1163
|
-
print word_list.inspect
|
|
1164
|
-
end
|
|
1165
|
-
end
|
|
1166
|
-
|
|
1167
|
-
end
|
|
1168
|
-
|
|
1169
|
-
it "creates a new Command instance and runs it" do
|
|
1170
|
-
xyz = %w[x y z]
|
|
1171
|
-
command.class.run("cmd", xyz)
|
|
1172
|
-
expect(stdout).to eq xyz.inspect
|
|
1173
|
-
end
|
|
1174
|
-
|
|
1175
|
-
end
|
|
1176
|
-
|
|
1177
|
-
context "when invoked with a context hash" do
|
|
1178
|
-
|
|
1179
|
-
before do
|
|
1180
|
-
|
|
1181
|
-
command.class.class_eval do
|
|
1182
|
-
def execute
|
|
1183
|
-
print context[:foo]
|
|
1184
|
-
end
|
|
1185
|
-
end
|
|
1186
|
-
|
|
1187
|
-
end
|
|
1188
|
-
|
|
1189
|
-
it "makes the context available within the command" do
|
|
1190
|
-
command.class.run("xyz", [], foo: "bar")
|
|
1191
|
-
expect(stdout).to eq "bar"
|
|
1192
|
-
end
|
|
1193
|
-
|
|
1194
|
-
end
|
|
1195
|
-
|
|
1196
|
-
context "when there's a CommandError" do
|
|
1197
|
-
|
|
1198
|
-
before do
|
|
1199
|
-
|
|
1200
|
-
command.class.class_eval do
|
|
1201
|
-
def execute
|
|
1202
|
-
signal_error "Oh crap!", status: 456
|
|
1203
|
-
end
|
|
1204
|
-
end
|
|
1205
|
-
|
|
1206
|
-
end
|
|
1207
|
-
|
|
1208
|
-
it "outputs the error message and exits with the specified status" do # rubocop:disable RSpec/ExampleLength
|
|
1209
|
-
expect { command.class.run("cmd", []) }
|
|
1210
|
-
.to raise_error(
|
|
1211
|
-
an_instance_of(SystemExit).and(having_attributes(status: 456))
|
|
1212
|
-
).and output(<<~TEXT).to_stderr
|
|
1213
|
-
ERROR: Oh crap!
|
|
1214
|
-
TEXT
|
|
1215
|
-
end
|
|
1216
|
-
|
|
1217
|
-
end
|
|
1218
|
-
|
|
1219
|
-
context "when there's a UsageError" do
|
|
1220
|
-
|
|
1221
|
-
before do
|
|
1222
|
-
|
|
1223
|
-
command.class.class_eval do
|
|
1224
|
-
def execute
|
|
1225
|
-
signal_usage_error "bad dog!"
|
|
1226
|
-
end
|
|
1227
|
-
end
|
|
1228
|
-
|
|
1229
|
-
begin
|
|
1230
|
-
command.class.run("cmd", [])
|
|
1231
|
-
rescue SystemExit => e
|
|
1232
|
-
@system_exit = e
|
|
1233
|
-
end
|
|
1234
|
-
|
|
1235
|
-
end
|
|
1236
|
-
|
|
1237
|
-
it "outputs the error message and help" do # rubocop:disable RSpec/ExampleLength
|
|
1238
|
-
expect { command.class.run("cmd", []) }
|
|
1239
|
-
.to raise_error(
|
|
1240
|
-
an_instance_of(SystemExit).and(having_attributes(status: 1))
|
|
1241
|
-
).and output(<<~TEXT).to_stderr
|
|
1242
|
-
ERROR: bad dog!
|
|
1243
|
-
|
|
1244
|
-
See: 'cmd --help'
|
|
1245
|
-
TEXT
|
|
1246
|
-
end
|
|
1247
|
-
|
|
1248
|
-
end
|
|
1249
|
-
|
|
1250
|
-
context "when help is requested" do
|
|
1251
|
-
|
|
1252
|
-
it "outputs help" do
|
|
1253
|
-
command.class.run("cmd", ["--help"])
|
|
1254
|
-
expect(stdout).to include "Usage:"
|
|
1255
|
-
end
|
|
1256
|
-
|
|
1257
|
-
end
|
|
1258
|
-
|
|
1259
|
-
end
|
|
1260
|
-
|
|
1261
|
-
describe "subclass" do
|
|
1262
|
-
|
|
1263
|
-
let(:command) do
|
|
1264
|
-
parent_command_class = Class.new(Clamp::Command) do
|
|
1265
|
-
option "--verbose", :flag, "be louder"
|
|
1266
|
-
end
|
|
1267
|
-
derived_command_class = Class.new(parent_command_class) do
|
|
1268
|
-
option "--iterations", "N", "number of times to go around"
|
|
1269
|
-
end
|
|
1270
|
-
derived_command_class.new("cmd")
|
|
1271
|
-
end
|
|
1272
|
-
|
|
1273
|
-
it "inherits options from it's superclass" do
|
|
1274
|
-
command.parse(["--verbose"])
|
|
1275
|
-
expect(command).to be_verbose
|
|
1276
|
-
end
|
|
1277
|
-
|
|
1278
|
-
end
|
|
1279
|
-
|
|
1280
|
-
end
|