command_mapper 0.1.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ruby.yml +27 -0
  3. data/.gitignore +10 -0
  4. data/.rspec +1 -0
  5. data/.yardopts +1 -0
  6. data/ChangeLog.md +25 -0
  7. data/Gemfile +15 -0
  8. data/LICENSE.txt +20 -0
  9. data/README.md +369 -0
  10. data/Rakefile +12 -0
  11. data/commnad_mapper.gemspec +61 -0
  12. data/gemspec.yml +23 -0
  13. data/lib/command_mapper/arg.rb +75 -0
  14. data/lib/command_mapper/argument.rb +142 -0
  15. data/lib/command_mapper/command.rb +606 -0
  16. data/lib/command_mapper/exceptions.rb +19 -0
  17. data/lib/command_mapper/option.rb +282 -0
  18. data/lib/command_mapper/option_value.rb +21 -0
  19. data/lib/command_mapper/sudo.rb +73 -0
  20. data/lib/command_mapper/types/enum.rb +35 -0
  21. data/lib/command_mapper/types/hex.rb +82 -0
  22. data/lib/command_mapper/types/input_dir.rb +35 -0
  23. data/lib/command_mapper/types/input_file.rb +35 -0
  24. data/lib/command_mapper/types/input_path.rb +29 -0
  25. data/lib/command_mapper/types/key_value.rb +131 -0
  26. data/lib/command_mapper/types/key_value_list.rb +45 -0
  27. data/lib/command_mapper/types/list.rb +90 -0
  28. data/lib/command_mapper/types/map.rb +64 -0
  29. data/lib/command_mapper/types/num.rb +50 -0
  30. data/lib/command_mapper/types/str.rb +85 -0
  31. data/lib/command_mapper/types/type.rb +102 -0
  32. data/lib/command_mapper/types.rb +6 -0
  33. data/lib/command_mapper/version.rb +4 -0
  34. data/lib/command_mapper.rb +2 -0
  35. data/spec/arg_spec.rb +137 -0
  36. data/spec/argument_spec.rb +513 -0
  37. data/spec/commnad_spec.rb +1175 -0
  38. data/spec/exceptions_spec.rb +14 -0
  39. data/spec/option_spec.rb +882 -0
  40. data/spec/option_value_spec.rb +17 -0
  41. data/spec/spec_helper.rb +6 -0
  42. data/spec/sudo_spec.rb +24 -0
  43. data/spec/types/enum_spec.rb +31 -0
  44. data/spec/types/hex_spec.rb +158 -0
  45. data/spec/types/input_dir_spec.rb +30 -0
  46. data/spec/types/input_file_spec.rb +34 -0
  47. data/spec/types/input_path_spec.rb +32 -0
  48. data/spec/types/key_value_list_spec.rb +100 -0
  49. data/spec/types/key_value_spec.rb +272 -0
  50. data/spec/types/list_spec.rb +143 -0
  51. data/spec/types/map_spec.rb +62 -0
  52. data/spec/types/num_spec.rb +90 -0
  53. data/spec/types/str_spec.rb +232 -0
  54. data/spec/types/type_spec.rb +59 -0
  55. metadata +118 -0
