clamp 1.2.0.beta1 → 1.3.2

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 (51) hide show
  1. checksums.yaml +5 -5
  2. data/.editorconfig +9 -0
  3. data/.gitignore +1 -1
  4. data/.rspec +1 -0
  5. data/.rubocop.yml +26 -19
  6. data/.travis.yml +3 -6
  7. data/CHANGES.md +17 -1
  8. data/Gemfile +8 -6
  9. data/Guardfile +3 -1
  10. data/README.md +36 -43
  11. data/Rakefile +8 -0
  12. data/clamp.gemspec +8 -6
  13. data/examples/admin +3 -2
  14. data/examples/defaulted +4 -3
  15. data/examples/flipflop +1 -0
  16. data/examples/fubar +1 -0
  17. data/examples/gitdown +2 -1
  18. data/examples/scoop +3 -2
  19. data/examples/speak +3 -2
  20. data/examples/subcommand_missing +1 -0
  21. data/examples/word +1 -0
  22. data/lib/clamp.rb +3 -1
  23. data/lib/clamp/attribute/declaration.rb +5 -0
  24. data/lib/clamp/attribute/definition.rb +25 -11
  25. data/lib/clamp/attribute/instance.rb +25 -3
  26. data/lib/clamp/command.rb +9 -1
  27. data/lib/clamp/errors.rb +7 -3
  28. data/lib/clamp/help.rb +38 -17
  29. data/lib/clamp/messages.rb +25 -15
  30. data/lib/clamp/option/declaration.rb +5 -1
  31. data/lib/clamp/option/definition.rb +9 -3
  32. data/lib/clamp/option/parsing.rb +38 -43
  33. data/lib/clamp/parameter/declaration.rb +4 -0
  34. data/lib/clamp/parameter/definition.rb +9 -3
  35. data/lib/clamp/parameter/parsing.rb +5 -1
  36. data/lib/clamp/subcommand/declaration.rb +17 -15
  37. data/lib/clamp/subcommand/definition.rb +5 -6
  38. data/lib/clamp/subcommand/execution.rb +12 -1
  39. data/lib/clamp/subcommand/parsing.rb +4 -0
  40. data/lib/clamp/truthy.rb +4 -2
  41. data/lib/clamp/version.rb +3 -1
  42. data/spec/clamp/command_group_spec.rb +29 -11
  43. data/spec/clamp/command_spec.rb +130 -48
  44. data/spec/clamp/help_spec.rb +63 -0
  45. data/spec/clamp/messages_spec.rb +5 -4
  46. data/spec/clamp/option/definition_spec.rb +13 -11
  47. data/spec/clamp/option_module_spec.rb +3 -1
  48. data/spec/clamp/option_reordering_spec.rb +6 -4
  49. data/spec/clamp/parameter/definition_spec.rb +14 -12
  50. data/spec/spec_helper.rb +3 -3
  51. metadata +9 -7
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Clamp
2
- VERSION = "1.2.0.beta1".freeze
4
+ VERSION = "1.3.2".freeze
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "spec_helper"
2
4
 
3
5
  describe Clamp::Command do
@@ -122,8 +124,7 @@ describe Clamp::Command do
122
124
 
123
125
  subcommand "bar", "Baaaa!" do
124
126
 
125
- def self.this_is_bar
126
- end
127
+ def self.this_is_bar; end
127
128
 
128
129
  def execute
129
130
  puts "FUBAR"
@@ -260,9 +261,9 @@ describe Clamp::Command do
260
261
 
261
262
  it "shows parameter in usage help" do
262
263
  begin
263
- command.run(["stuff", "say", "--help"])
264
+ command.run(["stuff", "say", "loud", "--help"])
264
265
  rescue Clamp::HelpWanted => e
265
- expect(e.command.invocation_path).to eql("with THING say")
266
+ expect(e.command.invocation_path).to eql("with THING say loud")
266
267
  end
