command_mapper 0.1.0.pre1

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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ruby.yml +27 -0
  3. data/.gitignore +10 -0
  4. data/.rspec +1 -0
  5. data/.yardopts +1 -0
  6. data/ChangeLog.md +25 -0
  7. data/Gemfile +15 -0
  8. data/LICENSE.txt +20 -0
  9. data/README.md +369 -0
  10. data/Rakefile +12 -0
  11. data/commnad_mapper.gemspec +61 -0
  12. data/gemspec.yml +23 -0
  13. data/lib/command_mapper/arg.rb +75 -0
  14. data/lib/command_mapper/argument.rb +142 -0
  15. data/lib/command_mapper/command.rb +606 -0
  16. data/lib/command_mapper/exceptions.rb +19 -0
  17. data/lib/command_mapper/option.rb +282 -0
  18. data/lib/command_mapper/option_value.rb +21 -0
  19. data/lib/command_mapper/sudo.rb +73 -0
  20. data/lib/command_mapper/types/enum.rb +35 -0
  21. data/lib/command_mapper/types/hex.rb +82 -0
  22. data/lib/command_mapper/types/input_dir.rb +35 -0
  23. data/lib/command_mapper/types/input_file.rb +35 -0
  24. data/lib/command_mapper/types/input_path.rb +29 -0
  25. data/lib/command_mapper/types/key_value.rb +131 -0
  26. data/lib/command_mapper/types/key_value_list.rb +45 -0
  27. data/lib/command_mapper/types/list.rb +90 -0
  28. data/lib/command_mapper/types/map.rb +64 -0
  29. data/lib/command_mapper/types/num.rb +50 -0
  30. data/lib/command_mapper/types/str.rb +85 -0
  31. data/lib/command_mapper/types/type.rb +102 -0
  32. data/lib/command_mapper/types.rb +6 -0
  33. data/lib/command_mapper/version.rb +4 -0
  34. data/lib/command_mapper.rb +2 -0
  35. data/spec/arg_spec.rb +137 -0
  36. data/spec/argument_spec.rb +513 -0
  37. data/spec/commnad_spec.rb +1175 -0
  38. data/spec/exceptions_spec.rb +14 -0
  39. data/spec/option_spec.rb +882 -0
  40. data/spec/option_value_spec.rb +17 -0
  41. data/spec/spec_helper.rb +6 -0
  42. data/spec/sudo_spec.rb +24 -0
  43. data/spec/types/enum_spec.rb +31 -0
  44. data/spec/types/hex_spec.rb +158 -0
  45. data/spec/types/input_dir_spec.rb +30 -0
  46. data/spec/types/input_file_spec.rb +34 -0
  47. data/spec/types/input_path_spec.rb +32 -0
  48. data/spec/types/key_value_list_spec.rb +100 -0
  49. data/spec/types/key_value_spec.rb +272 -0
  50. data/spec/types/list_spec.rb +143 -0
  51. data/spec/types/map_spec.rb +62 -0
  52. data/spec/types/num_spec.rb +90 -0
  53. data/spec/types/str_spec.rb +232 -0
  54. data/spec/types/type_spec.rb +59 -0
  55. metadata +118 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a8f5edcef133633cddd3398cb7314514661a48f2803800f43410b1ccd2195679
