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.
@@ -1,438 +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
-
10
- context "with subcommands" do
11
-
12
- given_command "flipflop" do
13
-
14
- def execute
15
- puts message
16
- end
17
-
18
- subcommand "flip", "flip it" do
19
- def message
20
- "FLIPPED"
21
- end
22
- end
23
-
24
- subcommand "flop", "flop it\nfor extra flop" do
25
- def message
26
- "FLOPPED"
27
- end
28
- end
29
-
30
- end
31
-
32
- describe "flip command" do
33
- before do
34
- command.run(["flip"])
35
- end
36
-
37
- it "delegates to sub-commands" do
38
- expect(stdout).to match(/FLIPPED/)
39
- end
40
- end
41
-
42
- describe "flop command" do
43
- before do
44
- command.run(["flop"])
45
- end
46
-
47
- it "delegates to sub-commands" do
48
- expect(stdout).to match(/FLOPPED/)
49
- end
50
- end
51
-
52
- context "when executed with no subcommand" do
53
-
54
- it "triggers help" do
55
- expect do
56
- command.run([])
57
- end.to raise_error(Clamp::HelpWanted)
58
- end
59
-
60
- end
61
-
62
- describe "#help" do
63
-
64
- it "shows subcommand parameters in usage" do
65
- expect(command.help).to include("flipflop [OPTIONS] SUBCOMMAND [ARG] ...")
66
- end
67
-
68
- it "lists subcommands" do
69
- expect(command.help).to match(/Subcommands:\n +flip +flip it\n +flop +flop it/)
70
- end
71
-
72
- it "handles new lines in subcommand descriptions" do
73
- expect(command.help).to match(/flop +flop it\n +for extra flop/)
74
- end
75
-
76
- end
77
-
78
- describe ".find_subcommand_class" do
79
-
80
- it "finds subcommand classes" do
81
- flip_class = command_class.find_subcommand_class("flip")
82
- expect(flip_class.new("xx").message).to eq("FLIPPED")
83
- end
84
-
85
- end
86
-
87
- end
88
-
89
- context "with an aliased subcommand" do
90
-
91
- given_command "blah" do
92
-
93
- subcommand ["say", "talk"], "Say something" do
94
-
95
- parameter "WORD ...", "stuff to say"
96
-
97
- def execute
98
- puts word_list
99
- end
100
-
101
- end
102
-
103
- end
104
-
105
- describe "the first alias" do
106
-
107
- before do
108
- command.run(["say", "boo"])
109
- end
110
-
111
- it "responds to it" do
112
- expect(stdout).to match(/boo/)
113
- end
114
-
115
- end
116
-
117
- describe "the second alias" do
118
-
119
- before do
120
- command.run(["talk", "jive"])
121
- end
122
-
123
- it "responds to it" do
124
- expect(stdout).to match(/jive/)
125
- end
126
-
127
- end
128
-
129
- describe "#help" do
130
-
131
- it "lists all aliases" do
132
- help = command.help
133
- expect(help).to match(/say, talk .* Say something/)
134
- end
135
-
136
- end
137
-
138
- end
139
-
140
- context "with nested subcommands" do
141
-
142
- given_command "fubar" do
143
-
144
- subcommand "foo", "Foo!" do
145
-
146
- subcommand "bar", "Baaaa!" do
147
-
148
- def self.this_is_bar; end
149
-
150
- def execute
151
- puts "FUBAR"
152
- end
153
-
154
- end
155
-
156
- end
157
-
158
- end
159
-
160
- it "delegates multiple levels" do
161
- command.run(["foo", "bar"])
162
- expect(stdout).to match(/FUBAR/)
163
- end
164
-
165
- describe ".find_subcommand_class" do
166
-
167
- it "finds nested subcommands" do
168
- expect(command_class.find_subcommand_class("foo", "bar")).to respond_to(:this_is_bar)
169
- end
170
-
171
- end
172
-
173
- end
174
-
175
- context "with a default subcommand" do
176
-
177
- given_command "admin" do
178
-
179
- self.default_subcommand = "status"
180
-
181
- subcommand "status", "Show status" do
182
-
183
- def execute
184
- puts "All good!"
185
- end
186
-
187
- end
188
-
189
- end
190
-
191
- context "when executed with no subcommand" do
192
-
193
- it "invokes the default subcommand" do
194
- command.run([])
195
- expect(stdout).to match(/All good/)
196
- end
197
-
198
- end
199
-
200
- end
201
-
202
- context "with a default subcommand, declared the old way" do
203
-
204
- given_command "admin" do
205
-
206
- default_subcommand "status", "Show status" do
207
-
208
- def execute
209
- puts "All good!"
210
- end
211
-
212
- end
213
-
214
- end
215
-
216
- context "when executed with no subcommand" do
217
-
218
- it "invokes the default subcommand" do
219
- command.run([])
220
- expect(stdout).to match(/All good/)
221
- end
222
-
223
- end
224
-
225
- end
226
-
227
- context "when declaring a default subcommand after subcommands" do
228
-
229
- let(:command) do
230
- Class.new(Clamp::Command) do
231
-
232
- subcommand "status", "Show status" do
233
-
234
- def execute
235
- puts "All good!"
236
- end
237
-
238
- end
239
-
240
- end
241
- end
242
-
243
- it "is not supported" do
244
-
245
- expect do
246
- command.default_subcommand = "status"
247
- end.to raise_error(/default_subcommand must be defined before subcommands/)
248
-
249
- end
250
-
251
- end
252
-
253
- context "with subcommands, declared after a parameter" do
254
-
255
- given_command "with" do
256
-
257
- parameter "THING", "the thing"
258
-
259
- subcommand "spit", "spit it" do
260
- def execute
261
- puts "spat the #{thing}"
262
- end
263
- end
264
-
265
- subcommand "say", "say it" do
266
- subcommand "loud", "yell it" do
267
- def execute
268
- puts thing.upcase
269
- end
270
- end
271
- end
272
-
273
- end
274
-
275
- it "allows the parameter to be specified first" do
276
- command.run(["dummy", "spit"])
277
- expect(stdout.strip).to eq "spat the dummy"
278
- end
279
-
280
- it "passes the parameter down the stack" do
281
- command.run(["money", "say", "loud"])
282
- expect(stdout.strip).to eq "MONEY"
283
- end
284
-
285
- it "shows parameter in usage help" do
286
- command.run(["stuff", "say", "loud", "--help"])
287
- rescue Clamp::HelpWanted => e
288
- expect(e.command.invocation_path).to eq "with THING say loud"
289
- end
290
-
291
- end
292
-
293
- describe "each subcommand" do
294
-
295
- let(:command_class) do
296
-
297
- speed_options = Module.new do
298
- extend Clamp::Option::Declaration
299
-
300
- option "--speed", "SPEED", "how fast", default: "slowly"
301
- end
302
-
303
- Class.new(Clamp::Command) do
304
-
305
- option "--direction", "DIR", "which way", default: "home"
306
-
307
- include speed_options
308
-
309
- subcommand "move", "move in the appointed direction" do
310
-
311
- def execute
312
- motion = context[:motion] || "walking"
313
- puts "#{motion} #{direction} #{speed}"
314
- end
315
-
316
- end
317
-
318
- end
319
- end
320
-
321
- let(:command) do
322
- command_class.new("go")
323
- end
324
-
325
- it "accepts options defined in superclass (specified after the subcommand)" do
326
- command.run(["move", "--direction", "north"])
327
- expect(stdout).to match(/walking north/)
328
- end
329
-
330
- it "accepts options defined in superclass (specified before the subcommand)" do
331
- command.run(["--direction", "north", "move"])
332
- expect(stdout).to match(/walking north/)
333
- end
334
-
335
- it "accepts options defined in included modules" do
336
- command.run(["move", "--speed", "very quickly"])
337
- expect(stdout).to match(/walking home very quickly/)
338
- end
339
-
340
- it "has access to command context" do
341
- command = command_class.new("go", motion: "wandering")
342
- command.run(["move"])
343
- expect(stdout).to match(/wandering home/)
344
- end
345
-
346
- end
347
-
348
- context "with a subcommand, with options" do
349
-
350
- given_command "weeheehee" do
351
- option "--json", "JSON", "a json blob" do |option|
352
- print "parsing!"
353
- option
354
- end
355
-
356
- subcommand "woohoohoo", "like weeheehee but with more o" do
357
- def execute; end
358
- end
359
- end
360
-
361
- it "only parses options once" do
362
- command.run(["--json", '{"a":"b"}', "woohoohoo"])
363
- expect(stdout).to eq "parsing!"
364
- end
365
-
366
- end
367
-
368
- context "with an unknown subcommand" do
369
-
370
- let(:subcommand_missing) do
371
- Module.new do
372
- def subcommand_missing(_name)
373
- abort "there is no such thing"
374
- end
375
- end
376
- end
377
-
378
- let(:subcommand_missing_with_return) do
379
- Module.new do
380
- def subcommand_missing(_name)
381
- self.class.recognised_subcommands.first.subcommand_class
382
- end
383
- end
384
- end
385
-
386
- let(:command_class) do
387
-
388
- Class.new(Clamp::Command) do
389
- subcommand "test", "test subcommand" do
390
- def execute
391
- puts "known subcommand"
392
- end
393
- end
394
-
395
- def execute; end
396
- end
397
- end
398
-
399
- let(:command) do
400
- command_class.new("foo")
401
- end
402
-
403
- it "signals no such subcommand usage error" do
404
- expect { command.run(["foo"]) }.to raise_error(Clamp::UsageError, "No such sub-command 'foo'")
405
- end
406
-
407
- it "executes the subcommand missing method" do
408
- command.extend subcommand_missing
409
- expect { command.run(["foo"]) }.to raise_error(SystemExit, /there is no such thing/)
410
- end
411
-
412
- it "uses the subcommand class returned from subcommand_missing" do
413
- command.extend subcommand_missing_with_return
414
- command.run(["foo"])
415
- expect(stdout).to match(/known subcommand/)
416
- end
417
-
418
- end
419
-
420
- context "with a subcommand and required options" do
421
-
422
- given_command "movements" do
423
- option "--direction", "N|S|E|W", "bearing", required: true
424
- subcommand "hop", "Hop" do
425
- def execute
426
- puts "Hopping #{direction}"
427
- end
428
- end
429
- end
430
-
431
- it "allows options after the subcommand" do
432
- command.run(%w[hop --direction south])
433
- expect(stdout).to eq "Hopping south\n"
434
- end
435
-
436
- end
437
-
438
- end
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
-
5
- describe Clamp::Command do
6
-
7
- include OutputCapture
8
-
9
- context "with included module" do
10
-
11
- let(:command) do
12
-
13
- shared_options = Module.new do
14
- extend Clamp::Option::Declaration
15
-
16
- option "--size", "SIZE", default: 4
17
- end
18
-
19
- command_class = Class.new(Clamp::Command) do
20
-
21
- include shared_options
22
-
23
- def execute
24
- puts "size = #{size}"
25
- end
26
-
27
- end
28
-
29
- command_class.new("foo")
30
-
31
- end
32
-
33
- it "accepts options from included module" do
34
- command.run(["--size", "42"])
35
- expect(stdout).to eq "size = 42\n"
36
- end
37
-
38
- end
39
-
40
- end
@@ -1,58 +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
-
10
- context "with allow_options_after_parameters enabled" do
11
-
12
- before do
13
- Clamp.allow_options_after_parameters = true
14
- end
15
-
16
- after do
17
- Clamp.allow_options_after_parameters = false
18
- end
19
-
20
- given_command("cmd") do
21
-
22
- option ["-v", "--verbose"], :flag, "Be noisy"
23
-
24
- subcommand "say", "Say something" do
25
-
26
- option "--loud", :flag, "say it loud"
27
-
28
- parameter "WORDS ...", "the thing to say", attribute_name: :words
29
-
30
- def execute
31
- message = words.join(" ")
32
- message = message.upcase if loud?
33
- message *= 3 if verbose?
34
- $stdout.puts message
35
- end
36
-
37
- end
38
-
39
- end
40
-
41
- it "still works" do
42
- command.run(%w[say foo])
43
- expect(stdout).to eq "foo\n"
44
- end
45
-
46
- it "honours options after positional arguments" do
47
- command.run(%w[say blah --verbose])
48
- expect(stdout).to eq "blahblahblah\n"
49
- end
50
-
51
- it "honours options declared on subcommands" do
52
- command.run(%w[say --loud blah])
53
- expect(stdout).to eq "BLAH\n"
54
- end
55
-
56
- end
57
-
58
- end