267
268
  end
268
269
 
@@ -274,12 +275,12 @@ describe Clamp::Command do
274
275
 
275
276
  speed_options = Module.new do
276
277
  extend Clamp::Option::Declaration
277
- option "--speed", "SPEED", "how fast", :default => "slowly"
278
+ option "--speed", "SPEED", "how fast", default: "slowly"
278
279
  end
279
280
 
280
281
  Class.new(Clamp::Command) do
281
282
 
282
- option "--direction", "DIR", "which way", :default => "home"
283
+ option "--direction", "DIR", "which way", default: "home"
283
284
 
284
285
  include speed_options
285
286
 
@@ -315,7 +316,7 @@ describe Clamp::Command do
315
316
  end
316
317
 
317
318
  it "has access to command context" do
318
- command = command_class.new("go", :motion => "wandering")
319
+ command = command_class.new("go", motion: "wandering")
319
320
  command.run(["move"])
320
321
  expect(stdout).to match(/wandering home/)
321
322
  end
@@ -331,8 +332,7 @@ describe Clamp::Command do
331
332
  end
332
333
 
333
334
  subcommand "woohoohoo", "like weeheehee but with more o" do
334
- def execute
335
- end
335
+ def execute; end
336
336
  end
337
337
  end
338
338
 
@@ -370,8 +370,7 @@ describe Clamp::Command do
370
370
  end
371
371
  end
372
372
 
373
- def execute
374
- end
373
+ def execute; end
375
374
  end
376
375
  end
377
376
 
@@ -396,6 +395,25 @@ describe Clamp::Command do
396
395
  command.run(["foo"])
397
396
  expect(stdout).to match(/known subcommand/)
398
397
  end
398
+
399
+ end
400
+
401
+ context "with a subcommand and required options" do
402
+
403
+ given_command "movements" do
404
+ option "--direction", "N|S|E|W", "bearing", required: true
405
+ subcommand "hop", "Hop" do
406
+ def execute
407
+ puts "Hopping #{direction}"
408
+ end
409
+ end
410
+ end
411
+
412
+ it "allows options after the subcommand" do
413
+ command.run(%w[hop --direction south])
414
+ expect(stdout).to eql "Hopping south\n"
415
+ end
416
+
399
417
  end
400
418
 
401
419
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  require "spec_helper"
3
4
 
@@ -60,7 +61,7 @@ describe Clamp::Command do
60
61
  context "with explicit :attribute_name" do
61
62
 
62
63
  before do
63
- command.class.option "--foo", "FOO", "A foo", :attribute_name => :bar
64
+ command.class.option "--foo", "FOO", "A foo", attribute_name: :bar
64
65
  end
65
66
 
66
67
  it "uses the specified attribute_name name to name accessors" do
@@ -95,7 +96,7 @@ describe Clamp::Command do
95
96
  context "with :default value" do
96
97
 
97
98
  before do
98
- command.class.option "--port", "PORT", "port to listen on", :default => 4321
99
+ command.class.option "--port", "PORT", "port to listen on", default: 4321
99
100
  end
100
101
 
101
102
  it "declares default method" do
@@ -112,10 +113,22 @@ describe Clamp::Command do
112
113
 
113
114
  end
114
115
 
116
+ context "without :default value" do
117
+
118
+ before do
119
+ command.class.option "--port", "PORT", "port to listen on"
120
+ end
121
+
122
+ it "does not declare default method" do
123
+ expect(command).to_not respond_to(:default_port)
124
+ end
125
+
126
+ end
127
+
115
128
  context "with :multivalued" do
116
129
 
117
130
  before do
118
- command.class.option "--flavour", "FLAVOUR", "flavour(s)", :multivalued => true, :attribute_name => :flavours
131
+ command.class.option "--flavour", "FLAVOUR", "flavour(s)", multivalued: true, attribute_name: :flavours
119
132
  end
120
133
 
