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.
- checksums.yaml +7 -0
- data/.github/workflows/ruby.yml +27 -0
- data/.gitignore +10 -0
- data/.rspec +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +25 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +20 -0
- data/README.md +369 -0
- data/Rakefile +12 -0
- data/commnad_mapper.gemspec +61 -0
- data/gemspec.yml +23 -0
- data/lib/command_mapper/arg.rb +75 -0
- data/lib/command_mapper/argument.rb +142 -0
- data/lib/command_mapper/command.rb +606 -0
- data/lib/command_mapper/exceptions.rb +19 -0
- data/lib/command_mapper/option.rb +282 -0
- data/lib/command_mapper/option_value.rb +21 -0
- data/lib/command_mapper/sudo.rb +73 -0
- data/lib/command_mapper/types/enum.rb +35 -0
- data/lib/command_mapper/types/hex.rb +82 -0
- data/lib/command_mapper/types/input_dir.rb +35 -0
- data/lib/command_mapper/types/input_file.rb +35 -0
- data/lib/command_mapper/types/input_path.rb +29 -0
- data/lib/command_mapper/types/key_value.rb +131 -0
- data/lib/command_mapper/types/key_value_list.rb +45 -0
- data/lib/command_mapper/types/list.rb +90 -0
- data/lib/command_mapper/types/map.rb +64 -0
- data/lib/command_mapper/types/num.rb +50 -0
- data/lib/command_mapper/types/str.rb +85 -0
- data/lib/command_mapper/types/type.rb +102 -0
- data/lib/command_mapper/types.rb +6 -0
- data/lib/command_mapper/version.rb +4 -0
- data/lib/command_mapper.rb +2 -0
- data/spec/arg_spec.rb +137 -0
- data/spec/argument_spec.rb +513 -0
- data/spec/commnad_spec.rb +1175 -0
- data/spec/exceptions_spec.rb +14 -0
- data/spec/option_spec.rb +882 -0
- data/spec/option_value_spec.rb +17 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/sudo_spec.rb +24 -0
- data/spec/types/enum_spec.rb +31 -0
- data/spec/types/hex_spec.rb +158 -0
- data/spec/types/input_dir_spec.rb +30 -0
- data/spec/types/input_file_spec.rb +34 -0
- data/spec/types/input_path_spec.rb +32 -0
- data/spec/types/key_value_list_spec.rb +100 -0
- data/spec/types/key_value_spec.rb +272 -0
- data/spec/types/list_spec.rb +143 -0
- data/spec/types/map_spec.rb +62 -0
- data/spec/types/num_spec.rb +90 -0
- data/spec/types/str_spec.rb +232 -0
- data/spec/types/type_spec.rb +59 -0
- metadata +118 -0
data/spec/option_spec.rb
ADDED
@@ -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
|