4
+ data.tar.gz: 57dcbb7895fa2dff5ef8528439ae128fe9b86072674fb32da93f13e03c5d2572
5
+ SHA512:
6
+ metadata.gz: 5675fc6957f545350b54cb9a46003c9c059496366c1198b4bf2a1656dc7484d009d6e9bd896f56c14fdce222354fb788cedaf6b54b0f88c4835c28893124fc13
7
+ data.tar.gz: d3fbefad0bc588d75a4dfdf0e0f009d2162ed15562d2ee6ae44f08b9e1efc0815547f5d5fe5276548c921bad0b727e72a139b8ffbb8a09838ece1e19688676e7
@@ -0,0 +1,27 @@
1
+ name: CI
2
+
3
+ on: [ push, pull_request ]
4
+
5
+ jobs:
6
+ tests:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ fail-fast: false
10
+ matrix:
11
+ ruby:
12
+ - 2.6
13
+ - 2.7
14
+ - 3.0
15
+ - jruby
16
+ - truffleruby
17
+ name: Ruby ${{ matrix.ruby }}
18
+ steps:
19
+ - uses: actions/checkout@v2
20
+ - name: Set up Ruby
21
+ uses: ruby/setup-ruby@v1
22
+ with:
23
+ ruby-version: ${{ matrix.ruby }}
24
+ - name: Install dependencies
25
+ run: bundle install --jobs 4 --retry 3
26
+ - name: Run tests
27
+ run: bundle exec rake test
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /Gemfile.lock
2
+ /coverage
3
+ /doc/
4
+ /pkg/
5
+ /vendor/bundle/
6
+ .DS_Store
7
+ .yardoc
8
+ *.log
9
+ *.swp
10
+ *~
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour --format documentation
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup markdown --title "CommandMapper Documentation" --protected --quiet
data/ChangeLog.md ADDED
@@ -0,0 +1,25 @@
1
+ ### 0.1.0 / 2021-11-22
2
+
3
+ * Initial release:
4
+ * Added {CommandMapper::Error}.
5
+ * Added {CommandMapper::ValidationError}.
6
+ * Added {CommandMapper::ArgumentRequired}.
7
+ * Added {CommandMapper::Types::Type}.
8
+ * Added {CommandMapper::Types::Str}.
9
+ * Added {CommandMapper::Types::Num}.
10
+ * Added {CommandMapper::Types::Hex}.
11
+ * Added {CommandMapper::Types::Map}.
12
+ * Added {CommandMapper::Types::Enum}.
13
+ * Added {CommandMapper::Types::InputPath}.
14
+ * Added {CommandMapper::Types::InputFile}.
15
+ * Added {CommandMapper::Types::InputDir}.
16
+ * Added {CommandMapper::Types::List}.
17
+ * Added {CommandMapper::Types::KeyValue}.
18
+ * Added {CommandMapper::Types::KeyValueList}.
19
+ * Added {CommandMapper::Arg}.
20
+ * Added {CommandMapper::Argument}.
21
+ * Added {CommandMapper::OptionValue}.
22
+ * Added {CommandMapper::Option}.
23
+ * Added {CommandMapper::Command}.
24
+ * Added {CommandMapper::Sudo}.
25
+
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'rake'
7
+ gem 'rubygems-tasks', '~> 0.2'
8
+ gem 'rspec', '~> 3.0'
9
+ gem 'simplecov', '~> 0.20', require: false
10
+ gem 'kramdown'
11
+ gem 'yard', '~> 0.9'
12
+ gem 'yard-spellcheck'
13
+
14
+ gem 'dead_end'
15
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2021 Hal Brodigan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,369 @@
1
+ # command_mapper
2
+
3
+ [![CI](https://github.com/postmodern/command_mapper.rb/actions/workflows/ruby.yml/badge.svg)](https://github.com/postmodern/command_mapper.rb/actions/workflows/ruby.yml)
4
+ [![Code Climate](https://codeclimate.com/github/postmodern/command_mapper.rb.svg)](https://codeclimate.com/github/postmodern/command_mapper.rb)
5
+
6
+ * [Source](https://github.com/postmodern/command_mapper)
7
+ * [Issues](https://github.com/postmodern/command_mapper/issues)
8
+ * [Documentation](http://rubydoc.info/gems/command_mapper/frames)
9
+
10
+ ## Description
11
+
12
+ Command Mapper maps a command's options and arguments to Class attributes to
13
+ allow safely and securely executing commands.
14
+
15
+ ## Features
16
+
17
+ * Supports defining commands as Ruby classes.
18
+ * Supports mapping in options and additional arguments.
19
+ * Supports common option types:
20
+ * `Str`: string values
21
+ * `Num`: numeric values
22
+ * `Hex`: hexadecimal values
23
+ * `Map`: maps `true`/`false` to `yes`/`no`, or `enabled`/`disabled`
24
+ (aka `--opt=yes|no` or `--opt=enabled|disabled` values).
25
+ * `Enum`: maps a finite set of Symbols to a finite set of Strings
26
+ (aka `--opt={foo|bar|baz}` values).
27
+ * `List`: comma-separated list (aka `--opt VALUE,...`).
28
+ * `KeyValue`: maps a Hash or Array to key:value Strings
29
+ (aka `--opt KEY:VALUE` or `--opt KEY=VALUE` values).
30
+ * `KeyValueList`: a key-value list
31
+ (aka `--opt KEY:VALUE,...` or `--opt KEY=VALUE;...` values).
32
+ * `InputPath`: a path to a pre-existing file or directory
33
+ * `InputFile`: a path to a pre-existing file
34
+ * `InputDir`: a path to a pre-existing directory
35
+ * Supports mapping in sub-commands.
36
+ * Allows running the command via `IO.popen` to read the command's output.
37
+ * Allows running commands with additional environment variables.
38
+ * Allows overriding the command name or path to the command.
39
+ * Allows running commands via `sudo`.
40
+ * Prevents command injection and option injection.
41
+
42
+ ## Examples
43
+
44
+ ```ruby
45
+ require 'command_mapper/command'
46
+
47
+ #
48
+ # Represents the `grep` command
49
+ #
50
+ class Grep < CommandMapper::Command
51
+
52
+ command "grep" do
53
+ option "--extended-regexp"
54
+ option "--fixed-strings"
55
+ option "--basic-regexp"
56
+ option "--perl-regexp"
57
+ option "--regexp", equals: true, value: true
58
+ option "--file", equals: true, value: true
59
+ option "--ignore-case"
60
+ option "--no-ignore-case"
61
+ option "--word-regexp"
62
+ option "--line-regexp"
63
+ option "--null-data"
64
+ option "--no-messages"
65
+ option "--invert-match"
66
+ option "--version"
67
+ option "--help"
68
+ option "--max-count", equals: true, value: {type: Num.new}
69
+ option "--byte-offset"
70
+ option "--line-number"
71
+ option "--line-buffered"
72
+ option "--with-filename"
73
+ option "--no-filename"
74
+ option "--label", equals: true, value: true
75
+ option "--only-matching"
76
+ option "--quiet"
77
+ option "--binary-files", equals: true, value: true
78
+ option "--text"
79
+ option "-I", name: # FIXME: name
80
+ option "--directories", equals: true, value: true
81
+ option "--devices", equals: true, value: true
82
+ option "--recursive"
83
+ option "--dereference-recursive"
84
+ option "--include", equals: true, value: true
85
+ option "--exclude", equals: true, value: true
86
+ option "--exclude-from", equals: true, value: true
87
+ option "--exclude-dir", equals: true, value: true
88
+ option "--files-without-match", value: true
89
+ option "--files-with-matches"
90
+ option "--count"
91
+ option "--initial-tab"
92
+ option "--null"
93
+ option "--before-context", equals: true, value: {type: Num.new}
94
+ option "--after-context", equals: true, value: {type: Num.new}
95
+ option "--context", equals: true, value: {type: Num.new}
96
+ option "--group-separator", equals: true, value: true
97
+ option "--no-group-separator"
98
+ option "--color", equals: :optional, value: {required: false}
99
+ option "--colour", equals: :optional, value: {required: false}
100
+ option "--binary"
101
+
102
+ argument :patterns
103
+ argument :file, required: false, repeats: true
104
+ end
105
+
106
+ end
107
+ ```
108
+
109
+ ### Defining Options
110
+
111
+ ```ruby
112
+ option "--opt"
113
+ ```
114
+
115
+ Define a short option:
116
+
117
+ ```ruby
118
+ option "-o", name: :opt
119
+ ```
120
+
121
+ Defines an option with a required value:
122
+
123
+ ```ruby
124
+ option "--output", value: {required: true}
125
+ ```
126
+
127
+ Defines an option that can be specified multiple times:
128
+
129
+ ```ruby
130
+ option "--include-dir", repeats: true
131
+ ```
132
+
133
+ Defines an option that accepts a numeric value:
134
+
135
+ ```ruby
136
+ option "--count", value: {type: Num.new}
137
+ ```
138
+
139
+ Defines an option that accepts a comma-separated list:
140
+
141
+ ```ruby
142
+ option "--list", value: {type: List.new}
143
+ ```
144
+
145
+ Defines an option that accepts a `key=value` pair:
146
+
147
+ ```ruby
148
+ option "--param", value: {type: KeyValue.new}
149
+ ```
150
+
151
+ Defines an option that accepts a `key:value` pair:
152
+
153
+ ```ruby
154
+ option "--param", value: {type: KeyValue.new(separator: ':')}
155
+ ```
156
+
157
+ Defines an option that accepts a finite number of values:
158
+
159
+ ```ruby
160
+ option "--type", value: {type: Enum[:foo, :bar, :baz]}
161
+ ```
162
+
163
+ Custom methods:
164
+
165
+ ```ruby
166
+ def foo
167
+ @foo || @bar
168
+ end
169
+
170
+ def foo=(value)
171
+ @foo = case value
172
+ when Hash then ...
173
+ when Array then ...
174
+ else value.to_s
175
+ end
176
+ end
177
+ ```
178
+
179
+ ### Defining Arguments
180
+
181
+ ```ruby
182
+ argument :host
183
+ ```
184
+
185
+ Define an optional argument:
186
+
187
+ ```ruby
188
+ argument :optional_output, required: false
189
+ ```
190
+
191
+ Define an argument that can be repeated:
192
+
193
+ ```ruby
194
+ argument :files, repeats: true
195
+ ```
196
+
197
+ Define an argument that accepts an existing file:
198
+
199
+ ```ruby
200
+ argument :file, type: InputFile.new
201
+ ```
202
+
203
+ Define an argument that accepts an existing directory:
204
+
205
+ ```ruby
206
+ argument :dir, type: InputDir.new
207
+ ```
208
+
209
+ Custom methods:
210
+
211
+ ```ruby
212
+ def foo
213
+ @foo || @bar
214
+ end
215
+
216
+ def foo=(value)
217
+ @foo = case value
218
+ when Hash then ...
219
+ when Array then ...
220
+ else value.to_s
221
+ end
222
+ end
223
+ ```
224
+
225
+ ### Custom Types
226
+
227
+ ```ruby
228
+ class PortRange < CommandMapper::Types::Type
229
+
230
+ def validate(value)
231
+ case value
232
+ when Integer
233
+ true
234
+ when Range
235
+ if value.begin.kind_of?(Integer)
236
+ true
237
+ else
238
+ [false, "port range can only contain Integers"]
239
+ end
240
+ else
241
+ [false, "port range must be an Integer or a Range of Integers"]
242
+ end
243
+ end
244
+
245
+ def format(value)
246
+ case value
247
+ when Integer
248
+ "#{value}"
249
+ when Range
250
+ "#{value.begin}-#{value.end}"
251
+ end
252
+ end
253
+
254
+ end
255
+
256
+ option :ports, value: {required: true, type: PortRange.new}
257
+ ```
258
+
259
+ ### Running
260
+
261
+ Keyword arguments:
262
+
263
+ ```ruby
264
+ Grep.run(ignore_case: true, patterns: "foo", file: "file.txt")
265
+ # ...
266
+ ```
267
+
268
+ With a block:
269
+
270
+ ```ruby
271
+ Grep.run do |grep|
272
+ grep.ignore_case = true
273
+ grep.patterns = "foo"
274
+ grep.file = "file.txt"
275
+ end
276
+ ```
277
+
278
+ ### Capturing output
279
+
280
+ ```ruby
281
+ Grep.capture(ignore_case: true, patterns: "foo", file: "file.txt")
282
+ # => "..."
283
+ ```
284
+
285
+ ### popen
286
+
287
+ ```ruby
288
+ io = Grep.popen(ignore_case: true, patterns: "foo", file: "file.txt")
289
+
290
+ io.each_line do |line|
291
+ # ...
292
+ end
293
+ ```
294
+
295
+ ### sudo
296
+
297
+ ```ruby
298
+ Grep.sudo(patterns: "Error", file: "/var/log/syslog")
299
+ # Password:
300
+ # ...
301
+ ```
302
+
303
+ ### Code Gen
304
+
305
+ [command_mapper-gen] can automatically generate command classes from a command's
306
+ `--help` output and/or man page.
307
+
308
+ [command_mapper-gen]: https://github.com/postmodern/command_mapper-gen.rb#readme
309
+
310
+ ```
311
+ $ gem install command_mapper-gen
312
+ $ command_mapper-gen cat
313
+ require 'command_mapper/command'
314
+
315
+ #
316
+ # Represents the `cat` command
317
+ #
318
+ class Cat < CommandMapper::Command
319
+
320
+ command "cat" do
321
+ option "--show-all"
322
+ option "--number-nonblank"
323
+ option "-e", name: # FIXME: name
324
+ option "--show-ends"
325
+ option "--number"
326
+ option "--squeeze-blank"
327
+ option "-t", name: # FIXME: name
328
+ option "--show-tabs"
329
+ option "-u", name: # FIXME: name
330
+ option "--show-nonprinting"
331
+ option "--help"
332
+ option "--version"
333
+
334
+ argument :file, required: false, repeats: true
335
+ end
336
+
337
+ end
338
+ ```
339
+
340
+ ## Requirements
341
+
342
+ * [ruby] >= 2.0.0
343
+
344
+ ## Install
345
+
346
+ ```shell
347
+ $ gem install command_mapper
348
+ ```
349
+
350
+ ### Gemfile
351
+
352
+ ```ruby
353
+ gem 'command_mapper', '~> 0.1'
354
+ ```
355
+
356
+ ### gemspec
357
+
358
+ ```ruby
359
+ gemspec.add_dependency 'command_mapper', '~> 0.1'
360
+ ```
361
+
362
+ ## License
363
+
364
+ Copyright (c) 2021 Hal Brodigan
365
+
366
+ See {file:LICENSE.txt} for license information.
367
+
368
+ [command_mapper]: https://github.com/postmodern/command_mapper.rb#readme
369
+ [ruby]: https://www.ruby-lang.org/
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+
3
+ require 'rubygems/tasks'
4
+ Gem::Tasks.new
5
+
6
+ require 'rspec/core/rake_task'
7
+ RSpec::Core::RakeTask.new
8
+ task :test => :spec
9
+ task :default => :spec
10
+
11
+ require 'yard'
12
+ YARD::Rake::YardocTask.new
@@ -0,0 +1,61 @@
1
+ # encoding: utf-8
2
+
3
+ require 'yaml'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gemspec = YAML.load_file('gemspec.yml')
7
+
8
+ gem.name = gemspec.fetch('name')
9
+ gem.version = gemspec.fetch('version') do
10
+ lib_dir = File.join(File.dirname(__FILE__),'lib')
11
+ $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
12
+
13
+ require 'command_mapper/version'
14
+ CommandMapper::VERSION
15
+ end
16
+
17
+ gem.summary = gemspec['summary']
18
+ gem.description = gemspec['description']
19
+ gem.licenses = Array(gemspec['license'])
20
+ gem.authors = Array(gemspec['authors'])
21
+ gem.email = gemspec['email']
22
+ gem.homepage = gemspec['homepage']
23
+ gem.metadata = gemspec['metadata'] if gemspec['metadata']
24
+
25
+ glob = lambda { |patterns| gem.files & Dir[*patterns] }
26
+
27
+ gem.files = `git ls-files`.split($/)
28
+ gem.files = glob[gemspec['files']] if gemspec['files']
29
+
30
+ gem.executables = gemspec.fetch('executables') do
31
+ glob['bin/*'].map { |path| File.basename(path) }
32
+ end
33
+ gem.default_executable = gem.executables.first if Gem::VERSION < '1.7.'
34
+
35
+ gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
36
+ gem.test_files = glob[gemspec['test_files'] || '{test/{**/}*_test.rb']
37
+ gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
38
+
39
+ gem.require_paths = Array(gemspec.fetch('require_paths') {
40
+ %w[ext lib].select { |dir| File.directory?(dir) }
41
+ })
42
+
43
+ gem.requirements = gemspec['requirements']
44
+ gem.required_ruby_version = gemspec['required_ruby_version']
45
+ gem.required_rubygems_version = gemspec['required_rubygems_version']
46
+ gem.post_install_message = gemspec['post_install_message']
47
+
48
+ split = lambda { |string| string.split(/,\s*/) }
49
+
50
+ if gemspec['dependencies']
51
+ gemspec['dependencies'].each do |name,versions|
52
+ gem.add_dependency(name,split[versions])
53
+ end
54
+ end
55
+
56
+ if gemspec['development_dependencies']
57
+ gemspec['development_dependencies'].each do |name,versions|
58
+ gem.add_development_dependency(name,split[versions])
59
+ end
60
+ end
61
+ end
data/gemspec.yml ADDED
@@ -0,0 +1,23 @@
1
+ name: command_mapper
2
+ summary: Safe and secure execution of commands.
3
+ description:
4
+ Command Mapper maps a command's arguments to Class attributes to allow safely
5
+ and securely executing commands.
6
+
7
+ license: MIT
8
+ authors: Postmodern
9
+ email: postmodern.mod3@gmail.com
10
+ homepage: https://github.com/postmodern/command_mapper.rb#readme
11
+ has_yard: true
12
+
13
+ metadata:
14
+ documentation_uri: https://rubydoc.info/gems/command_mapper
15
+ source_code_uri: https://github.com/postmodern/command_mapper.rb
16
+ bug_tracker_uri: https://github.com/postmodern/command_mapper.rb/issues
17
+ changelog_uri: https://github.com/postmodern/command_mapper.rb/blob/master/ChangeLog.md
18
+ rubygems_mfa_required: 'true'
19
+
20
+ required_ruby_version: ">= 2.0.0"
21
+
22
+ development_dependencies:
23
+ bundler: ~> 2.0
@@ -0,0 +1,75 @@
1
+ require 'command_mapper/types/type'
2
+ require 'command_mapper/types/str'
3
+
4
+ module CommandMapper
5
+ #
6
+ # The base class for both {Option options} and {Argument arguments}.
7
+ #
8
+ class Arg
9
+ # The argument's arg's type.
10
+ #
11
+ # @return [Types::Type, nil]
12
+ attr_reader :type
13
+
14
+ #
15
+ # Initializes the argument.
16
+ #
17
+ # @param [Boolean] required
18
+ # Specifies whether the argument is required or can be omitted.
19
+ #
20
+ # @param [Types::Type, Hash, nil] type
21
+ #
22
+ # @raise [ArgumentError]
23
+ # The `type` keyword argument was given a `nil` value.
24
+ #
25
+ def initialize(required: true, type: Types::Str.new)
26
+ @required = required
27
+
28
+ if type.nil?
29
+ raise(ArgumentError,"type: keyword cannot be nil")
30
+ end
31
+
32
+ @type = Types::Type(type)
33
+ end
34
+
35
+ #
36
+ # Specifies whether the argument value is required.
37
+ #
38
+ # @return [Boolean]
39
+ #
40
+ def required?
41
+ @required
42
+ end
43
+
44
+ #
45
+ # Specifies whether the argument value can be omitted.
46
+ #
47
+ # @return [Boolean]
48
+ #
49
+ def optional?
50
+ !@required
51
+ end
52
+
53
+ #
54
+ # Validates whether a given value is compatible with the arg.
55
+ #
56
+ # @param [Object] value
57
+ #
58
+ # @return [true, (false, String)]
59
+ # Returns true if the value is valid, or `false` and a validation error
60
+ # message if the value is not compatible.
61
+ #
62
+ def validate(value)
63
+ if value.nil?
64
+ if required?
65
+ return [false, "does not accept a nil value"]
66
+ else
67
+ return true
68
+ end
69
+ else
70
+ return @type.validate(value)
71
+ end
72
+ end
73
+
74
+ end
75
+ end