121
134
  it "defaults to empty array" do
@@ -123,20 +136,26 @@ describe Clamp::Command do
123
136
  end
124
137
 
125
138
  it "supports multiple values" do
126
- command.parse(%w(--flavour chocolate --flavour vanilla))
127
- expect(command.flavours).to eql %w(chocolate vanilla)
139
+ command.parse(%w[--flavour chocolate --flavour vanilla])
140
+ expect(command.flavours).to eql %w[chocolate vanilla]
128
141
  end
129
142
 
130
143
  it "generates a single-value appender method" do
131
144
  command.append_to_flavours("mud")
132
145
  command.append_to_flavours("pie")
133
- expect(command.flavours).to eql %w(mud pie)
146
+ expect(command.flavours).to eql %w[mud pie]
134
147
  end
135
148
 
136
149
  it "generates a multi-value setter method" do
137
150
  command.append_to_flavours("replaceme")
138
- command.flavours = %w(mud pie)
139
- expect(command.flavours).to eql %w(mud pie)
151
+ command.flavours = %w[mud pie]
152
+ expect(command.flavours).to eql %w[mud pie]
153
+ end
154
+
155
+ it "does not require a value" do
156
+ expect do
157
+ command.parse([])
158
+ end.not_to raise_error
140
159
  end
141
160
 
142
161
  end
@@ -148,8 +167,8 @@ describe Clamp::Command do
148
167
 
149
168
  before do
150
169
  command.class.option "--port", "PORT", "port to listen on",
151
- :default => 4321,
152
- :environment_variable => "PORT",
170
+ default: 4321,
171
+ environment_variable: "PORT",
153
172
  &:to_i
154
173
  set_env("PORT", environment_value)
155
174
  command.parse(args)
@@ -173,7 +192,7 @@ describe Clamp::Command do
173
192
 
174
193
  context "and a value is specified on the command-line" do
175
194
 
176
- let(:args) { %w(--port 1500) }
195
+ let(:args) { %w[--port 1500] }
177
196
 
178
197
  it "uses command-line value" do
179
198
  expect(command.port).to eql 1500
@@ -198,7 +217,7 @@ describe Clamp::Command do
198
217
  let(:environment_value) { nil }
199
218
 
200
219
  before do
201
- command.class.option "--[no-]enable", :flag, "enable?", :default => false, :environment_variable => "ENABLE"
220
+ command.class.option "--[no-]enable", :flag, "enable?", default: false, environment_variable: "ENABLE"
202
221
  set_env("ENABLE", environment_value)
203
222
  command.parse([])
204
223
  end
@@ -211,7 +230,7 @@ describe Clamp::Command do
211
230
 
212
231
  end
213
232
 
214
- %w(1 yes enable on true).each do |truthy_value|
233
+ %w[1 yes enable on true].each do |truthy_value|
215
234
 
216
235
  context "when environment variable is #{truthy_value.inspect}" do
217
236
 
@@ -225,7 +244,7 @@ describe Clamp::Command do
225
244
 
226
245
  end
227
246
 
228
- %w(0 no disable off false).each do |falsey_value|
247
+ %w[0 no disable off false].each do |falsey_value|
229
248
 
230
249
  context "when environment variable is #{falsey_value.inspect}" do
231
250
 
@@ -244,7 +263,43 @@ describe Clamp::Command do
244
263
  context "with :required" do
245
264
 
246
265
  before do
