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,272 @@
1
+ require 'spec_helper'
2
+ require 'command_mapper/types/key_value'
3
+ require 'command_mapper/types/num'
4
+ require 'command_mapper/types/list'
5
+
6
+ describe CommandMapper::Types::KeyValue do
7
+ describe "#initialize" do
8
+ it "must default #separator to '='" do
9
+ expect(subject.separator).to eq('=')
10
+ end
11
+
12
+ it "must initialize #key" do
13
+ expect(subject.key).to be_kind_of(CommandMapper::Types::Str)
14
+ end
15
+
16
+ it "must require a non-empty 'key' value by default" do
17
+ expect(subject.key.allow_empty?).to be(false)
18
+ end
19
+
20
+ it "must initialize #value" do
21
+ expect(subject.value).to be_kind_of(CommandMapper::Types::Str)
22
+ end
23
+
24
+ it "must require a non-empty 'value' value by default" do
25
+ expect(subject.value.allow_empty?).to be(false)
26
+ end
27
+
28
+ context "when given the separator: keyword" do
29
+ let(:separator) { ':' }
30
+
31
+ subject { described_class.new(separator: separator) }
32
+
33
+ it "must set #separator" do
34
+ expect(subject.separator).to eq(separator)
35
+ end
36
+ end
37
+
38
+ context "when given key: nil" do
39
+ it do
40
+ expect {
41
+ described_class.new(key: nil)
42
+ }.to raise_error(ArgumentError,"key: keyword cannot be nil")
43
+ end
44
+ end
45
+
46
+ context "when given value: nil" do
47
+ it do
48
+ expect {
49
+ described_class.new(value: nil)
50
+ }.to raise_error(ArgumentError,"value: keyword cannot be nil")
51
+ end
52
+ end
53
+ end
54
+
55
+ describe "#validate" do
56
+ context "when given a Hash" do
57
+ context "but it's empty" do
58
+ let(:hash) { {} }
59
+
60
+ it "must return [false, \"cannot be empty\"]" do
61
+ expect(subject.validate(hash)).to eq(
62
+ [false, "cannot be empty"]
63
+ )
64
+ end
65
+ end
66
+
67
+ context "and the Hash contains only one key:value pair" do
68
+ let(:key) { "foo" }
69
+ let(:value) { "bar" }
70
+ let(:hash) { {key => value} }
71
+
72
+ it "must return true" do
73
+ expect(subject.validate(hash)).to be(true)
74
+ end
75
+
76
+ context "when #key is a custom Types::Type object" do
77
+ let(:key_type) { Types::Num.new }
78
+ let(:key) { "foo" }
79
+
80
+ subject { described_class.new(key: key_type) }
81
+
82
+ it "must validate the key value using #key.validate" do
83
+ expect(subject.validate(hash)).to eq(
84
+ [false, "key contains non-numeric characters (#{key.inspect})"]
85
+ )
86
+ end
87
+ end
88
+
89
+ context "when #value is a custom Types::Type object" do
90
+ let(:value_type) { Types::Num.new }
91
+ let(:value) { "foo" }
92
+
93
+ subject { described_class.new(value: value_type) }
94
+
95
+ it "must validate the key value using #key.validate" do
96
+ expect(subject.validate(hash)).to eq(
97
+ [false, "value contains non-numeric characters (#{value.inspect})"]
98
+ )
99
+ end
100
+ end
101
+ end
102
+
103
+ context "but the Hash contains more than one pair" do
104
+ let(:hash) { {"foo" => "bar", "baz" => "qux"} }
105
+
106
+ it "must return [false, \"cannot contain multiple key:value pairs (...)\"]" do
107
+ expect(subject.validate(hash)).to eq(
108
+ [false, "cannot contain multiple key:value pairs (#{hash.inspect})"]
109
+ )
110
+ end
111
+ end
112
+ end
113
+
114
+ context "when given an Array" do
115
+ context "but the Array is empty" do
116
+ let(:array) { [] }
117
+
118
+ it "must return [false, \"must contain two elements (...)\"]" do
119
+ expect(subject.validate(array)).to eq(
120
+ [false, "must contain two elements (#{array.inspect})"]
121
+ )
122
+ end
123
+ end
124
+
125
+ context "and the Array contains only one elemnet" do
126
+ let(:array) { ["foo"] }
127
+
128
+ it "must return true" do
129
+ expect(subject.validate(array)).to eq(
130
+ [false, "must contain two elements (#{array.inspect})"]
131
+ )
132
+ end
133
+ end
134
+
135
+ context "and the Array contains only two elements" do
136
+ let(:key) { "foo" }
137
+ let(:value) { "bar" }
138
+ let(:array) { [key, value] }
139
+
140
+ it "must return true" do
141
+ expect(subject.validate(array)).to be(true)
142
+ end
143
+
144
+ context "when #key is a custom Types::Type object" do
145
+ let(:key_type) { Types::Num.new }
146
+ let(:key) { "foo" }
147
+
148
+ subject { described_class.new(key: key_type) }
149
+
150
+ it "must validate the key value using #key.validate" do
151
+ expect(subject.validate(array)).to eq(
152
+ [false, "key contains non-numeric characters (#{key.inspect})"]
153
+ )
154
+ end
155
+ end
156
+
157
+ context "when #value is a custom Types::Type object" do
158
+ let(:value_type) { Types::Num.new }
159
+ let(:value) { "foo" }
160
+
161
+ subject { described_class.new(value: value_type) }
162
+
163
+ it "must validate the key value using #key.validate" do
164
+ expect(subject.validate(array)).to eq(
165
+ [false, "value contains non-numeric characters (#{value.inspect})"]
166
+ )
167
+ end
168
+ end
169
+ end
170
+
171
+ context "but the Array contains more than two elements" do
172
+ let(:array) { ["foo", "bar", "baz"] }
173
+
174
+ it "must return [false, \"cannot contain more than two elements (...)\"]" do
175
+ expect(subject.validate(array)).to eq(
176
+ [false, "cannot contain more than two elements (#{array.inspect})"]
177
+ )
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ describe "#format" do
184
+ context "when given a Hash" do
185
+ let(:key) { "foo" }
186
+ let(:value) { "bar" }
187
+ let(:hash) { {key => value} }
188
+
189
+ it "must format the hash'es key and value into a key=value pair" do
190
+ expect(subject.format(hash)).to eq("#{key}=#{value}")
191
+ end
192
+
193
+ context "when initialized with a custom key: keyword argument" do
194
+ subject { described_class.new(key: Types::Num.new) }
195
+
196
+ let(:key) { 42 }
197
+
198
+ it "must format the key using #key.format" do
199
+ expect(subject.format(hash)).to eq("#{key}=#{value}")
200
+ end
201
+ end
202
+
203
+ context "when initialized with a custom key: keyword argument" do
204
+ subject { described_class.new(value: Types::List.new) }
205
+
206
+ let(:value) { %w[bar baz] }
207
+
208
+ it "must format the value using #value.format" do
209
+ expect(subject.format(hash)).to eq("#{key}=#{value.join(',')}")
210
+ end
211
+ end
212
+
213
+ context "when initialized with a custom separator" do
214
+ let(:separator) { ':' }
215
+
216
+ subject { described_class.new(separator: separator) }
217
+
218
+ it "must use the custom separator" do
219
+ expect(subject.format(hash)).to eq("#{key}#{separator}#{value}")
220
+ end
221
+ end
222
+ end
223
+
224
+ context "when given an Array" do
225
+ let(:key) { "foo" }
226
+ let(:value) { "bar" }
227
+ let(:array) { [key, value] }
228
+
229
+ it "must format the array's first and second elements into a key=value pair" do
230
+ expect(subject.format(array)).to eq("#{key}=#{value}")
231
+ end
232
+
233
+ context "when initialized with a custom key: keyword argument" do
234
+ subject { described_class.new(key: Types::Num.new) }
235
+
236
+ let(:key) { 42 }
237
+
238
+ it "must format the key using #key.format" do
239
+ expect(subject.format(array)).to eq("#{key}=#{value}")
240
+ end
241
+ end
242
+
243
+ context "when initialized with a custom key: keyword argument" do
244
+ subject { described_class.new(value: Types::List.new) }
245
+
246
+ let(:value) { %w[bar baz] }
247
+
248
+ it "must format the value using #value.format" do
249
+ expect(subject.format(array)).to eq("#{key}=#{value.join(',')}")
250
+ end
251
+ end
252
+
253
+ context "when initialized with a custom separator" do
254
+ let(:separator) { ':' }
255
+
256
+ subject { described_class.new(separator: separator) }
257
+
258
+ it "must use the custom separator" do
259
+ expect(subject.format(array)).to eq("#{key}#{separator}#{value}")
260
+ end
261
+ end
262
+ end
263
+
264
+ context "when another kind of Object is given" do
265
+ let(:value) { 42 }
266
+
267
+ it "must return the String version of that object" do
268
+ expect(subject.format(value)).to eq(value.to_s)
269
+ end
270
+ end
271
+ end
272
+ end
@@ -0,0 +1,143 @@
1
+ require 'spec_helper'
2
+ require 'command_mapper/types/list'
3
+ require 'command_mapper/types/num'
4
+
5
+ describe CommandMapper::Types::List do
6
+ describe "#initialize" do
7
+ it "must default #separator to ','" do
8
+ expect(subject.separator).to eq(',')
9
+ end
10
+
11
+ context "when given the separator: keyword" do
12
+ let(:separator) { ':' }
13
+
14
+ subject { described_class.new(separator: separator) }
15
+
16
+ it "must set #separator" do
17
+ expect(subject.separator).to eq(separator)
18
+ end
19
+ end
20
+
21
+ context "when given type: keyword argument" do
22
+ context "and it's a Types::Type object" do
23
+ let(:type) { Types::Num.new }
24
+
25
+ subject { described_class.new(type: type) }
26
+
27
+ it "must set a custom #type" do
28
+ expect(subject.type).to eq(type)
29
+ end
30
+ end
31
+
32
+ context "but it's nil" do
33
+ it do
34
+ expect {
35
+ described_class.new(type: nil)
36
+ }.to raise_error(ArgumentError,"type: keyword cannot be nil")
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ describe "#validate" do
43
+ context "when given a single value" do
44
+ let(:value) { "foo" }
45
+
46
+ it "must return true" do
47
+ expect(subject.validate(value)).to be(true)
48
+ end
49
+
50
+ context "but the value is nil" do
51
+ let(:value) { nil }
52
+
53
+ it "must return [false, \"cannot be empty\"]" do
54
+ expect(subject.validate(value)).to eq(
55
+ [false, "cannot be empty"]
56
+ )
57
+ end
58
+
59
+ context "when #allow_empty? is true" do
60
+ subject { described_class.new(allow_empty: true) }
61
+
62
+ it "must return true" do
63
+ expect(subject.validate(value)).to be(true)
64
+ end
65
+ end
66
+ end
67
+
68
+ context "and the value is invalid" do
69
+ let(:value) { "" }
70
+
71
+ it "must return the validation error from #type.validate" do
72
+ expect(subject.validate(value)).to eq(
73
+ [false, "element does not allow an empty value"]
74
+ )
75
+ end
76
+ end
77
+ end
78
+
79
+ context "when given multiple values" do
80
+ let(:values) { %w[foo bar baz] }
81
+
82
+ it "must return true" do
83
+ expect(subject.validate(values)).to be(true)
84
+ end
85
+
86
+ context "but the value is []" do
87
+ let(:value) { [] }
88
+
89
+ it "must return [false, \"cannot be empty\"]" do
90
+ expect(subject.validate(value)).to eq(
91
+ [false, "cannot be empty"]
92
+ )
93
+ end
94
+
95
+ context "when #allow_empty? is true" do
96
+ subject { described_class.new(allow_empty: true) }
97
+
98
+ it "must return true" do
99
+ expect(subject.validate(value)).to be(true)
100
+ end
101
+ end
102
+ end
103
+
104
+ context "but one of the values is invalid" do
105
+ let(:values) { ["foo", nil, "bar"] }
106
+
107
+ it "must return the validation error from #type.validate" do
108
+ expect(subject.validate(values)).to eq(
109
+ [false, "element cannot be nil"]
110
+ )
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ describe "#format" do
117
+ context "when given a single value" do
118
+ let(:value) { "foo" }
119
+
120
+ it "must return the String version of that value" do
121
+ expect(subject.format(value)).to eq(value.to_s)
122
+ end
123
+ end
124
+
125
+ context "when given multiple values" do
126
+ let(:values) { %w[foo bar baz] }
127
+
128
+ it "must join the values with ','" do
129
+ expect(subject.format(values)).to eq(values.join(','))
130
+ end
131
+
132
+ context "when initialized with a custom separator" do
133
+ let(:separator) { ':' }
134
+
135
+ subject { described_class.new(separator: separator) }
136
+
137
+ it "must join the values with #separator" do
138
+ expect(subject.format(values)).to eq(values.join(subject.separator))
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+ require 'command_mapper/types/map'
3
+
4
+ describe CommandMapper::Types::Map do
5
+ let(:map) { {1 => 'one', 2 => 'two'} }
6
+
7
+ subject { described_class.new(map) }
8
+
9
+ describe "#initialize" do
10
+ it "must initialize #map" do
11
+ expect(subject.map).to eq(map)
12
+ end
13
+ end
14
+
15
+ describe ".[]" do
16
+ subject { described_class[map] }
17
+
18
+ it "must create a new Map with the given values" do
19
+ expect(subject).to be_kind_of(described_class)
20
+ expect(subject.map).to eq(map)
21
+ end
22
+ end
23
+
24
+ describe "#validate" do
25
+ context "when given a value that's in the map" do
26
+ let(:value) { 2 }
27
+
28
+ it "must return true" do
29
+ expect(subject.validate(value)).to be(true)
30
+ end
31
+ end
32
+ context "when given a value that is not in the map" do
33
+ let(:value) { 42 }
34
+
35
+ it "must return [false, \"unknown value (...)\"]" do
36
+ expect(subject.validate(value)).to eq(
37
+ [false, "unknown value (#{value.inspect})"]
38
+ )
39
+ end
40
+ end
41
+ end
42
+
43
+ describe "#format" do
44
+ context "when given a value that's in the map" do
45
+ let(:value) { 2 }
46
+
47
+ it "must return the corresponding mapped value" do
48
+ expect(subject.format(value)).to eq(map[value])
49
+ end
50
+ end
51
+
52
+ context "when given a value that is not in the map" do
53
+ let(:value) { 42 }
54
+
55
+ it "must return the String version of the value" do
56
+ expect {
57
+ subject.format(value)
58
+ }.to raise_error(KeyError)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,90 @@
1
+ require 'spec_helper'
2
+ require 'command_mapper/types/num'
3
+
4
+ describe CommandMapper::Types::Num do
5
+ describe "#validate" do
6
+ context "when given an Integer" do
7
+ let(:value) { 1 }
8
+
9
+ it "must return true" do
10
+ expect(subject.validate(value)).to be(true)
11
+ end
12
+ end
13
+
14
+ context "when given a String" do
15
+ context "and it contains only digits" do
16
+ let(:value) { "0123456789" }
17
+
18
+ it "must return true" do
19
+ expect(subject.validate(value)).to be(true)
20
+ end
21
+
22
+ context "and the String contains a newline" do
23
+ let(:value) { "01234\n56789" }
24
+
25
+ it "must return [false, \"contains non-numeric characters (...)\"]" do
26
+ expect(subject.validate(value)).to eq(
27
+ [false, "contains non-numeric characters (#{value.inspect})"]
28
+ )
29
+ end
30
+ end
31
+ end
32
+
33
+ context "and it contains non-digits" do
34
+ let(:value) { "12abc34" }
35
+
36
+ it "must return [false, \"contains non-numeric characters (...)\"]" do
37
+ expect(subject.validate(value)).to eq(
38
+ [false, "contains non-numeric characters (#{value.inspect})"]
39
+ )
40
+ end
41
+ end
42
+ end
43
+
44
+ context "when given another type of Object" do
45
+ context "but it defines a #to_i method" do
46
+ let(:value) { 1.0 }
47
+
48
+ it "must return true" do
49
+ expect(subject.validate(value)).to be(true)
50
+ end
51
+ end
52
+
53
+ context "but it does not define a #to_i method" do
54
+ let(:value) { Object.new }
55
+
56
+ it "must return [false, \"value cannot be converted into an Integer\"]" do
57
+ expect(subject.validate(value)).to eq(
58
+ [false, "cannot be converted into an Integer (#{value.inspect})"]
59
+ )
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ describe "#format" do
66
+ context "when given a String" do
67
+ let(:value) { "1234567890" }
68
+
69
+ it "must return the same String" do
70
+ expect(subject.format(value)).to eq(value)
71
+ end
72
+ end
73
+
74
+ context "when given an Intger" do
75
+ let(:value) { 1234567890 }
76
+
77
+ it "must convert the Integer into a String" do
78
+ expect(subject.format(value)).to eq(value.to_s)
79
+ end
80
+ end
81
+
82
+ context "when given another type of Object" do
83
+ let(:value) { 1.0 }
84
+
85
+ it "must call #to_i then #to_s" do
86
+ expect(subject.format(value)).to eq(value.to_i.to_s)
87
+ end
88
+ end
89
+ end
90
+ end