command_mapper 0.2.0 → 0.3.0

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.
@@ -11,16 +11,22 @@ module CommandMapper
11
11
  # The separator String between the key and value.
12
12
  #
13
13
  # @return [String]
14
+ #
15
+ # @api semipublic
14
16
  attr_reader :separator
15
17
 
16
18
  # The key's type.
17
19
  #
18
20
  # @return [Type]
21
+ #
22
+ # @api semipublic
19
23
  attr_reader :key
20
24
 
21
25
  # The value's type.
22
26
  #
23
27
  # @return [Type]
28
+ #
29
+ # @api semipublic
24
30
  attr_reader :value
25
31
 
26
32
  #
@@ -63,6 +69,8 @@ module CommandMapper
63
69
  # Returns true if the value is valid, or `false` and a validation error
64
70
  # message if the value is not compatible.
65
71
  #
72
+ # @api semipublic
73
+ #
66
74
  def validate(value)
67
75
  case value
68
76
  when Hash
@@ -113,6 +121,8 @@ module CommandMapper
113
121
  # @return [String]
114
122
  # The formatted key-value pair.
115
123
  #
124
+ # @api semipublic
125
+ #
116
126
  def format(value)
117
127
  case value
118
128
  when Hash, Array
@@ -36,6 +36,8 @@ module CommandMapper
36
36
  # @return [String]
37
37
  # The formatted key-value list.
38
38
  #
39
+ # @api semipublic
40
+ #
39
41
  def format(value)
40
42
  super(Array(value).map(&@type.method(:format)))
41
43
  end
@@ -11,11 +11,15 @@ module CommandMapper
11
11
  # The seperator character.
12
12
  #
13
13
  # @return [String]
14
+ #
15
+ # @api semipublic
14
16
  attr_reader :separator
15
17
 
16
18
  # The list element type.
17
19
  #
18
20
  # @return [Type]
21
+ #
22
+ # @api semipublic
19
23
  attr_reader :type
20
24
 
21
25
  #
@@ -43,6 +47,8 @@ module CommandMapper
43
47
  #
44
48
  # @return [Boolean]
45
49
  #
50
+ # @api semipublic
51
+ #
46
52
  def allow_empty?
47
53
  @allow_empty
48
54
  end
@@ -57,6 +63,8 @@ module CommandMapper
57
63
  # Returns true if the value is valid, or `false` and a validation error
58
64
  # message if the value is not compatible.
59
65
  #
66
+ # @api semipublic
67
+ #
60
68
  def validate(value)
61
69
  values = Array(value)
62
70
 
@@ -86,6 +94,8 @@ module CommandMapper
86
94
  # @return [String]
87
95
  # The formatted list.
88
96
  #
97
+ # @api semipublic
98
+ #
89
99
  def format(value)
90
100
  Array(value).map(&@type.method(:format)).join(@separator)
91
101
  end
@@ -2,11 +2,16 @@ require 'command_mapper/types/type'
2
2
 
3
3
  module CommandMapper
4
4
  module Types
5
+ #
6
+ # Represents a mapping of Ruby values to other String values.
7
+ #
5
8
  class Map < Type
6
9
 
7
10
  # The map of values to Strings.
8
11
  #
9
12
  # @return [Hash{Object => String}]
13
+ #
14
+ # @api semipublic
10
15
  attr_reader :map
11
16
 
12
17
  #
@@ -47,9 +52,11 @@ module CommandMapper
47
52
  # Returns true if the value is valid, or `false` and a validation error
48
53
  # message if the value is not compatible.
49
54
  #
55
+ # @api semipublic
56
+ #
50
57
  def validate(value)
51
58
  unless (@map.has_key?(value) || @map.has_value?(value))
52
- return [false, "unknown value (#{value.inspect})"]
59
+ return [false, "unknown value (#{value.inspect}) must be #{@map.keys.map(&:inspect).join(', ')}, or #{@map.values.map(&:inspect).join(', ')}"]
53
60
  end