@@ -0,0 +1,882 @@
1
+ require 'spec_helper'
2
+ require 'command_mapper/option'
3
+ require 'command_mapper/types/list'
4
+ require 'command_mapper/types/key_value'
5
+
6
+ describe CommandMapper::Option do
7
+ describe "#initialize" do
8
+ let(:flag) { '--foo' }
9
+
10
+ subject { described_class.new(flag) }
11
+
12
+ it "must set #flag" do
13
+ expect(subject.flag).to eq(flag)
14
+ end
15
+
16
+ context "when a name: keyword argument is given" do
17
+ let(:name) { :bar }
18
+
19
+ subject { described_class.new(flag, name: name) }
20
+
21
+ it "must set #name" do
22
+ expect(subject.name).to eq(name)
23
+ end
24
+ end
25
+
26
+ context "when no name: keyword argument is given" do
27
+ it "must default #name to .infer_name_from_flag" do
28
+ expect(subject.name).to eq(described_class.infer_name_from_flag(flag))
29
+ end
30
+ end
31
+
32
+ it "must default #repeats? to false" do
33
+ expect(subject.repeats?).to be(false)
34
+ end
35
+
36
+ context "when equals: true is given" do
37
+ subject { described_class.new(flag, equals: true) }
38
+
39
+ it "#equals? must return true" do
40
+ expect(subject.equals?).to be(true)
41
+ end
42
+ end
43
+
44
+ context "when given the repeats: true keyword argument" do
45
+ subject { described_class.new(flag, repeats: true) }
46
+
47
+ it "#repeats? must be true" do
48
+ expect(subject.repeats?).to be(true)
49
+ end
50
+ end
51
+
52
+ context "when value: is given" do
53
+ context "and when value: is true" do
54
+ subject { described_class.new(flag, value: true) }
55
+
56
+ it "must initialize #value as an OptionValue" do
57
+ expect(subject.value).to be_kind_of(OptionValue)
58
+ end
59
+
60
+ it "must set #value.required? to true" do
61
+ expect(subject.value.required?).to be(true)
62
+ end
63
+ end
64
+
65
+ context "and when value: is a Hash" do
66
+ let(:value_required) { true }
67
+ let(:value_type) { Types::KeyValue.new }
68
+ let(:value_kwargs) do
69
+ {
70
+ required: value_required,
71
+ type: value_type
72
+ }
73
+ end
74
+
75
+ subject { described_class.new(flag, value: value_kwargs) }
76
+
77
+ it "must initialize #value as an OptionValue" do
78
+ expect(subject.value).to be_kind_of(OptionValue)
79
+ end
80
+
81
+ it "must initialize #value with the value: ... Hash" do
82
+ expect(subject.value.required?).to be(value_required)
83
+ expect(subject.value.type).to eq(value_type)
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ describe ".infer_name_from_flag" do
90
+ subject { described_class.infer_name_from_flag(flag) }
91
+
92
+ context "when given a long flag" do
93
+ let(:name) { "foo" }
94
+ let(:flag) { "--#{name}" }
95
+
96
+ it "must return the flag name as a Symbol" do
97
+ expect(subject).to eq(name.to_sym)
98
+ end
99
+
100
+ context "when the flag contains a '-'" do
101
+ let(:flag) { '--foo-bar' }
102
+
103
+ it "must convert all '-' characters to '_'" do
104
+ expect(subject).to eq(:foo_bar)
105
+ end
106
+ end
107
+
108
+ context "when the flag contains multiple '-'" do
109
+ let(:flag) { '--foo--bar' }
110
+
111
+ it "must replace multiple '-' characters with a single '_'" do
112
+ expect(subject).to eq(:foo_bar)
113
+ end
114
+ end
115
+
116
+ context "when the flag contains multiple '_'" do
117
+ let(:flag) { '--foo__bar' }
118
+
119
+ it "must replace multiple '_' characters with a single '_'" do
120
+ expect(subject).to eq(:foo_bar)
121
+ end
122
+ end
123
+ end
124
+
125
+ context "when given a short flag" do
126
+ context "when the flag length is 1" do
127
+ let(:flag) { '-x' }
128
+
129
+ it "must raise an ArgumentError" do
130
+ expect {
131
+ described_class.infer_name_from_flag(flag)
132
+ }.to raise_error(ArgumentError,"cannot infer a name from short option flag: #{flag.inspect}")
133
+ end
134
+ end
135
+
136
+ context "when the flag length is 2" do
137
+ let(:flag) { '-ip' }
138
+
139
+ it "must return the flag name without the '-'" do
140
+ expect(subject).to eq(:ip)
141
+ end
142
+ end
143
+
144
+ context "when the flag contains uppercase characters" do
145
+ let(:flag) { '-Ip' }
146
+
147
+ it "must convert all uppercase characters to lowercase" do
148
+ expect(subject).to eq(:ip)
149
+ end
150
+ end
151
+
152
+ context "when the flag length is > 2" do
153
+ context "when the flag contains a '-'" do
154
+ let(:flag) { '-foo-bar' }
155
+
156
+ it "must convert all '-' characters to '_'" do
157
+ expect(subject).to eq(:foo_bar)
158
+ end
159
+ end
160
+
161
+ context "when the flag contains multiple '-'" do
162
+ let(:flag) { '-foo--bar' }
163
+
164
+ it "must replace multiple '-' characters with a single '_'" do
165
+ expect(subject).to eq(:foo_bar)
166
+ end
167
+ end
168
+
169
+ context "when the flag contains multiple '_'" do
170
+ let(:flag) { '-foo__bar' }
171
+
172
+ it "must replace multiple '_' characters with a single '_'" do
173
+ expect(subject).to eq(:foo_bar)
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
179
+
180
+ let(:flag) { "--opt" }
181
+ let(:name) { "opt" }
182
+
183
+ let(:repeats) { false }
184
+ let(:accepts_value) { false }
185
+
186
+ let(:value_required) { true }
187
+ let(:value_allows_empty) { false }
188
+ let(:value_allows_blank) { false }
189
+ let(:value_type) do
190
+ {
191
+ allow_empty: value_allows_empty,
192
+ allow_blank: value_allows_blank
193
+ }
194
+ end
195
+ let(:value_kwargs) do
196
+ {
197
+ required: value_required,
198
+ type: value_type
199
+ }
200
+ end
201
+
202
+ subject do
203
+ if accepts_value
204
+ described_class.new(flag, name: name,
205
+ value: value_kwargs,
206
+ repeats: repeats)
207
+ else
208
+ described_class.new(flag, name: name, repeats: repeats)
209
+ end
210
+ end
211
+
212
+ describe "#validate" do
213
+ context "when the option accepts a value" do
214
+ let(:accepts_value) { true }
215
+ let(:value_required) { true }
216
+
217
+ context "when the option can be specified multiple times" do
218
+ let(:repeats) { true }
219
+
220
+ context "and is given an Array" do
221
+ context "and all elements of the Array are Strings" do
222
+ let(:values) { %w[foo bar baz] }
223
+
224
+ it "must return true" do
225
+ expect(subject.validate(values)).to be(true)
226
+ end
227
+ end
228
+
229
+ context "but one of the Array's elements is nil" do
230
+ let(:values) { ["foo", nil, "bar"] }
231
+
232
+ it "must return [false, \"does not accept a nil value\"]" do
233
+ expect(subject.validate(values)).to eq(
234
+ [false, "does not accept a nil value"]
235
+ )
236
+ end
237
+
238
+ context "but #value.required? is false" do
239
+ let(:value_required) { false }
240
+
241
+ it "must return true" do
242
+ expect(subject.validate(values)).to be(true)
243
+ end
244
+ end
245
+ end
246
+
247
+ context "but the Array contains Hashes" do
248
+ let(:values) do
249
+ [{"foo" => 1}, {"bar" => 2 }]
250
+ end
251
+
252
+ it "must return [false, \"cannot convert a Hash into a String (...)\"]" do
253
+ expect(subject.validate(values)).to eq(
254
+ [false, "cannot convert a Hash into a String (#{values[0].inspect})"]
255
+ )
256
+ end
257
+
258
+ context "but #value.type is a Types::KeyValue object" do
259
+ let(:value_type) { Types::KeyValue.new }
260
+
261
+ let(:values) do
262
+ [{"foo" => 1}, {"bar" => 2 }]
263
+ end
264
+
265
+ it "must return true" do
266
+ expect(subject.validate(values)).to be(true)
267
+ end
268
+
269
+ context "but one of the Hashes is empty" do
270
+ let(:values) do
271
+ [{"foo" => 1}, {}]
272
+ end
273
+
274
+ it "must return [false, \"requires at least one value\"]" do
275
+ expect(subject.validate(values)).to eq(
276
+ [false, "cannot be empty"]
277
+ )
278
+ end
279
+ end
280
+ end
281
+ end
282
+
283
+ context "but it's empty" do
284
+ let(:value) { [] }
285
+
286
+ it "must return [false, \"requires at least one value\"]" do
287
+ expect(subject.validate(value)).to eq(
288
+ [false, "requires at least one value"]
289
+ )
290
+ end
291
+ end
292
+ end
293
+
294
+ context "and it's only given one value" do
295
+ context "and it's a String" do
296
+ let(:value) { "foo" }
297
+
298
+ it "must return true" do
299
+ expect(subject.validate(value)).to be(true)
300
+ end
301
+ end
302
+
303
+ context "and it's a Hash" do
304
+ let(:value) do
305
+ {"foo" => "bar"}
306
+ end
307
+
308
+ it "must return [false, \"cannot convert a Hash into a String (...)\"]" do
309
+ expect(subject.validate(value)).to eq(
310
+ [false, "cannot convert a Hash into a String (#{value.inspect})"]
311
+ )
312
+ end
313
+
314
+ context "but #value.type is a Types::KeyValue object" do
315
+ let(:value_type) { Types::KeyValue.new }
316
+
317
+ it "must return true" do
318
+ expect(subject.validate(value)).to be(true)
319
+ end
320
+
321
+ context "but it's empty" do
322
+ let(:value) { {} }
323
+
324
+ it "must return [false, \"cannot be empty\"]" do
325
+ expect(subject.validate(value)).to eq(
326
+ [false, "cannot be empty"]
327
+ )
328
+ end
329
+ end
330
+ end
331
+ end
332
+ end
333
+ end
334
+
335
+ context "when the option can only be specified once" do
336
+ let(:repeats) { false }
337
+
338
+ context "and is given a String" do
339
+ let(:value) { "foo" }
340
+
341
+ it "must return true" do
342
+ expect(subject.validate(value)).to be(true)
343
+ end
344
+ end
345
+
346
+ context "and is given an Array" do
347
+ let(:value) { [1,2,3,4] }
348
+
349
+ it "must return [false, \"cannot convert a Array into a String (...)\"]" do
350
+ expect(subject.validate(value)).to eq(
351
+ [false, "cannot convert a Array into a String (#{value.inspect})"]
352
+ )
353
+ end
354
+
355
+ context "when #value.type is a Types::List object" do
356
+ let(:value_type) { Types::List.new }
357
+
358
+ it "must return true" do
359
+ expect(subject.validate(value)).to be(true)
360
+ end
361
+
362
+ context "but one of the Array elements is nil" do
363
+ let(:value) { [1,2,nil,4] }
364
+
365
+ it "must return [false, \"element cannot be nil\"]" do
366
+ expect(subject.validate(value)).to eq(
367
+ [false, "element cannot be nil"]
368
+ )
369
+ end
370
+ end
371
+
372
+ context "but it's empty" do
373
+ let(:value) { [] }
374
+
375
+ it "must return [false, \"cannot be empty\"]" do
376
+ expect(subject.validate(value)).to eq(
377
+ [false, "cannot be empty"]
378
+ )
379
+ end
380
+ end
381
+ end
382
+ end
383
+
384
+ context "and it's a Hash" do
385
+ let(:value) do
386
+ {"foo" => "bar"}
387
+ end
388
+
389
+ it "must return [false, \"cannot convert a Hash into a String (...)\"]" do
390
+ expect(subject.validate(value)).to eq(
391
+ [false, "cannot convert a Hash into a String (#{value.inspect})"]
392
+ )
393
+ end
394
+
395
+ context "but #value.type is a Types::KeyValue object" do
396
+ let(:value_type) { Types::KeyValue.new }
397
+
398
+ it "must return true" do
399
+ expect(subject.validate(value)).to be(true)
400
+ end
401
+
402
+ context "but it's empty" do
403
+ let(:value) { {} }
404
+
405
+ it "must return [false, \"cannot be empty\"]" do
406
+ expect(subject.validate(value)).to eq(
407
+ [false, "cannot be empty"]
408
+ )
409
+ end
410
+ end
411
+ end
412
+ end
413
+ end
414
+ end
415
+
416
+ context "when the option does not accept a value" do
417
+ let(:allows_value) { false }
418
+
419
+ shared_examples "and a Boolean is given" do
420
+ context "and is given true" do
421
+ let(:value) { true }
422
+
423
+ it "must return true" do
424
+ expect(subject.validate(value)).to be(true)
425
+ end
426
+ end
427
+
428
+ context "and is given false" do
429
+ let(:value) { false }
430
+
431
+ it "must return true" do
432
+ expect(subject.validate(value)).to be(true)
433
+ end
434
+ end
435
+
436
+ context "and is given nil" do
437
+ let(:value) { nil }
438
+
439
+ it "must return true" do
440
+ expect(subject.validate(value)).to be(true)
441
+ end
442
+ end
443
+
444
+ end
445
+
446
+ context "and when the option can be repeated" do
447
+ let(:repeats) { true }
448
+
449
+ context "and is given true" do
450
+ let(:value) { true }
451
+
452
+ it "must return true" do
453
+ expect(subject.validate(value)).to be(true)
454
+ end
455
+ end
456
+
457
+ context "and is given false" do
458
+ let(:value) { false }
459
+
460
+ it "must return true" do
461
+ expect(subject.validate(value)).to be(true)
462
+ end
463
+ end
464
+
465
+ context "and is given nil" do
466
+ let(:value) { nil }
467
+
468
+ it "must return true" do
469
+ expect(subject.validate(value)).to be(true)
470
+ end
471
+ end
472
+
473
+ context "and is given an Integer" do
474
+ let(:value) { 3 }
475
+
476
+ it "must return true" do
477
+ expect(subject.validate(value)).to be(true)
478
+ end
479
+ end
480
+ end
481
+
482
+ context "but the option can only be specified once" do
483
+ let(:repeats) { false }
484
+
485
+ context "is given an Integer" do
486
+ let(:value) { 3 }
487
+
488
+ it "must return false and a validation error message" do
489
+ expect(subject.validate(value)).to eq(
490
+ [false, "only repeating options may accept Integers"]
491
+ )
492
+ end
493
+ end
494
+ end
495
+ end
496
+ end
497
+
498
+ describe "#argv" do
499
+ context "when the option accepts a value" do
500
+ let(:accepts_value) { true }
501
+
502
+ context "when the option can be specified multiple times" do
503
+ let(:repeats) { true }
504
+
505
+ context "and it's given an Array" do
506
+ context "and all elements of the Array are Strings" do
507
+ let(:values) { %w[foo bar baz] }
508
+
509
+ it "must return an argv of the option flags followed by values" do
510
+ expect(subject.argv(values)).to eq(
511
+ [
512
+ flag, values[0],
513
+ flag, values[1],
514
+ flag, values[2]
515
+ ]
516
+ )
517
+ end
518
+
519
+ context "but one of the Array's elements is invalid" do
520
+ let(:value) { ["foo", " ", "baz"] }
521
+
522
+ it do
523
+ expect {
524
+ subject.argv(value)
525
+ }.to raise_error(ValidationError,"option #{name} was given an invalid value (#{value.inspect}): does not allow a blank value (#{value[1].inspect})")
526
+ end
527
+ end
528
+
529
+ context "but one of the Array's elements starts with a '-'" do
530
+ let(:value) { ["foo", "-bar", "baz"] }
531
+
532
+ it do
533
+ expect {
534
+ subject.argv(value)
535
+ }.to raise_error(ValidationError,"option #{name} formatted value (#{value[1].inspect}) cannot start with a '-'")
536
+ end
537
+ end
538
+ end
539
+
540
+ context "but one of the Array's elements is true" do
541
+ let(:values) { ["foo", true, "bar"] }
542
+
543
+ context "but #value.required? is false" do
544
+ let(:value_required) { false }
545
+
546
+ it "must only emit the option's flag for true values" do
547
+ expect(subject.argv(values)).to eq(
548
+ [
549
+ flag, values[0],
550
+ flag,
551
+ flag, values[2]
552
+ ]
553
+ )
554
+ end
555
+ end
556
+ end
557
+
558
+ context "but the Array contains Hashes" do
559
+ let(:values) do
560
+ [{"foo" => 1}, {"bar" => 2 }]
561
+ end
562
+
563
+ it do
564
+ expect {
565
+ subject.argv(values)
566
+ }.to raise_error(ValidationError,"option #{name} was given an invalid value (#{values.inspect}): cannot convert a Hash into a String (#{values[0].inspect})")
567
+ end
568
+
569
+ context "but #value.type is a Types::KeyValue object" do
570
+ let(:value_type) { Types::KeyValue.new }
571
+
572
+ it "must format each value using #value.format" do
573
+ expect(subject.argv(values)).to eq(
574
+ [
575
+ flag, subject.value.format(values[0]),
576
+ flag, subject.value.format(values[1])
577
+ ]
578
+ )
579
+ end
580
+
581
+ context "but one of the Hashes is empty" do
582
+ let(:values) do
583
+ [{"foo" => 1}, {}]
584
+ end
585
+
586
+ it do
587
+ expect {
588
+ subject.argv(values)
589
+ }.to raise_error(ValidationError,"option #{name} was given an invalid value (#{values.inspect}): cannot be empty")
590
+ end
591
+ end
592
+
593
+ context "but one of the Hash's keys starts with a '-'" do
594
+ let(:value) { [{"foo" => 1}, {"-bar" => 2 }] }
595
+
596
+ it do
597
+ expect {
598
+ subject.argv(value)
599
+ }.to raise_error(ValidationError,"option #{name} formatted value (\"-bar=2\") cannot start with a '-'")
600
+ end
601
+ end
602
+ end
603
+ end
604
+
605
+ context "but it's empty" do
606
+ let(:value) { [] }
607
+
608
+ it do
609
+ expect {
610
+ subject.argv(value)
611
+ }.to raise_error(ValidationError,"option #{name} was given an invalid value (#{value.inspect}): requires at least one value")
612
+ end
613
+ end
614
+ end
615
+
616
+ context "and it's only given one value" do
617
+ context "and it's a String" do
618
+ let(:value) { "foo" }
619
+
620
+ it "must return an argv only containing the option flag and value" do
621
+ expect(subject.argv(value)).to eq([flag, value])
622
+ end
623
+
624
+ context "but it's invalid" do
625
+ let(:value) { " " }
626
+
627
+ it do
628
+ expect {
629
+ subject.argv(value)
630
+ }.to raise_error(ValidationError,"option #{name} was given an invalid value (#{value.inspect}): does not allow a blank value (#{value.inspect})")
631
+ end
632
+ end
633
+
634
+ context "but it starts with a '-'" do
635
+ let(:value) { "-foo" }
636
+
637
+ it do
638
+ expect {
639
+ subject.argv(value)
640
+ }.to raise_error(ValidationError,"option #{name} formatted value (#{value.inspect}) cannot start with a '-'")
641
+ end
642
+ end
643
+ end
644
+
645
+ context "and it's a Hash" do
646
+ let(:value) do
647
+ {"foo" => "bar"}
648
+ end
649
+
650
+ context "but #value.type is a Types::KeyValue object" do
651
+ let(:value_type) { Types::KeyValue.new }
652
+
653
+ it "must format the value using #value.format" do
654
+ expect(subject.argv(value)).to eq(
655
+ [flag, subject.value.format(value)]
656
+ )
657
+ end
658
+
659
+ context "but it's empty" do
660
+ let(:value) { {} }
661
+
662
+ it do
663
+ expect {
664
+ subject.argv(value)
665
+ }.to raise_error(ValidationError,"option #{name} was given an invalid value (#{value.inspect}): cannot be empty")
666
+ end
667
+ end
668
+
669
+ context "but the key starts with a '-'" do
670
+ let(:value) do
671
+ {"-foo" => "bar"}
672
+ end
673
+
674
+ it do
675
+ expect {
676
+ subject.argv(value)
677
+ }.to raise_error(ValidationError,"option #{name} formatted value (\"-foo=bar\") cannot start with a '-'")
678
+ end
679
+ end
680
+ end
681
+ end
682
+ end
683
+ end
684
+
685
+ context "when the option can only be specified once" do
686
+ let(:repeats) { false }
687
+
688
+ context "and it's true" do
689
+ let(:value) { true }
690
+
691
+ context "but #value.required? is false" do
692
+ let(:value_required) { false }
693
+
694
+ it "must only emit the option's flag for true values" do
695
+ expect(subject.argv(value)).to eq([flag])
696
+ end
697
+ end
698
+ end
699
+
700
+ context "and it's a String" do
701
+ let(:value) { "foo" }
702
+
703
+ it "must return an argv only containing the option flag and value" do
704
+ expect(subject.argv(value)).to eq([flag, value])
705
+ end
706
+
707
+ context "but it's invalid" do
708
+ let(:value) { " " }
709
+
710
+ it do
711
+ expect {
712
+ subject.argv(value)
713
+ }.to raise_error(ValidationError,"option #{name} was given an invalid value (#{value.inspect}): does not allow a blank value (#{value.inspect})")
714
+ end
715
+ end
716
+
717
+ context "but it starts with a '-'" do
718
+ let(:value) { "-foo" }
719
+
720
+ it do
721
+ expect {
722
+ subject.argv(value)
723
+ }.to raise_error(ValidationError,"option #{name} formatted value (#{value.inspect}) cannot start with a '-'")
724
+ end
725
+ end
726
+ end
727
+
728
+ context "and it's an Array" do
729
+ let(:value) { %w[foo bar baz] }
730
+
731
+ it do
732
+ expect {
733
+ subject.argv(value)
734
+ }.to raise_error(ValidationError,"option #{name} was given an invalid value (#{value.inspect}): cannot convert a Array into a String (#{value.inspect})")
735
+ end
736
+
737
+ context "but #value.type is a Types::List object" do
738
+ let(:value_type) { Types::List.new }
739
+
740
+ it "must format the value using #value.format" do
741
+ expect(subject.argv(value)).to eq(
742
+ [flag, subject.value.format(value)]
743
+ )
744
+ end
745
+
746
+ context "but one of the Array's elements is nil" do
747
+ let(:value) { ["foo", nil, "baz"] }
748
+
749
+ it do
750
+ expect {
751
+ subject.argv(value)
752
+ }.to raise_error(ValidationError,"option #{name} was given an invalid value (#{value.inspect}): element cannot be nil")
753
+ end
754
+ end
755
+
756
+ context "but it's empty" do
757
+ let(:value) { [] }
758
+
759
+ it do
760
+ expect {
761
+ subject.argv(value)
762
+ }.to raise_error(ValidationError,"option #{name} was given an invalid value (#{value.inspect}): cannot be empty")
763
+ end
764
+ end
765
+
766
+ context "but the first element starts with a '-'" do
767
+ let(:value) { ["-foo", "bar", "baz"] }
768
+
769
+ it do
770
+ expect {
771
+ subject.argv(value)
772
+ }.to raise_error(ValidationError,"option #{name} formatted value (#{value.join(',').inspect}) cannot start with a '-'")
773
+ end
774
+ end
775
+ end
776
+ end
777
+
778
+ context "and it's a Hash" do
779
+ let(:value) do
780
+ {"foo" => "bar"}
781
+ end
782
+
783
+ it do
784
+ expect {
785
+ subject.argv(value)
786
+ }.to raise_error(ValidationError,"option #{name} was given an invalid value (#{value.inspect}): cannot convert a Hash into a String (#{value.inspect})")
787
+ end
788
+
789
+ context "but #value.type is a Types::KeyValue object" do
790
+ let(:value_type) { Types::KeyValue.new }
791
+
792
+ it "must format the value using #value.format" do
793
+ expect(subject.argv(value)).to eq(
794
+ [flag, subject.value.format(value)]
795
+ )
796
+ end
797
+
798
+ context "but it's empty" do
799
+ let(:value) { {} }
800
+
801
+ it do
802
+ expect {
803
+ subject.argv(value)
804
+ }.to raise_error(ValidationError,"option #{name} was given an invalid value (#{value.inspect}): cannot be empty")
805
+ end
806
+ end
807
+
808
+ context "but the first key starts with a '-'" do
809
+ let(:value) do
810
+ {"-foo" => "bar"}
811
+ end
812
+
813
+ it do
814
+ expect {
815
+ subject.argv(value)
816
+ }.to raise_error(ValidationError,"option #{name} formatted value (\"-foo=bar\") cannot start with a '-'")
817
+ end
818
+ end
819
+ end
820
+ end
821
+ end
822
+ end
823
+
824
+ context "when the option does not accept a value" do
825
+ let(:accepts_value) { false }
826
+
827
+ context "and is given true" do
828
+ it "must return an argv only containing the option's flag" do
829
+ expect(subject.argv(true)).to eq([flag])
830
+ end
831
+ end
832
+
833
+ context "and is given false" do
834
+ it "must return an empty argv" do
835
+ expect(subject.argv(false)).to eq([])
836
+ end
837
+ end
838
+
839
+ context "and is given nil" do
840
+ it "must return an empty argv" do
841
+ expect(subject.argv(nil)).to eq([])
842
+ end
843
+ end
844
+
845
+ context "and it's given a non-Boolean value" do
846
+ let(:value) { "foo" }
847
+ let(:message) { "only accepts true, false, or nil" }
848
+
849
+ it do
850
+ expect {
851
+ subject.argv(value)
852
+ }.to raise_error(ValidationError,"option #{name} was given an invalid value (#{value.inspect}): #{message}")
853
+ end
854
+ end
855
+
856
+ context "and when the option can be specified multiple times" do
857
+ let(:repeats) { true }
858
+
859
+ context "and is given an Integer" do
860
+ let(:value) { 3 }
861
+
862
+ it "must return an argv containing multiple instances of the flag" do
863
+ expect(subject.argv(value)).to eq([flag] * value)
864
+ end
865
+ end
866
+ end
867
+ end
868
+
869
+ context "when given an argv array and a value" do
870
+ let(:accepts_value) { true }
871
+ let(:value) { "foo" }
872
+
873
+ let(:argv) { [] }
874
+
875
+ before { subject.argv(argv,value) }
876
+
877
+ it "must concat the args to the argv array" do
878
+ expect(argv).to eq([flag, value])
879
+ end
880
+ end
881
+ end
882
+ end