247
- command.class.option "--port", "PORT", "port to listen on", :required => true
266
+ command.class.option "--port", "PORT", "port to listen on", required: true
267
+ end
268
+
269
+ describe "#help" do
270
+
271
+ it "marks it as required" do
272
+ expect(command.help).to include("port to listen on (required)")
273
+ end
274
+
275
+ end
276
+
277
+ context "when no value is provided" do
278
+
279
+ it "raises a UsageError" do
280
+ expect do
281
+ command.parse([])
282
+ end.to raise_error(Clamp::UsageError)
283
+ end
284
+
285
+ end
286
+
287
+ context "when a value is provided" do
288
+
289
+ it "does not raise an error" do
290
+ expect do
291
+ command.parse(["--port", "12345"])
292
+ end.not_to raise_error
293
+ end
294
+
295
+ end
296
+
297
+ end
298
+
299
+ context "with :required and :multivalued" do
300
+
301
+ before do
302
+ command.class.option "--port", "PORT", "port to listen on", required: true, multivalued: true
248
303
  end
249
304
 
250
305
  context "when no value is provided" do
@@ -295,12 +350,12 @@ describe Clamp::Command do
295
350
  command.class.option ["-f", "--flavour"], "FLAVOUR", "Flavour of the month"
296
351
  command.class.option ["-c", "--color"], "COLOR", "Preferred hue"
297
352
  command.class.option ["--scoops"], "N", "Number of scoops",
298
- :default => 1,
299
- :environment_variable => "DEFAULT_SCOOPS" do |arg|
353
+ default: 1,
354
+ environment_variable: "DEFAULT_SCOOPS" do |arg|
300
355
  Integer(arg)
301
356
  end
302
357
  command.class.option ["-n", "--[no-]nuts"], :flag, "Nuts (or not)\nMay include nuts"
303
- command.class.parameter "[ARG] ...", "extra arguments", :attribute_name => :arguments
358
+ command.class.parameter "[ARG] ...", "extra arguments", attribute_name: :arguments
304
359
  end
305
360
 
306
361
  describe "#parse" do
@@ -309,7 +364,7 @@ describe Clamp::Command do
309
364
 
310
365
  it "raises a UsageError" do
311
366
  expect do
312
- command.parse(%w(--foo bar))
367
+ command.parse(%w[--foo bar])
313
368
  end.to raise_error(Clamp::UsageError)
314
369
  end
315
370
 
@@ -318,7 +373,7 @@ describe Clamp::Command do
318
373
  context "with options" do
319
374
 
320
375
  before do
321
- command.parse(%w(--flavour strawberry --nuts --color blue))
376
+ command.parse(%w[--flavour strawberry --nuts --color blue])
322
377
  end
323
378
 
324
379
  it "maps the option values onto the command object" do
@@ -332,7 +387,7 @@ describe Clamp::Command do
332
387
  context "with short options" do
333
388
 
334
389
  before do
335
- command.parse(%w(-f strawberry -c blue))
390
+ command.parse(%w[-f strawberry -c blue])
336
391
  end
337
392
 
338
393
  it "recognises short options as aliases" do
@@ -345,7 +400,7 @@ describe Clamp::Command do
345
400
  context "with a value appended to a short option" do
346
401
 
347
402
  before do
348
- command.parse(%w(-fstrawberry))
403
+ command.parse(%w[-fstrawberry])
349
404
  end
350
405
 
351
406
  it "works as though the value were separated" do
@@ -357,7 +412,7 @@ describe Clamp::Command do
357
412
  context "with combined short options" do
358
413
 
359
414
  before do
360
- command.parse(%w(-nf strawberry))
415
+ command.parse(%w[-nf strawberry])
361
416
  end
362
417
 
363
418
  it "works as though the options were separate" do
@@ -370,7 +425,7 @@ describe Clamp::Command do
370
425
  context "with option arguments attached using equals sign" do
371
426
 
372
427
  before do
373
- command.parse(%w(--flavour=strawberry --color=blue))
428
+ command.parse(%w[--flavour=strawberry --color=blue])
374
429
  end
375
430
 
376
431
  it "works as though the option arguments were separate" do
@@ -380,11 +435,24 @@ describe Clamp::Command do
380
435
 
381
436
  end
382
437
 