54
61
 
55
62
  return true
@@ -67,6 +74,8 @@ module CommandMapper
67
74
  # @raise [KeyError]
68
75
  # The given value is not a key or value in the map.
69
76
  #
77
+ # @api semipublic
78
+ #
70
79
  def format(value)
71
80
  if @map.has_key?(value)
72
81
  super(@map[value])
@@ -10,6 +10,8 @@ module CommandMapper
10
10
  # The optional range of acceptable numbers.
11
11
  #
12
12
  # @return [Range, nil]
13
+ #
14
+ # @api semipublic
13
15
  attr_reader :range
14
16
 
15
17
  #
@@ -32,6 +34,8 @@ module CommandMapper
32
34
  # Returns true if the value is valid, or `false` and a validation error
33
35
  # message if the value is not compatible.
34
36
  #
37
+ # @api semipublic
38
+ #
35
39
  def validate(value)
36
40
  case value
37
41
  when Integer
@@ -48,7 +52,7 @@ module CommandMapper
48
52
 
49
53
  if @range
50
54
  unless @range.include?(value.to_i)
51
- return [false, "unacceptable value (#{value.inspect})"]
55
+ return [false, "(#{value.inspect}) not within the range of acceptable values (#{range.inspect})"]
52
56
  end
53
57
  end
54
58
 
@@ -64,6 +68,8 @@ module CommandMapper
64
68
  # @return [String]
65
69
  # The formatted numeric value.
66
70
  #
71
+ # @api semipublic
72
+ #
67
73
  def format(value)
68
74
  case value
69
75
  when Integer, String then value.to_s
@@ -2,6 +2,9 @@ require 'command_mapper/types/type'
2
2
 
3
3
  module CommandMapper
4
4
  module Types
5
+ #
6
+ # Represents an arbitrary string value.
7
+ #
5
8
  class Str < Type
6
9
  #
7
10
  # Initializes the value.
@@ -22,6 +25,8 @@ module CommandMapper
22
25
  #
23
26
  # @return [Boolean]
24
27
  #
28
+ # @api semipublic
29
+ #
25
30
  def allow_empty?
26
31
  @allow_empty
27
32
  end
@@ -31,6 +36,8 @@ module CommandMapper
31
36
  #
32
37
  # @return [Boolean]
33
38
  #
39
+ # @api semipublic
40
+ #
34
41
  def allow_blank?
35
42
  @allow_blank
36
43
  end
@@ -42,7 +49,7 @@ module CommandMapper
42
49
  # The given value to validate.
43
50
  #
44
51
  # @return [true, (false, String)]
45
- # Returns true if the valid is considered valid, or false and a
52
+ # Returns true if the value is considered valid, or false and a
46
53
  # validation message if the value is not valid.
47
54
  # * If `nil` is given and a value is required, then `false` will be
48
55
  # returned.
@@ -51,6 +58,8 @@ module CommandMapper
51
58
  # * If an empty value is given and blank values are not allowed, then
52
59
  # `false` will be returned.
53
60
  #
61
+ # @api semipublic
62
+ #
54
63
  def validate(value)
55
64
  case value
56
65
  when nil
@@ -50,13 +50,15 @@ module CommandMapper
50
50
  #
51
51
  # argument :ports, required: true, type: PortRange.new
52
52
  #
53
+ # @api semipublic
54
+ #
53
55
  class Type
54
56
 
55
57
  #
56
58
  # The default `validate` method for all types.
57
59
  #
58
60
  # @param [Object]
59
- # The given value to format.
61
+ # The given value to validate.
60
62
  #
61
63
  # @return [true, (false, String)]
62
64
  # Returns true if the value is valid, or `false` and a validation error
@@ -96,6 +98,8 @@ module CommandMapper
96
98
  # @raise [ArgumentError]
97
99
  # The given type value was not a {Type}, `Hash`, or `nil`,
