command_mapper 0.1.0.pre1

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 (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