438
+ context "with option arguments that look like options" do
439
+
440
+ before do
441
+ command.parse(%w[--flavour=-dashing- --scoops -1])
442
+ end
443
+
444
+ it "sets the options" do
445
+ expect(command.flavour).to eq("-dashing-")
446
+ expect(command.scoops).to eq(-1)
447
+ end
448
+
449
+ end
450
+
383
451
  context "with option-like things beyond the arguments" do
384
452
 
385
453
  it "treats them as positional arguments" do
386
- command.parse(%w(a b c --flavour strawberry))
387
- expect(command.arguments).to eql %w(a b c --flavour strawberry)
454
+ command.parse(%w[a b c --flavour strawberry])
455
+ expect(command.arguments).to eql %w[a b c --flavour strawberry]
388
456
  end
389
457
 
390
458
  end
@@ -406,8 +474,8 @@ describe Clamp::Command do
406
474
  context "with an option terminator" do
407
475
 
408
476
  it "considers everything after the terminator to be an argument" do
409
- command.parse(%w(--color blue -- --flavour strawberry))
410
- expect(command.arguments).to eql %w(--flavour strawberry)
477
+ command.parse(%w[--color blue -- --flavour strawberry])
478
+ expect(command.arguments).to eql %w[--flavour strawberry]
411
479
  end
412
480
 
413
481
  end
@@ -415,7 +483,7 @@ describe Clamp::Command do
415
483
  context "with --flag" do
416
484
 
417
485
  before do
418
- command.parse(%w(--nuts))
486
+ command.parse(%w[--nuts])
419
487
  end
420
488
 
421
489
  it "sets the flag" do
@@ -428,7 +496,7 @@ describe Clamp::Command do
428
496
 
429
497
  before do
430
498
  command.nuts = true
431
- command.parse(%w(--no-nuts))
499
+ command.parse(%w[--no-nuts])
432
500
  end
433
501
 
434
502
  it "clears the flag" do
@@ -441,7 +509,7 @@ describe Clamp::Command do
441
509
 
442
510
  it "requests help" do
443
511
  expect do
444
- command.parse(%w(--help))
512
+ command.parse(%w[--help])
445
513
  end.to raise_error(Clamp::HelpWanted)
446
514
  end
447
515
 
@@ -451,7 +519,7 @@ describe Clamp::Command do
451
519
 
452
520
  it "requests help" do
453
521
  expect do
454
- command.parse(%w(-h))
522
+ command.parse(%w[-h])
455
523
  end.to raise_error(Clamp::HelpWanted)
456
524
  end
457
525
 
@@ -461,7 +529,7 @@ describe Clamp::Command do
461
529
 
462
530
  it "signals a UsageError" do
463
531
  expect do
464
- command.parse(%w(--scoops reginald))
532
+ command.parse(%w[--scoops reginald])
465
533
  end.to raise_error(Clamp::UsageError, /^option '--scoops': invalid value for Integer/)
466
534
  end
467
535
 
@@ -507,14 +575,14 @@ describe Clamp::Command do
507
575
 
508
576
  it "does not generate implicit help option" do
509
577
  expect do
510
- command.parse(%w(--help))
578
+ command.parse(%w[--help])
511
579
  end.to_not raise_error
512
580
  expect(command.help?).to be true
513
581
  end
514
582
 
515
583
  it "does not recognise -h" do
516
584
  expect do
517
- command.parse(%w(-h))
585
+ command.parse(%w[-h])
518
586
  end.to raise_error(Clamp::UsageError)
519
587
  end
520
588
 
@@ -534,7 +602,7 @@ describe Clamp::Command do
534
602
 
535
603
  it "still recognises --help" do
536
604
  expect do
537
- command.parse(%w(--help))
605
+ command.parse(%w[--help])
538
606
  end.to raise_error(Clamp::HelpWanted)
539
607
  end
540
608
 
@@ -552,7 +620,7 @@ describe Clamp::Command do
552
620
  context "with explicit :attribute_name" do
553
621
 
554
622
  before do