98
100
  #
101
+ # @api semipublic
102
+ #
99
103
  def self.Type(value)
100
104
  case value
101
105
  when Type then value
@@ -1,6 +1,7 @@
1
1
  require 'command_mapper/types/type'
2
2
  require 'command_mapper/types/str'
3
3
  require 'command_mapper/types/num'
4
+ require 'command_mapper/types/dec'
4
5
  require 'command_mapper/types/hex'
5
6
  require 'command_mapper/types/map'
6
7
  require 'command_mapper/types/enum'
@@ -1,4 +1,4 @@
1
1
  module CommandMapper
2
2
  # Version of command_mapper
3
- VERSION = '0.2.0'
3
+ VERSION = '0.3.0'
4
4
  end
data/spec/commnad_spec.rb CHANGED
@@ -808,7 +808,7 @@ describe CommandMapper::Command do
808
808
  end
809
809
 
810
810
  it "must initialize a new command with the Hash of params and call #run_command" do
811
- if RUBY_VERSION < '3.' || RUBY_ENGINE == 'truffleruby'
811
+ if RUBY_VERSION < '3.'
812
812
  expect(subject).to receive(:new).with({},params).and_return(command_instance)
813
813
  else
814
814
  expect(subject).to receive(:new).with(params).and_return(command_instance)
@@ -846,7 +846,7 @@ describe CommandMapper::Command do
846
846
  end
847
847
 
848
848
  it "must initialize a new command with the Hash of params and call #spawn_command" do
849
- if RUBY_VERSION < '3.' || RUBY_ENGINE == 'truffleruby'
849
+ if RUBY_VERSION < '3.'
850
850
  expect(subject).to receive(:new).with({},params).and_return(command_instance)
851
851
  else
852
852
  expect(subject).to receive(:new).with(params).and_return(command_instance)
@@ -884,7 +884,7 @@ describe CommandMapper::Command do
884
884
  end
885
885
 
886
886
  it "must initialize a new command with the Hash of params and call #capture_command" do
887
- if RUBY_VERSION < '3.' || RUBY_ENGINE == 'truffleruby'
887
+ if RUBY_VERSION < '3.'
888
888
  expect(subject).to receive(:new).with({},params).and_return(command_instance)
889
889
  else
890
890
  expect(subject).to receive(:new).with(params).and_return(command_instance)
@@ -922,7 +922,7 @@ describe CommandMapper::Command do
922
922
  end
923
923
 
924
924
  it "must initialize a new command with the Hash of params and call #popen_command" do
925
- if RUBY_VERSION < '3.' || RUBY_ENGINE == 'truffleruby'
925
+ if RUBY_VERSION < '3.'
926
926
  expect(subject).to receive(:new).with({},params).and_return(command_instance)
927
927
  else
928
928
  expect(subject).to receive(:new).with(params).and_return(command_instance)
@@ -960,7 +960,7 @@ describe CommandMapper::Command do
960
960
  end
961
961
 
962
962
  it "must initialize a new command with the Hash of params and call #sudo_command" do
963
- if RUBY_VERSION < '3.' || RUBY_ENGINE == 'truffleruby'
963
+ if RUBY_VERSION < '3.'
964
964
  expect(subject).to receive(:new).with({},params).and_return(command_instance)
965
965
  else
966
966
  expect(subject).to receive(:new).with(params).and_return(command_instance)
@@ -1163,6 +1163,26 @@ describe CommandMapper::Command do
1163
1163
  }.to raise_error(ArgumentRequired,"argument arg2 is required")
1164
1164
  end
1165
1165
  end
