command_mapper 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7f40de593c4e0124a5c3075b9e89ec08e38fe7451af1ede8fc62030b89b694ec
4
- data.tar.gz: '0392b215a5dff4c4b2bffd34ec7df72fece82adbe7d260a975bd909edb9f962d'
3
+ metadata.gz: abc51ed8c1f7edf0484053eb62854a56b13dce9d5e3ea7b3373799c4b7a1d4fb
4
+ data.tar.gz: 0e7945e79e57cb99a4448b4eafef1237e9cc5e06d4523e13efd68b2767106a2f
5
5
  SHA512:
6
- metadata.gz: f5c09b88caea6446ac20238bbba137accd62552c413e5fa67d9dce9a62f2e3f027624c6a951ce59fc6c720f2683e80dcbe5d3e8942d85f472e5d25e12f5e5a85
7
- data.tar.gz: a8e1b267f3628fd23d2924f9d6f25534744af6c2af8a80d5cc3b3fa63bbdc1f35853dcda99871db67cbeea6f6824f3ed77a19b33bd8bd1bb1f5ca7750abf8a7b
6
+ metadata.gz: ea9449f43ab086e941cc011dffd778cea4d356a2a9171aef5496e9a4593f62ca3ba68356d455cc510046ee926970c290c065b76bf445ba843a317f60c0fbbd31
7
+ data.tar.gz: 5979c264d481d50be2fed8ca17a19cb59b4c522303ff4d5cdf14164195b9b27fd6589ebe5b8e0b06276494f5298c6898fb297a97a606ca10d5fb916dbe1d568d
data/ChangeLog.md CHANGED
@@ -1,3 +1,14 @@
1
+ ### 0.2.1 / 2022-04-22
2
+
3
+ * Properly validate in {CommandMapper::OptionValue#validate} when an option,
4
+ who's value is not required, is given `true`.
5
+ * Omit `nil` arguments from {CommandMapper::Command#command_argv} if the
6
+ argument is not required.
7
+ * Improve validation error message for {CommandMapper::Types::Num} when
8
+ initialized with a `range:` value.
9
+ * Improve validation error message for {CommandMapper::Types::Map} and
10
+ {CommandMapper::Types::Enum}.
11
+
1
12
  ### 0.2.0 / 2022-04-18
2
13
 
3
14
  * Added {CommandMapper::Command.spawn} and
data/README.md CHANGED
@@ -21,9 +21,9 @@ allow safely and securely executing commands.
21
21
  * [Str][CommandMapper::Types::Str]: string values
22
22
  * [Num][CommandMapper::Types::Num]: numeric values
23
23
  * [Hex][CommandMapper::Types::Hex]: hexadecimal values
24
- * [Map][CommandMapper::Types::Map]: maps `true`/`false` to `yes`/`no`, or
25
- `enabled`/`disabled` (aka `--opt=yes|no` or
26
- `--opt=enabled|disabled` values).
24
+ * [Map][CommandMapper::Types::Map]: maps Ruby values to other String values.
25
+ * `Map::YesNo`: maps `true`/`false` to `yes`/`no`.
26
+ * `Map::EnabledDisabled`: Maps `true`/`false` to `enabled`/`disabled`.
27
27
  * [Enum][CommandMapper::Types::Enum]: maps a finite set of Symbols to a
28
28
  finite set of Strings (aka `--opt={foo|bar|baz}` values).
29
29
  * [List][CommandMapper::Types::List]: comma-separated list
@@ -314,6 +314,18 @@ Grep.run do |grep|
314
314
  end
315
315
  ```
316
316
 
317
+ Overriding the command name:
318
+
319
+ ```ruby
320
+ Grep.run(..., command_name: 'grep2')
321
+ ```
322
+
323
+ Specifying the direct path to the command:
324
+
325
+ ```ruby
326
+ Grep.run(..., command_path: '/path/to/grep')
327
+ ```
328
+
317
329
  ### Capturing output
318
330
 
319
331
  ```ruby
data/examples/grep.rb ADDED
@@ -0,0 +1,62 @@
1
+ require 'command_mapper/command'
2
+
3
+ #
4
+ # Represents the `grep` command
5
+ #
6
+ class Grep < CommandMapper::Command
7
+
8
+ command "grep" do
9
+ option "--extended-regexp"
10
+ option "--fixed-strings"
11
+ option "--basic-regexp"
12
+ option "--perl-regexp"
13
+ option "--regexp", equals: true, value: true
14
+ option "--file", name: :patterns_file, equals: true, value: true
15
+ option "--ignore-case"
16
+ option "--no-ignore-case"
17
+ option "--word-regexp"
18
+ option "--line-regexp"
19
+ option "--null-data"
20
+ option "--no-messages"
21
+ option "--invert-match"
22
+ option "--version"
23
+ option "--help"
24
+ option "--max-count", equals: true, value: {type: Num.new}
25
+ option "--byte-offset"
26
+ option "--line-number"
27
+ option "--line-buffered"
28
+ option "--with-filename"
29
+ option "--no-filename"
30
+ option "--label", equals: true, value: true
31
+ option "--only-matching"
32
+ option "--quiet"
33
+ option "--binary-files", equals: true, value: true
34
+ option "--text"
35
+ option "-I", name: :binary
36
+ option "--directories", equals: true, value: true
37
+ option "--devices", equals: true, value: true
38
+ option "--recursive"
39
+ option "--dereference-recursive"
40
+ option "--include", equals: true, value: true
41
+ option "--exclude", equals: true, value: true
42
+ option "--exclude-from", equals: true, value: true
43
+ option "--exclude-dir", equals: true, value: true
44
+ option "--files-without-match", value: true
45
+ option "--files-with-matches"
46
+ option "--count"
47
+ option "--initial-tab"
48
+ option "--null"
49
+ option "--before-context", equals: true, value: {type: Num.new}
50
+ option "--after-context", equals: true, value: {type: Num.new}
51
+ option "--context", equals: true, value: {type: Num.new}
52
+ option "--group-separator", equals: true, value: true
53
+ option "--no-group-separator"
54
+ option "--color", equals: :optional, value: {required: false}
55
+ option "--colour", equals: :optional, value: {required: false}
56
+ option "--binary"
57
+
58
+ argument :patterns
59
+ argument :file, required: false, repeats: true
60
+ end
61
+
62
+ end
@@ -24,6 +24,8 @@ module CommandMapper
24
24
  # @raise [ArgumentError]
25
25
  # The `type` keyword argument was given a `nil` value.
26
26
  #
27
+ # @api private
28
+ #
27
29
  def initialize(required: true, type: Types::Str.new)
28
30
  @required = required
29
31
 
@@ -62,6 +64,8 @@ module CommandMapper
62
64
  # Returns true if the value is valid, or `false` and a validation error
63
65
  # message if the value is not compatible.
64
66
  #
67
+ # @api semipublic
68
+ #
65
69
  def validate(value)
66
70
  if value.nil?
67
71
  if required?
@@ -30,6 +30,8 @@ module CommandMapper
30
30
  # @raise [ArgumentError]
31
31
  # The given `type:` must not be `false` or `nil`.
32
32
  #
33
+ # @api private
34
+ #
33
35
  def initialize(name, required: true, type: Types::Str.new, repeats: false)
34
36
  super(required: required, type: type)
35
37
 
@@ -56,6 +58,8 @@ module CommandMapper
56
58
  # Returns true if the value is valid, or `false` and a validation error
57
59
  # message if the value is not compatible.
58
60
  #
61
+ # @api semipublic
62
+ #
59
63
  def validate(value)
60
64
  if repeats?
61
65
  values = case value
@@ -101,6 +105,8 @@ module CommandMapper
101
105
  # @raise [ArgumentError]
102
106
  # The given value was incompatible with the argument.
103
107
  #
108
+ # @api semipublic
109
+ #
104
110
  def argv(argv=[],value)
105
111
  valid, message = validate(value)
106
112
 
@@ -5,6 +5,11 @@ require 'command_mapper/option'
5
5
  require 'shellwords'
6
6
 
7
7
  module CommandMapper
8
+ #
9
+ # Base class for all mapped commands.
10
+ #
11
+ # @api public
12
+ #
8
13
  class Command
9
14
 
10
15
  include Types
@@ -24,16 +29,6 @@ module CommandMapper
24
29
  # @return [Hash{String => String}]
25
30
  attr_reader :command_env
26
31
 
27
- # The option values to execute the command with.
28
- #
29
- # @return [Hash{String => Object}]
30
- attr_reader :command_options
31
-
32
- # The argument values to execute the command with.
33
- #
34
- # @return [Hash{String => Object}]
35
- attr_reader :command_arguments
36
-
37
32
  # The subcommand's options and arguments.
38
33
  #
39
34
  # @return [Command, nil]
@@ -51,7 +46,7 @@ module CommandMapper
51
46
  # @param [String, nil] command_path
52
47
  # Overrides the command with a custom path to the command.
53
48
  #
54
- # @param [Hash{String => String}] env
49
+ # @param [Hash{String => String}] command_env
55
50
  # Custom environment variables to pass to the command.
56
51
  #
57
52
  # @param [Hash{Symbol => Object}] kwargs
@@ -94,12 +89,16 @@ module CommandMapper
94
89
  # Initializes and runs the command.
95
90
  #
96
91
  # @param [Hash{Symbol => Object}] params
97
- # The option values.
92
+ # The option and argument values.
98
93
  #
99
- # @yield [self]
94
+ # @param [Hash{Symbol => Object}] kwargs
95
+ # Additional keywords arguments. These will be used to populate
96
+ # {#options} and {#arguments}, along with `params`.
97
+ #
98
+ # @yield [command]
100
99
  # The newly initialized command.
101
100
  #
102
- # @yieldparam [Command] self
101
+ # @yieldparam [Command] command
103
102
  #
104
103
  # @return [Boolean, nil]
105
104
  #
@@ -113,12 +112,16 @@ module CommandMapper
113
112
  # PID of the process.
114
113
  #
115
114
  # @param [Hash{Symbol => Object}] params
116
- # The option values.
115
+ # The option and argument values.
117
116
  #
118
- # @yield [self]
117
+ # @param [Hash{Symbol => Object}] kwargs
118
+ # Additional keywords arguments. These will be used to populate
119
+ # {#options} and {#arguments}, along with `params`.
120
+ #
121
+ # @yield [command]
119
122
  # The newly initialized command.
120
123
  #
121
- # @yieldparam [Command] self
124
+ # @yieldparam [Command] command
122
125
  #
123
126
  # @return [Integer]
124
127
  # The PID of the new command process.
@@ -138,12 +141,16 @@ module CommandMapper
138
141
  # output.
139
142
  #
140
143
  # @param [Hash{Symbol => Object}] params
141
- # The option values.
144
+ # The option and argument values.
142
145
  #
143
- # @yield [self]
146
+ # @param [Hash{Symbol => Object}] kwargs
147
+ # Additional keywords arguments. These will be used to populate
148
+ # {#options} and {#arguments}, along with `params`.
149
+ #
150
+ # @yield [command]
144
151
  # The newly initialized command.
145
152
  #
146
- # @yieldparam [Command] self
153
+ # @yieldparam [Command] command
147
154
  #
148
155
  # @return [String]
149
156
  # The stdout output of the command.
@@ -157,12 +164,19 @@ module CommandMapper
157
164
  # Initializes and executes the command and returns an IO object to it.
158
165
  #
159
166
  # @param [Hash{Symbol => Object}] params
160
- # The option values.
167
+ # The option and argument values.
161
168
  #
162
- # @yield [self]
169
+ # @param [String] mode
170
+ # The IO "mode" to open the IO pipe in.
171
+ #
172
+ # @param [Hash{Symbol => Object}] kwargs
173
+ # Additional keywords arguments. These will be used to populate
174
+ # {#options} and {#arguments}, along with `params`.
175
+ #
176
+ # @yield [command]
163
177
  # The newly initialized command.
164
178
  #
165
- # @yieldparam [Command] self
179
+ # @yieldparam [Command] command
166
180
  #
167
181
  # @return [IO]
168
182
  #
@@ -175,15 +189,15 @@ module CommandMapper
175
189
  # Initializes and runs the command through `sudo`.
176
190
  #
177
191
  # @param [Hash{Symbol => Object}] params
178
- # The option values.
192
+ # The option and argument values.
179
193
  #
180
194
  # @param [Hash{Symbol => Object}] kwargs
181
195
  # Additional keyword arguments for {#initialize}.
182
196
  #
183
- # @yield [self]
197
+ # @yield [command]
184
198
  # The newly initialized command.
185
199
  #
186
- # @yieldparam [Command] self
200
+ # @yieldparam [Command] command
187
201
  #
188
202
  # @return [Boolean, nil]
189
203
  #
@@ -360,6 +374,7 @@ module CommandMapper
360
374
  # All defined options.
361
375
  #
362
376
  # @return [Hash{Symbol => Argument}]
377
+ # The mapping of argument names and {Argument} objects.
363
378
  #
364
379
  # @api semipublic
365
380
  #
@@ -438,7 +453,8 @@ module CommandMapper
438
453
  #
439
454
  # All defined subcommands.
440
455
  #
441
- # @return [Hash{Symbol => Command}]
456
+ # @return [Hash{Symbol => Command.class}]
457
+ # The mapping of subcommand names and subcommand classes.
442
458
  #
443
459
  # @api semipublic
444
460
  #
@@ -535,8 +551,10 @@ module CommandMapper
535
551
  # Gets the value of an option or an argument.
536
552
  #
537
553
  # @param [Symbol] name
554
+ # The name of the option, argument, or subcommand.
538
555
  #
539
556
  # @return [Object]
557
+ # The value of the option, argument, or subcommand.
540
558
  #
541
559
  # @raise [ArgumentError]
542
560
  # The given name was not match any option or argument.
@@ -555,10 +573,13 @@ module CommandMapper
555
573
  # Sets an option or an argument with the given name.
556
574
  #
557
575
  # @param [Symbol] name
576
+ # The name of the option, argument, or subcommand.
558
577
  #
559
578
  # @param [Object] value
579
+ # The new value for the option, argument, or subcommand.
560
580
  #
561
581
  # @return [Object]
582
+ # The new value for the option, argument, or subcommand.
562
583
  #
563
584
  # @raise [ArgumentError]
564
585
  # The given name was not match any option or argument.
@@ -575,6 +596,7 @@ module CommandMapper
575
596
  # Returns an Array of command-line arguments for the command.
576
597
  #
577
598
  # @return [Array<String>]
599
+ # The formatted command-line arguments.
578
600
  #
579
601
  # @raise [ArgumentReqired]
580
602
  # A required argument was not set.
@@ -597,8 +619,10 @@ module CommandMapper
597
619
  self.class.arguments.each do |name,argument|
598
620
  value = self[name]
599
621
 
600
- if value.nil? && argument.required?
601
- raise(ArgumentRequired,"argument #{name} is required")
622
+ if value.nil?
623
+ if argument.required?
624
+ raise(ArgumentRequired,"argument #{name} is required")
625
+ end
602
626
  else
603
627
  argument.argv(additional_args,value)
604
628
  end
@@ -639,6 +663,8 @@ module CommandMapper
639
663
  # Runs the command.
640
664
  #
641
665
  # @return [Boolean, nil]
666
+ # Indicates whether the command exited successfully or not.
667
+ # `nil` indicates the command could not be found.
642
668
  #
643
669
  def run_command
644
670
  Kernel.system(@command_env,*command_argv)
@@ -674,6 +700,7 @@ module CommandMapper
674
700
  # Executes the command and returns an IO object to it.
675
701
  #
676
702
  # @return [IO]
703
+ # The IO object for the command's `STDIN`.
677
704
  #
678
705
  def popen_command(mode=nil)
679
706
  if mode then IO.popen(@command_env,command_argv,mode)
@@ -688,6 +715,8 @@ module CommandMapper
688
715
  # Additional keyword arguments for {Sudo#initialize}.
689
716
  #
690
717
  # @return [Boolean, nil]
718
+ # Indicates whether the command exited successfully or not.
719
+ # `nil` indicates the command could not be found.
691
720
  #
692
721
  def sudo_command(**sudo_kwargs,&block)
693
722
  sudo_params = sudo_kwargs.merge(command: command_argv)
@@ -718,6 +747,7 @@ module CommandMapper
718
747
  # The method name.
719
748
  #
720
749
  # @return [Boolean]
750
+ # Indicates that the method name is also an intenral method name.
721
751
  #
722
752
  def self.is_internal_method?(name)
723
753
  Command.instance_methods(false).include?(name.to_sym)
@@ -51,6 +51,8 @@ module CommandMapper
51
51
  # Specifies that the value should be appended to the option's flag
52
52
  # (ex: `-Fvalue`).
53
53
  #
54
+ # @api private
55
+ #
54
56
  def initialize(flag, name: nil, value: nil, repeats: false,
55
57
  # formatting options
56
58
  equals: nil,
@@ -81,6 +83,8 @@ module CommandMapper
81
83
  # Could not infer the name from the given option flag or was not given a
82
84
  # valid option flag.
83
85
  #
86
+ # @api private
87
+ #
84
88
  def self.infer_name_from_flag(flag)
85
89
  if flag.start_with?('--')
86
90
  name = flag[2..-1]
@@ -146,6 +150,8 @@ module CommandMapper
146
150
  # Returns true if the value is valid, or `false` and a validation error
147
151
  # message if the value is not compatible.
148
152
  #
153
+ # @api semipublic
154
+ #
149
155
  def validate(value)
150
156
  if accepts_value?
151
157
  if repeats?
@@ -173,6 +179,8 @@ module CommandMapper
173
179
  # @raise [ArgumentError]
174
180
  # The given value was incompatible with the option.
175
181
  #
182
+ # @api semipublic
183
+ #
176
184
  def argv(argv=[],value)
177
185
  valid, message = validate(value)
178
186
 
@@ -6,6 +6,26 @@ module CommandMapper
6
6
  #
7
7
  class OptionValue < Arg
8
8
 
9
+ #
10
+ # Validates whether a given value is compatible with the option {#type}.
11
+ #
12
+ # @param [Object] value
13
+ # The given value to validate.
14
+ #
15
+ # @return [true, (false, String)]
16
+ # Returns true if the value is valid, or `false` and a validation error
17
+ # message if the value is not compatible.
18
+ #
19
+ # @api semipublic
20
+ #
21
+ def validate(value)
22
+ if !required? && value == true
23
+ return true
24
+ else
25
+ super(value)
26
+ end
27
+ end
28
+
9
29
  #
10
30
  # Formats a value using the options {#type}.
11
31
  #
@@ -15,6 +35,8 @@ module CommandMapper
15
35
  # @return [String]
16
36
  # The formatted value.
17
37
  #
38
+ # @api semipublic
39
+ #
18
40
  def format(value)
19
41
  @type.format(value)
20
42
  end
@@ -2,9 +2,17 @@ require 'command_mapper/types/map'
2
2
 
3
3
  module CommandMapper
4
4
  module Types
5
+ #
6
+ # Represents a mapping of Ruby values to their String equivalents.
7
+ #
5
8
  class Enum < Map
6
9
 
10
+ # The values of the enum.
11
+ #
7
12
  # @return [Array<Object>]
13
+ #
14
+ # @api semipublic
15
+ #
8
16
  attr_reader :values
9
17
 
10
18
  #
@@ -27,6 +27,8 @@ module CommandMapper
27
27
  #
28
28
  # @return [Boolean]
29
29
  #
30
+ # @api semipublic
31
+ #
30
32
  def leading_zero?
31
33
  @leading_zero
32
34
  end
@@ -41,6 +43,8 @@ module CommandMapper
41
43
  # Returns true if the value is valid, or `false` and a validation error
42
44
  # message if the value is not compatible.
43
45
  #
46
+ # @api semipublic
47
+ #
44
48
  def validate(value)
45
49
  case value
46
50
  when String
@@ -69,6 +73,8 @@ module CommandMapper
69
73
  # @return [String]
70
74
  # The formatted numeric value.
71
75
  #
76
+ # @api semipublic
77
+ #
72
78
  def format(value)
73
79
  case value
74
80
  when String
@@ -17,6 +17,8 @@ module CommandMapper
17
17
  # Returns true if the value is valid, or `false` and a validation error
18
18
  # message if the value is not compatible.
19
19
  #
20
+ # @api semipublic
21
+ #
20
22
  def validate(value)
21
23
  valid, message = super(value)
22
24
 
@@ -17,6 +17,8 @@ module CommandMapper
17
17
  # Returns true if the value is valid, or `false` and a validation error
18
18
  # message if the value is not compatible.
19
19
  #
20
+ # @api semipublic
21
+ #
20
22
  def validate(value)
21
23
  valid, message = super(value)
22
24
 
@@ -17,6 +17,8 @@ module CommandMapper
17
17
  # Returns true if the value is valid, or `false` and a validation error
18
18
  # message if the value is not compatible.
19
19
  #
20
+ # @api semipublic
21
+ #
20
22
  def validate(value)
21
23
  unless value.empty?
22
24
  unless File.exists?(value)
@@ -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,6 +50,8 @@ 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
  #
@@ -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,4 +1,4 @@
1
1
  module CommandMapper
2
2
  # Version of command_mapper
3
- VERSION = '0.2.0'
3
+ VERSION = '0.2.1'
4
4
  end
data/spec/commnad_spec.rb CHANGED
@@ -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
 
@@ -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
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: command_mapper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Postmodern
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-19 00:00:00.000000000 Z
11
+ date: 2022-04-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -45,6 +45,7 @@ files:
45
45
  - README.md
46
46
  - Rakefile
47
47
  - commnad_mapper.gemspec
48
+ - examples/grep.rb
48
49
  - gemspec.yml
49
50
  - lib/command_mapper.rb
50
51
  - lib/command_mapper/arg.rb