command_mapper 0.2.0 → 0.2.1

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 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