1166
+
1167
+ context "but the command has un-required arguments that repeat" do
1168
+ module TestCommand
1169
+ class CommandWithUnRequiredRepeatingArguments < CommandMapper::Command
1170
+ command "test" do
1171
+ argument :arg1, required: false
1172
+ argument :arg2, required: false, repeats: true
1173
+ argument :arg3, required: false
1174
+ end
1175
+ end
1176
+ end
1177
+
1178
+ let(:command_class) { TestCommand::CommandWithUnRequiredRepeatingArguments }
1179
+
1180
+ subject { command_class.new(arg1: nil, arg2: nil, arg3: nil) }
1181
+
1182
+ it "must omit the un-required repeating arguments that are not set" do
1183
+ expect(subject.command_argv).to eq([subject.class.command_name])
1184
+ end
1185
+ end
1166
1186
  end
1167
1187
 
1168
1188
  context "when the command is initialized with the command_path: keyword" do
@@ -1,8 +1,36 @@
1
1
  require 'spec_helper'
2
2
  require 'command_mapper/option_value'
3
+ require 'command_mapper/types/num'
3
4
  require 'command_mapper/types/list'
4
5
 
5
6
  describe CommandMapper::OptionValue do
7
+ describe "#validate" do
8
+ let(:type) { Types::Num.new }
9
+
10
+ subject { described_class.new(type: type) }
11
+
12
+ context "when the option value is not required" do
13
+ subject do
14
+ described_class.new(type: type, required: false)
15
+ end
16
+
17
+ context "and given a value of true" do
18
+ it "must return true" do
19
+ expect(subject.validate(true)).to be(true)
20
+ end
21
+ end
22
+ end
23
+
24
+ context "otherwise" do
25
+ it "must validate the value using #type.validate" do
26
+ expect(subject.validate('1234')).to be(true)
27
+ expect(subject.validate('foo')).to eq(
28
+ [false, "contains non-numeric characters (\"foo\")"]
29
+ )
30
+ end
31
+ end
32
+ end
33
+
6
34
  describe "#format" do
7
35
  let(:type) { Types::List.new }
8
36
 