555
- command.class.parameter "FOO", "a foo", :attribute_name => :bar
623
+ command.class.parameter "FOO", "a foo", attribute_name: :bar
556
624
  end
557
625
 
558
626
  it "uses the specified attribute_name name to name accessors" do
@@ -565,7 +633,7 @@ describe Clamp::Command do
565
633
  context "with :default value" do
566
634
 
567
635
  before do
568
- command.class.parameter "[ORIENTATION]", "direction", :default => "west"
636
+ command.class.parameter "[ORIENTATION]", "direction", default: "west"
569
637
  end
570
638
 
571
639
  it "sets the specified default value" do
@@ -607,8 +675,8 @@ describe Clamp::Command do
607
675
  end
608
676
 
609
677
  it "accepts multiple arguments" do
610
- command.parse(%w(X Y Z))
611
- expect(command.file_list).to eql %w(X Y Z)
678
+ command.parse(%w[X Y Z])
679
+ expect(command.file_list).to eql %w[X Y Z]
612
680
  end
613
681
 
614
682
  end
@@ -636,8 +704,8 @@ describe Clamp::Command do
636
704
  context "with :environment_variable" do
637
705
 
638
706
  before do
639
- command.class.parameter "[FILE]", "a file", :environment_variable => "FILE",
640
- :default => "/dev/null"
707
+ command.class.parameter "[FILE]", "a file", environment_variable: "FILE",
708
+ default: "/dev/null"
641
709
  end
642
710
 
643
711
  let(:args) { [] }
@@ -715,7 +783,7 @@ describe Clamp::Command do
715
783
  before do
716
784
  command.class.parameter "X", "x\nxx"
717
785
  command.class.parameter "Y", "y"
718
- command.class.parameter "[Z]", "z", :default => "ZZZ"
786
+ command.class.parameter "[Z]", "z", default: "ZZZ"
719
787
  end
720
788
 
721
789
  describe "#parse" do
@@ -798,6 +866,20 @@ describe Clamp::Command do
798
866
 
799
867
  end
800
868
 
869
+ describe ".execute" do
870
+
871
+ it "provides an alternative way to declare execute method" do
872
+ command_class.class_eval do
873
+ execute do
874
+ puts "using execute DSL"
875
+ end
876
+ end
877
+ command.run([])
878
+ expect(stdout).to eq("using execute DSL\n")
879
+ end
880
+
881
+ end
882
+
801
883
  context "with explicit usage" do
802
884
 
803
885
  given_command("blah") do
@@ -840,12 +922,12 @@ describe Clamp::Command do
840
922
 
841
923
  given_command("punt") do
842
924
 
843
- banner <<-EOF
925
+ banner <<-TEXT
844
926
  Punt is an example command. It doesn't do much, really.
845
927
 
846
928
  The prefix at the beginning of this description should be normalised
847
929
  to two spaces.
848
- EOF
930
+ TEXT
849
931
 
850
932
  end
851
933
 
@@ -869,7 +951,7 @@ describe Clamp::Command do
869
951
  print word_list.inspect
870
952
  end
871
953
  end
872
- @xyz = %w(x y z)
954
+ @xyz = %w[x y z]
873
955
  command.class.run("cmd", @xyz)
874
956
  expect(stdout).to eql @xyz.inspect
875
957
  end
@@ -882,7 +964,7 @@ describe Clamp::Command do
882
964
  print context[:foo]
883
965
  end
884
966
  end
885
- command.class.run("xyz", [], :foo => "bar")
967
+ command.class.run("xyz", [], foo: "bar")
886
968
  expect(stdout).to eql "bar"
887
969
  end
888
970
 
@@ -894,7 +976,7 @@ describe Clamp::Command do
894
976
 
895
977
  command.class.class_eval do
896
978
  def execute
897
- signal_error "Oh crap!", :status => 456
979
+ signal_error "Oh crap!", status: 456
898
980
  end
899
981
  end
900
982