command_mapper 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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