@@ -0,0 +1,180 @@
1
+ require 'spec_helper'
2
+ require 'command_mapper/types/dec'
3
+
4
+ describe CommandMapper::Types::Dec do
5
+ describe "#initialize" do
6
+ context "when initialized with no keyword arguments" do
7
+ it "must set #range to nil" do
8
+ expect(subject.range).to be(nil)
9
+ end
10
+ end
11
+
12
+ context "when initialized with range: ..." do
13
+ let(:range) { 1.0..1.5 }
14
+
15
+ subject { described_class.new(range: range) }
16
+
17
+ it "must set #range" do
18
+ expect(subject.range).to eq(range)
19
+ end
20
+ end
21
+ end
22
+
23
+ describe "#validate" do
24
+ context "when given an Integer" do
25
+ let(:value) { 1 }
26
+
27
+ it "must return true" do
28
+ expect(subject.validate(value)).to be(true)
29
+ end
30
+
31
+ context "when initialized with range: ..." do
32
+ let(:range) { 2.0..10.0 }
33
+
34
+ subject { described_class.new(range: range) }
35
+
36
+ context "and the value is within the range of values" do
37
+ let(:value) { 4.0 }
38
+
39
+ it "must return true" do
40
+ expect(subject.validate(value)).to be(true)
41
+ end
42
+ end
43
+
44
+ context "but the value is not within the range of values" do
45
+ let(:value) { 0.0 }
46
+
47
+ it "must return [false, \"(...) not within the range of acceptable values (...)\"]" do
48
+ expect(subject.validate(value)).to eq(
49
+ [false, "(#{value.inspect}) not within the range of acceptable values (#{range.inspect})"]
50
+ )
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ context "when given a String" do
57
+ context "and it contains only digits" do
58
+ let(:value) { "0123456789" }
59
+
60
+ it "must return true" do
61
+ expect(subject.validate(value)).to be(true)
62
+ end
63
+
64
+ context "when initialized with range: ..." do
65
+ let(:range) { 2.0..10.0 }
66
+
67
+ subject { described_class.new(range: range) }
68
+
69
+ context "and the value is within the range of values" do
70
+ let(:value) { '4.0' }
71
+
72
+ it "must return true" do
73
+ expect(subject.validate(value)).to be(true)
74
+ end
75
+ end
76
+
77
+ context "but the value is not within the range of values" do
78
+ let(:value) { '0.0' }
79
+
80
+ it "must return [false, \"(...) not within the range of acceptable values (...)\"]" do
81
+ expect(subject.validate(value)).to eq(
82
+ [false, "(#{value.inspect}) not within the range of acceptable values (#{range.inspect})"]
83
+ )
84
+ end
85
+ end
86
+ end
87
+
88
+ context "but the String contains a newline" do
89
+ let(:value) { "01234.\n56789" }
90
+
91
+ it "must return [false, \"contains non-decimal characters (...)\"]" do
92
+ expect(subject.validate(value)).to eq(
93
+ [false, "contains non-decimal characters (#{value.inspect})"]
94
+ )
95
+ end
96
+ end
97
+ end
98
+
99
+ context "but it contains non-digits" do
100
+ let(:value) { "12.abc34" }
101
+
102
+ it "must return [false, \"contains non-decimal characters (...)\"]" do
103
+ expect(subject.validate(value)).to eq(
104
+ [false, "contains non-decimal characters (#{value.inspect})"]
105
+ )
106
+ end
107
+ end
108
+ end
109
+
110
+ context "when given another type of Object" do
111
+ context "and it defines a #to_f method" do
112
+ let(:value) { 1 }
113
+
114
+ it "must return true" do
115
+ expect(subject.validate(value)).to be(true)
116
+ end
117
+ end
118
+
119
+ context "when initialized with range: ..." do
120
+ let(:range) { 2.0..10.0 }
121
+
122
+ subject { described_class.new(range: range) }
123
+
124
+ context "and the value is within the range of values" do
125
+ let(:value) { 4 }
126
+
127
+ it "must return true" do
128
+ expect(subject.validate(value)).to be(true)
129
+ end
130
+ end
131
+
132
+ context "but the value is not within the range of values" do
133
+ let(:value) { 0 }
134
+
135
+ it "must return [false, \"(...) not within the range of acceptable values (...)\"]" do
136
+ expect(subject.validate(value)).to eq(
137
+ [false, "(#{value.inspect}) not within the range of acceptable values (#{range.inspect})"]
138
+ )
139
+ end
140
+ end
141
+ end
142
+
143
+ context "but it does not define a #to_f method" do
144
+ let(:value) { Object.new }
145
+
146
+ it "must return [false, \"value cannot be converted into a Float\"]" do
147
+ expect(subject.validate(value)).to eq(
148
+ [false, "cannot be converted into a Float (#{value.inspect})"]
149
+ )
150
+ end
151
+ end
152
+ end
153
+ end
154
+
155
+ describe "#format" do
156
+ context "when given a String" do
157
+ let(:value) { "12345.67890" }
158
+
159
+ it "must return the same String" do
160
+ expect(subject.format(value)).to eq(value)
161
+ end
162
+ end
163
+
164
+ context "when given an Intger" do
165
+ let(:value) { 12345.67890 }
166
+
167
+ it "must convert the Integer into a String" do
168
+ expect(subject.format(value)).to eq(value.to_s)
169
+ end
170
+ end
171
+
172
+ context "when given another type of Object" do
173
+ let(:value) { 1 }
174
+
175
+ it "must call #to_i then #to_s" do
176
+ expect(subject.format(value)).to eq(value.to_f.to_s)
177
+ end
178
+ end
179
+ end
180
+ end
@@ -3,19 +3,21 @@ require 'command_mapper/types/input_dir'
3
3
 
4
4
  describe CommandMapper::Types::InputDir do
5
5
  describe "#validate" do
6
- context "when given a valid file path" do
7
- let(:value) { __FILE__ }
6
+ context "when given a valid directory path" do
7
+ let(:value) { __dir__ }
8
8
 
