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.
- checksums.yaml +4 -4
- data/ChangeLog.md +15 -0
- data/README.md +19 -5
- data/{commnad_mapper.gemspec → command_mapper.gemspec} +0 -0
- data/examples/grep.rb +62 -0
- data/gemspec.yml +2 -2
- data/lib/command_mapper/arg.rb +4 -0
- data/lib/command_mapper/argument.rb +6 -0
- data/lib/command_mapper/command.rb +59 -29
- data/lib/command_mapper/option.rb +8 -0
- data/lib/command_mapper/option_value.rb +22 -0
- data/lib/command_mapper/types/dec.rb +84 -0
- data/lib/command_mapper/types/enum.rb +8 -0
- data/lib/command_mapper/types/hex.rb +6 -0
- data/lib/command_mapper/types/input_dir.rb +2 -0
- data/lib/command_mapper/types/input_file.rb +2 -0
- data/lib/command_mapper/types/input_path.rb +2 -0
- data/lib/command_mapper/types/key_value.rb +10 -0
- data/lib/command_mapper/types/key_value_list.rb +2 -0
- data/lib/command_mapper/types/list.rb +10 -0
- data/lib/command_mapper/types/map.rb +10 -1
- data/lib/command_mapper/types/num.rb +7 -1
- data/lib/command_mapper/types/str.rb +10 -1
- data/lib/command_mapper/types/type.rb +5 -1
- data/lib/command_mapper/types.rb +1 -0
- data/lib/command_mapper/version.rb +1 -1
- data/spec/commnad_spec.rb +25 -5
- data/spec/option_value_spec.rb +28 -0
- data/spec/types/dec_spec.rb +180 -0
- data/spec/types/input_dir_spec.rb +13 -9
- data/spec/types/map_spec.rb +2 -2
- data/spec/types/num_spec.rb +6 -6
- metadata +9 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 254468c58bbee95ac037609a4ddefc34c026240b4933a37038df1bade5b7487e
|
4
|
+
data.tar.gz: 1fe51a7d6ad09b0b51c753bee28632e70292bfa9e9938896e22183c0327f48ae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c1fc9b041fafa2d92300bcbf312e8b1d848450bd4691b6b0eea57fb401a8d20b8a73613328c69cc5d323e9b42313ecc2f3e66b8bcfff14454a8cdb5bd5db5467
|
7
|
+
data.tar.gz: 62fd74bd469bff9620c94a8c0710f6d309bb0611af7e58664fa7d04808ee40b14d3b076fb8143a6fdbd409a0fd35fb12682e535c30b41ed616200dc7be982bd2
|
data/ChangeLog.md
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
### 0.3.0 / 2022-11-11
|
2
|
+
|
3
|
+
* Added {CommandMapper::Types::Dec}.
|
4
|
+
|
5
|
+
### 0.2.1 / 2022-04-22
|
6
|
+
|
7
|
+
* Properly validate in {CommandMapper::OptionValue#validate} when an option,
|
8
|
+
who's value is not required, is given `true`.
|
9
|
+
* Omit `nil` arguments from {CommandMapper::Command#command_argv} if the
|
10
|
+
argument is not required.
|
11
|
+
* Improve validation error message for {CommandMapper::Types::Num} when
|
12
|
+
initialized with a `range:` value.
|
13
|
+
* Improve validation error message for {CommandMapper::Types::Map} and
|
14
|
+
{CommandMapper::Types::Enum}.
|
15
|
+
|
1
16
|
### 0.2.0 / 2022-04-18
|
2
17
|
|
3
18
|
* Added {CommandMapper::Command.spawn} and
|
data/README.md
CHANGED
@@ -10,8 +10,8 @@
|
|
10
10
|
|
11
11
|
## Description
|
12
12
|
|
13
|
-
Command Mapper maps
|
14
|
-
allow safely and securely executing commands.
|
13
|
+
Command Mapper maps an external command's options and arguments to Class
|
14
|
+
attributes to allow safely and securely executing commands.
|
15
15
|
|
16
16
|
## Features
|
17
17
|
|
@@ -20,10 +20,11 @@ allow safely and securely executing commands.
|
|
20
20
|
* Supports common option types:
|
21
21
|
* [Str][CommandMapper::Types::Str]: string values
|
22
22
|
* [Num][CommandMapper::Types::Num]: numeric values
|
23
|
+
* [Dec][CommandMapper::Types::Dec]: decimal values
|
23
24
|
* [Hex][CommandMapper::Types::Hex]: hexadecimal values
|
24
|
-
* [Map][CommandMapper::Types::Map]: maps
|
25
|
-
`
|
26
|
-
|
25
|
+
* [Map][CommandMapper::Types::Map]: maps Ruby values to other String values.
|
26
|
+
* `Map::YesNo`: maps `true`/`false` to `yes`/`no`.
|
27
|
+
* `Map::EnabledDisabled`: Maps `true`/`false` to `enabled`/`disabled`.
|
27
28
|
* [Enum][CommandMapper::Types::Enum]: maps a finite set of Symbols to a
|
28
29
|
finite set of Strings (aka `--opt={foo|bar|baz}` values).
|
29
30
|
* [List][CommandMapper::Types::List]: comma-separated list
|
@@ -50,6 +51,7 @@ allow safely and securely executing commands.
|
|
50
51
|
|
51
52
|
[CommandMapper::Types::Str]: https://rubydoc.info/gems/command_mapper/CommandMapper/Types/Str
|
52
53
|
[CommandMapper::Types::Num]: https://rubydoc.info/gems/command_mapper/CommandMapper/Types/Num
|
54
|
+
[CommandMapper::Types::Dec]: https://rubydoc.info/gems/command_mapper/CommandMapper/Types/Dec
|
53
55
|
[CommandMapper::Types::Hex]: https://rubydoc.info/gems/command_mapper/CommandMapper/Types/Hex
|
54
56
|
[CommandMapper::Types::Map]: https://rubydoc.info/gems/command_mapper/CommandMapper/Types/Map
|
55
57
|
[CommandMapper::Types::Enum]: https://rubydoc.info/gems/command_mapper/CommandMapper/Types/Enum
|
@@ -314,6 +316,18 @@ Grep.run do |grep|
|
|
314
316
|
end
|
315
317
|
```
|
316
318
|
|
319
|
+
Overriding the command name:
|
320
|
+
|
321
|
+
```ruby
|
322
|
+
Grep.run(..., command_name: 'grep2')
|
323
|
+
```
|
324
|
+
|
325
|
+
Specifying the direct path to the command:
|
326
|
+
|
327
|
+
```ruby
|
328
|
+
Grep.run(..., command_path: '/path/to/grep')
|
329
|
+
```
|
330
|
+
|
317
331
|
### Capturing output
|
318
332
|
|
319
333
|
```ruby
|
File without changes
|
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
|
data/gemspec.yml
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
name: command_mapper
|
2
2
|
summary: Safe and secure execution of commands.
|
3
3
|
description:
|
4
|
-
Command Mapper maps
|
5
|
-
and securely executing commands.
|
4
|
+
Command Mapper maps an external command's arguments to Class attributes to
|
5
|
+
allow safely and securely executing commands.
|
6
6
|
|
7
7
|
license: MIT
|
8
8
|
authors: Postmodern
|
data/lib/command_mapper/arg.rb
CHANGED
@@ -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}]
|
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
|
-
# @
|
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]
|
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
|
-
# @
|
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]
|
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
|
-
# @
|
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]
|
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
|
-
# @
|
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]
|
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 [
|
197
|
+
# @yield [command]
|
184
198
|
# The newly initialized command.
|
185
199
|
#
|
186
|
-
# @yieldparam [Command]
|
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?
|
601
|
-
|
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
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'command_mapper/types/type'
|
2
|
+
|
3
|
+
module CommandMapper
|
4
|
+
module Types
|
5
|
+
#
|
6
|
+
# Represents a decimal value (ex: `1.5`).
|
7
|
+
#
|
8
|
+
# @since 0.3.0
|
9
|
+
#
|
10
|
+
class Dec < Type
|
11
|
+
|
12
|
+
# The optional range of acceptable decimal numbers.
|
13
|
+
#
|
14
|
+
# @return [Range<Float,Float>, nil]
|
15
|
+
#
|
16
|
+
# @api semipublic
|
17
|
+
attr_reader :range
|
18
|
+
|
19
|
+
#
|
20
|
+
# Initializes the decimal type.
|
21
|
+
#
|
22
|
+
# @param [Range<Float,Float>] range
|
23
|
+
# Specifies the range of acceptable numbers.
|
24
|
+
#
|
25
|
+
def initialize(range: nil)
|
26
|
+
@range = range
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# Validates a value.
|
31
|
+
#
|
32
|
+
# @param [String, Numeric] value
|
33
|
+
# The given value to validate.
|
34
|
+
#
|
35
|
+
# @return [true, (false, String)]
|
36
|
+
# Returns true if the value is valid, or `false` and a validation error
|
37
|
+
# message if the value is not compatible.
|
38
|
+
#
|
39
|
+
# @api semipublic
|
40
|
+
#
|
41
|
+
def validate(value)
|
42
|
+
case value
|
43
|
+
when Float
|
44
|
+
# no-op
|
45
|
+
when String
|
46
|
+
unless value =~ /\A\d+(?:\.\d+)?\z/
|
47
|
+
return [false, "contains non-decimal characters (#{value.inspect})"]
|
48
|
+
end
|
49
|
+
else
|
50
|
+
unless value.respond_to?(:to_f)
|
51
|
+
return [false, "cannot be converted into a Float (#{value.inspect})"]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
if @range
|
56
|
+
unless @range.include?(value.to_f)
|
57
|
+
return [false, "(#{value.inspect}) not within the range of acceptable values (#{range.inspect})"]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
return true
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Formats a decimal value.
|
66
|
+
#
|
67
|
+
# @param [String, Float, #to_f] value
|
68
|
+
# The given value to format.
|
69
|
+
#
|
70
|
+
# @return [String]
|
71
|
+
# The formatted decimal value.
|
72
|
+
#
|
73
|
+
# @api semipublic
|
74
|
+
#
|
75
|
+
def format(value)
|
76
|
+
case value
|
77
|
+
when Float, String then value.to_s
|
78
|
+
else value.to_f.to_s
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
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
|