9
- it "must return [false, 'directory does not exist (...)']" do
10
- expect(subject.validate(value)).to eq([false, "directory does not exist (#{value.inspect})"])
9
+ it "must return true" do
10
+ expect(subject.validate(value)).to be(true)
11
11
  end
12
12
  end
13
13
 
14
- context "when given a valid directory path" do
15
- let(:value) { __dir__ }
14
+ context "when given a valid file path" do
15
+ let(:value) { __FILE__ }
16
16
 
17
- it "must return true" do
18
- expect(subject.validate(value)).to eq(true)
17
+ it "must return [false, 'directory does not exist (...)']" do
18
+ expect(subject.validate(value)).to eq(
19
+ [false, "directory does not exist (#{value.inspect})"]
20
+ )
19
21
  end
20
22
  end
21
23
 
@@ -23,7 +25,9 @@ describe CommandMapper::Types::InputDir do
23
25
  let(:value) { "/path/does/not/exist" }
24
26
 
25
27
  it "must return [false, 'path does not exist (...)']" do
26
- expect(subject.validate(value)).to eq([false, "path does not exist (#{value.inspect})"])
28
+ expect(subject.validate(value)).to eq(
29
+ [false, "path does not exist (#{value.inspect})"]
30
+ )
27
31
  end
28
32
  end
29
33
  end
@@ -41,9 +41,9 @@ describe CommandMapper::Types::Map do
41
41
  context "when given a value that is not in the map" do
42
42
  let(:value) { 42 }
43
43
 
44
- it "must return [false, \"unknown value (...)\"]" do
44
+ it "must return [false, \"unknown value (...) must be ..., or ...\"]" do
45
45
  expect(subject.validate(value)).to eq(
46
- [false, "unknown value (#{value.inspect})"]
46
+ [false, "unknown value (#{value.inspect}) must be #{subject.map.keys.map(&:inspect).join(', ')}, or #{subject.map.values.map(&:inspect).join(', ')}"]
47
47
  )
48
48
  end
49
49
  end
@@ -44,9 +44,9 @@ describe CommandMapper::Types::Num do
44
44
  context "but the value is not within the range of values" do
45
45
  let(:value) { 0 }
46
46
 
47
- it "must return [false, \"unacceptable value (...)\"]" do
47
+ it "must return [false, \"(...) not within the range of acceptable values (...)\"]" do
48
48
  expect(subject.validate(value)).to eq(
49
- [false, "unacceptable value (#{value.inspect})"]
49
+ [false, "(#{value.inspect}) not within the range of acceptable values (#{range.inspect})"]
50
50
  )
51
51
  end
52
52
  end
@@ -77,9 +77,9 @@ describe CommandMapper::Types::Num do
77
77
  context "but the value is not within the range of values" do
78
78
  let(:value) { '0' }
79
79
 
80
- it "must return [false, \"unacceptable value (...)\"]" do
80
+ it "must return [false, \"(...) not within the range of acceptable values (...)\"]" do
81
81
  expect(subject.validate(value)).to eq(
82
- [false, "unacceptable value (#{value.inspect})"]
82
+ [false, "(#{value.inspect}) not within the range of acceptable values (#{range.inspect})"]
83
83
  )
84
84
  end
85
85
  end
@@ -132,9 +132,9 @@ describe CommandMapper::Types::Num do
132
132
  context "but the value is not within the range of values" do
133
133
  let(:value) { 0.0 }
134
134
 
135
- it "must return [false, \"unacceptable value (...)\"]" do
135
+ it "must return [false, \"(...) not within the range of acceptable values (...)\"]" do
136
136
  expect(subject.validate(value)).to eq(
137
- [false, "unacceptable value (#{value.inspect})"]
137
+ [false, "(#{value.inspect}) not within the range of acceptable values (#{range.inspect})"]
138
138
  )
139
139
  end
140
140
  end