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.
- checksums.yaml +7 -0
- data/.github/workflows/ruby.yml +27 -0
- data/.gitignore +10 -0
- data/.rspec +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +25 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +20 -0
- data/README.md +369 -0
- data/Rakefile +12 -0
- data/commnad_mapper.gemspec +61 -0
- data/gemspec.yml +23 -0
- data/lib/command_mapper/arg.rb +75 -0
- data/lib/command_mapper/argument.rb +142 -0
- data/lib/command_mapper/command.rb +606 -0
- data/lib/command_mapper/exceptions.rb +19 -0
- data/lib/command_mapper/option.rb +282 -0
- data/lib/command_mapper/option_value.rb +21 -0
- data/lib/command_mapper/sudo.rb +73 -0
- data/lib/command_mapper/types/enum.rb +35 -0
- data/lib/command_mapper/types/hex.rb +82 -0
- data/lib/command_mapper/types/input_dir.rb +35 -0
- data/lib/command_mapper/types/input_file.rb +35 -0
- data/lib/command_mapper/types/input_path.rb +29 -0
- data/lib/command_mapper/types/key_value.rb +131 -0
- data/lib/command_mapper/types/key_value_list.rb +45 -0
- data/lib/command_mapper/types/list.rb +90 -0
- data/lib/command_mapper/types/map.rb +64 -0
- data/lib/command_mapper/types/num.rb +50 -0
- data/lib/command_mapper/types/str.rb +85 -0
- data/lib/command_mapper/types/type.rb +102 -0
- data/lib/command_mapper/types.rb +6 -0
- data/lib/command_mapper/version.rb +4 -0
- data/lib/command_mapper.rb +2 -0
- data/spec/arg_spec.rb +137 -0
- data/spec/argument_spec.rb +513 -0
- data/spec/commnad_spec.rb +1175 -0
- data/spec/exceptions_spec.rb +14 -0
- data/spec/option_spec.rb +882 -0
- data/spec/option_value_spec.rb +17 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/sudo_spec.rb +24 -0
- data/spec/types/enum_spec.rb +31 -0
- data/spec/types/hex_spec.rb +158 -0
- data/spec/types/input_dir_spec.rb +30 -0
- data/spec/types/input_file_spec.rb +34 -0
- data/spec/types/input_path_spec.rb +32 -0
- data/spec/types/key_value_list_spec.rb +100 -0
- data/spec/types/key_value_spec.rb +272 -0
- data/spec/types/list_spec.rb +143 -0
- data/spec/types/map_spec.rb +62 -0
- data/spec/types/num_spec.rb +90 -0
- data/spec/types/str_spec.rb +232 -0
- data/spec/types/type_spec.rb +59 -0
- metadata +118 -0
@@ -0,0 +1,606 @@
|
|
1
|
+
require 'command_mapper/types'
|
2
|
+
require 'command_mapper/argument'
|
3
|
+
require 'command_mapper/option'
|
4
|
+
|
5
|
+
require 'shellwords'
|
6
|
+
|
7
|
+
module CommandMapper
|
8
|
+
class Command
|
9
|
+
|
10
|
+
include Types
|
11
|
+
|
12
|
+
# The command name.
|
13
|
+
#
|
14
|
+
# @return [String]
|
15
|
+
attr_reader :command_name
|
16
|
+
|
17
|
+
# The optional path to the command.
|
18
|
+
#
|
19
|
+
# @return [String, nil]
|
20
|
+
attr_reader :command_path
|
21
|
+
|
22
|
+
# The environment variables to execute the command with.
|
23
|
+
#
|
24
|
+
# @return [Hash{String => String}]
|
25
|
+
attr_reader :command_env
|
26
|
+
|
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
|
+
# The subcommand's options and arguments.
|
38
|
+
#
|
39
|
+
# @return [Command, nil]
|
40
|
+
attr_reader :command_subcommand
|
41
|
+
|
42
|
+
#
|
43
|
+
# Initializes the command.
|
44
|
+
#
|
45
|
+
# @param [Hash{Symbol => Object}] params
|
46
|
+
# The option and argument values.
|
47
|
+
#
|
48
|
+
# @param [String] command_name
|
49
|
+
# Overrides the command with a custom command name.
|
50
|
+
#
|
51
|
+
# @param [String, nil] command_path
|
52
|
+
# Overrides the command with a custom path to the command.
|
53
|
+
#
|
54
|
+
# @param [Hash{String => String}] env
|
55
|
+
# Custom environment variables to pass to the command.
|
56
|
+
#
|
57
|
+
# @param [Hash{Symbol => Object}] kwargs
|
58
|
+
# Additional keywords arguments. These will be used to populate
|
59
|
+
# {#options} and {#arguments}, along with `params`.
|
60
|
+
#
|
61
|
+
# @yield [self]
|
62
|
+
# The newly initialized command.
|
63
|
+
#
|
64
|
+
# @yieldparam [Command] self
|
65
|
+
#
|
66
|
+
# @example with a symbol Hash
|
67
|
+
# MyCommand.new({foo: 'bar', baz: 'qux'})
|
68
|
+
#
|
69
|
+
# @example with a keyword arguments
|
70
|
+
# MyCommand.new(foo: 'bar', baz: 'qux')
|
71
|
+
#
|
72
|
+
# @example with a custom env Hash:
|
73
|
+
# MyCommand.new({foo: 'bar', baz: 'qux'}, env: {'FOO' =>'bar'})
|
74
|
+
# MyCommand.new(foo: 'bar', baz: 'qux', env: {'FOO' => 'bar'})
|
75
|
+
#
|
76
|
+
def initialize(params={}, command_name: self.class.command_name,
|
77
|
+
command_path: nil,
|
78
|
+
command_env: {},
|
79
|
+
**kwargs)
|
80
|
+
@command_name = command_name
|
81
|
+
@command_path = command_path
|
82
|
+
@command_env = command_env
|
83
|
+
|
84
|
+
params = params.merge(kwargs)
|
85
|
+
|
86
|
+
params.each do |name,value|
|
87
|
+
self[name] = value
|
88
|
+
end
|
89
|
+
|
90
|
+
yield self if block_given?
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
# Initializes and runs the command.
|
95
|
+
#
|
96
|
+
# @param [Hash{Symbol => Object}] params
|
97
|
+
# The option values.
|
98
|
+
#
|
99
|
+
# @yield [self]
|
100
|
+
# The newly initialized command.
|
101
|
+
#
|
102
|
+
# @yieldparam [Command] self
|
103
|
+
#
|
104
|
+
# @return [Boolean, nil]
|
105
|
+
#
|
106
|
+
def self.run(params={},**kwargs,&block)
|
107
|
+
command = new(params,**kwargs,&block)
|
108
|
+
command.run_command
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Runs the command in a shell and captures all stdout output.
|
113
|
+
#
|
114
|
+
# @param [Hash{Symbol => Object}] params
|
115
|
+
# The option values.
|
116
|
+
#
|
117
|
+
# @yield [self]
|
118
|
+
# The newly initialized command.
|
119
|
+
#
|
120
|
+
# @yieldparam [Command] self
|
121
|
+
#
|
122
|
+
# @return [String]
|
123
|
+
# The stdout output of the command.
|
124
|
+
#
|
125
|
+
def self.capture(params={},**kwargs,&block)
|
126
|
+
command = new(params,**kwargs,&block)
|
127
|
+
command.capture_command
|
128
|
+
end
|
129
|
+
|
130
|
+
#
|
131
|
+
# Executes the command and returns an IO object to it.
|
132
|
+
#
|
133
|
+
# @param [Hash{Symbol => Object}] params
|
134
|
+
# The option values.
|
135
|
+
#
|
136
|
+
# @yield [self]
|
137
|
+
# The newly initialized command.
|
138
|
+
#
|
139
|
+
# @yieldparam [Command] self
|
140
|
+
#
|
141
|
+
# @return [IO]
|
142
|
+
#
|
143
|
+
def self.popen(params={}, mode: 'r', **kwargs,&block)
|
144
|
+
command = new(params,**kwargs,&block)
|
145
|
+
command.popen_command
|
146
|
+
end
|
147
|
+
|
148
|
+
#
|
149
|
+
# Initializes and runs the command through sudo.
|
150
|
+
#
|
151
|
+
# @param [Hash{Symbol => Object}] params
|
152
|
+
# The option values.
|
153
|
+
#
|
154
|
+
# @param [Hash{Symbol => Object}] kwargs
|
155
|
+
# Additional keyword arguments for {#initialize}.
|
156
|
+
#
|
157
|
+
# @yield [self]
|
158
|
+
# The newly initialized command.
|
159
|
+
#
|
160
|
+
# @yieldparam [Command] self
|
161
|
+
#
|
162
|
+
# @return [Boolean, nil]
|
163
|
+
#
|
164
|
+
def self.sudo(params={}, sudo: {}, **kwargs,&block)
|
165
|
+
command = new(params,**kwargs,&block)
|
166
|
+
command.sudo_command(**sudo)
|
167
|
+
end
|
168
|
+
|
169
|
+
#
|
170
|
+
# Gets or sets the command name.
|
171
|
+
#
|
172
|
+
# @param [#to_s] new_name
|
173
|
+
# The optional new command name.
|
174
|
+
#
|
175
|
+
# @return [String]
|
176
|
+
# The command name.
|
177
|
+
#
|
178
|
+
# @raise [NotImplementedError]
|
179
|
+
# The command class did not call {command}.
|
180
|
+
#
|
181
|
+
# @api semipublic
|
182
|
+
#
|
183
|
+
def self.command_name
|
184
|
+
@command_name || raise(NotImplementedError,"#{self} did not call command(...)")
|
185
|
+
end
|
186
|
+
|
187
|
+
#
|
188
|
+
# @param [#to_s] new_command_name
|
189
|
+
#
|
190
|
+
# @yield [self]
|
191
|
+
#
|
192
|
+
# @example
|
193
|
+
# command 'grep'
|
194
|
+
# # ...
|
195
|
+
#
|
196
|
+
# @example
|
197
|
+
# command 'grep' do
|
198
|
+
# option "--regexp", equals: true, value: true
|
199
|
+
# # ...
|
200
|
+
# end
|
201
|
+
#
|
202
|
+
# @api public
|
203
|
+
#
|
204
|
+
def self.command(new_command_name,&block)
|
205
|
+
@command_name = new_command_name.to_s.freeze
|
206
|
+
yield self if block_given?
|
207
|
+
end
|
208
|
+
|
209
|
+
#
|
210
|
+
# All defined options.
|
211
|
+
#
|
212
|
+
# @return [Hash{Symbol => Option}]
|
213
|
+
#
|
214
|
+
# @api semipublic
|
215
|
+
#
|
216
|
+
def self.options
|
217
|
+
@options ||= if superclass < Command
|
218
|
+
superclass.options.dup
|
219
|
+
else
|
220
|
+
{}
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
#
|
225
|
+
# Defines an option for the command.
|
226
|
+
#
|
227
|
+
# @param [String] flag
|
228
|
+
# The option's command-line flag.
|
229
|
+
#
|
230
|
+
# @param [Symbol, nil] name
|
231
|
+
# The option's name.
|
232
|
+
#
|
233
|
+
# @param [Boolean] equals
|
234
|
+
# Specifies whether the option's flag and value should be separated with a
|
235
|
+
# `=` character.
|
236
|
+
#
|
237
|
+
# @param [Hash, nil] value
|
238
|
+
# The option's value.
|
239
|
+
#
|
240
|
+
# @option value [Boolean] :required
|
241
|
+
# Specifies whether the option requires a value or not.
|
242
|
+
#
|
243
|
+
# @option value [Types:Type, Hash, nil] :type
|
244
|
+
# The explicit type for the option's value.
|
245
|
+
#
|
246
|
+
# @param [Boolean] repeats
|
247
|
+
# Specifies whether the option can be given multiple times.
|
248
|
+
#
|
249
|
+
# @api public
|
250
|
+
#
|
251
|
+
# @example Defining an option:
|
252
|
+
# option '--foo'
|
253
|
+
#
|
254
|
+
# @example Defining an option with a custom name:
|
255
|
+
# option '-F', name: :foo
|
256
|
+
#
|
257
|
+
# @example Defining an option who's value is required:
|
258
|
+
# option '--file', value: true
|
259
|
+
#
|
260
|
+
# @example Defining an option who's value is optional:
|
261
|
+
# option '--file', value: {required: false}
|
262
|
+
#
|
263
|
+
# @example Defining an `--opt=value` option:
|
264
|
+
# option '--foo', equals: true, value: true
|
265
|
+
#
|
266
|
+
# @example Defining an option that can be repeated multiple times:
|
267
|
+
# option '--foo', repeats: true
|
268
|
+
#
|
269
|
+
# @example Defining an option that takes a comma-separated list:
|
270
|
+
# option '--list', value: List.new
|
271
|
+
#
|
272
|
+
# @raise [ArgumentError]
|
273
|
+
# The option flag conflicts with a pre-existing internal method.
|
274
|
+
#
|
275
|
+
def self.option(flag, name: nil, equals: nil, value: nil, repeats: false, &block)
|
276
|
+
option = Option.new(flag, name: name,
|
277
|
+
equals: equals,
|
278
|
+
value: value,
|
279
|
+
repeats: repeats,
|
280
|
+
&block)
|
281
|
+
|
282
|
+
self.options[option.name] = option
|
283
|
+
|
284
|
+
if is_internal_method?(option.name)
|
285
|
+
if name
|
286
|
+
raise(ArgumentError,"option #{flag.inspect} with name #{name.inspect} cannot override the internal method with same name: ##{option.name}")
|
287
|
+
else
|
288
|
+
raise(ArgumentError,"option #{flag.inspect} maps to method name ##{option.name} and cannot override the internal method with same name: ##{option.name}")
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
attr_accessor option.name
|
293
|
+
end
|
294
|
+
|
295
|
+
#
|
296
|
+
# All defined options.
|
297
|
+
#
|
298
|
+
# @return [Hash{Symbol => Argument}]
|
299
|
+
#
|
300
|
+
# @api semipublic
|
301
|
+
#
|
302
|
+
def self.arguments
|
303
|
+
@arguments ||= if superclass < Command
|
304
|
+
superclass.arguments.dup
|
305
|
+
else
|
306
|
+
{}
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
#
|
311
|
+
# Defines an option for the command.
|
312
|
+
#
|
313
|
+
# @param [Symbol] name
|
314
|
+
#
|
315
|
+
# @param [Boolean] required
|
316
|
+
# Specifies whether the argument is required or can be omitted.
|
317
|
+
#
|
318
|
+
# @param [Types::Type, Hash, nil] type
|
319
|
+
# The explicit type for the argument.
|
320
|
+
#
|
321
|
+
# @param [Boolean] repeats
|
322
|
+
# Specifies whether the option can be repeated multiple times.
|
323
|
+
#
|
324
|
+
# @api public
|
325
|
+
#
|
326
|
+
# @example Define an argument:
|
327
|
+
# argument :file
|
328
|
+
#
|
329
|
+
# @example Define an argument that can be specified multiple times:
|
330
|
+
# argument :files, repeats: true
|
331
|
+
#
|
332
|
+
# @example Define an optional argument:
|
333
|
+
# argument :file, required: false
|
334
|
+
#
|
335
|
+
# @raise [ArgumentError]
|
336
|
+
# The argument name conflicts with a pre-existing internal method.
|
337
|
+
#
|
338
|
+
def self.argument(name, required: true, type: Str.new, repeats: false)
|
339
|
+
name = name.to_sym
|
340
|
+
argument = Argument.new(name, required: required,
|
341
|
+
type: type,
|
342
|
+
repeats: repeats)
|
343
|
+
|
344
|
+
self.arguments[argument.name] = argument
|
345
|
+
|
346
|
+
if is_internal_method?(argument.name)
|
347
|
+
raise(ArgumentError,"argument #{name.inspect} cannot override internal method with same name: ##{argument.name}")
|
348
|
+
end
|
349
|
+
|
350
|
+
attr_accessor name
|
351
|
+
end
|
352
|
+
|
353
|
+
#
|
354
|
+
# All defined subcommands.
|
355
|
+
#
|
356
|
+
# @return [Hash{Symbol => Command}]
|
357
|
+
#
|
358
|
+
# @api semipublic
|
359
|
+
#
|
360
|
+
def self.subcommands
|
361
|
+
@subcommands ||= if superclass < Command
|
362
|
+
superclass.subcommands.dup
|
363
|
+
else
|
364
|
+
{}
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
#
|
369
|
+
# Defines a subcommand.
|
370
|
+
#
|
371
|
+
# @param [String] name
|
372
|
+
# The name of the subcommand.
|
373
|
+
#
|
374
|
+
# @yield [subcommand]
|
375
|
+
# The given block will be used to populate the subcommand's options.
|
376
|
+
#
|
377
|
+
# @yieldparam [Command] subcommand
|
378
|
+
# The newly created subcommand class.
|
379
|
+
#
|
380
|
+
# @note
|
381
|
+
# Also defines a class within the command class using the subcommand's
|
382
|
+
# name.
|
383
|
+
#
|
384
|
+
# @example Defining a sub-command:
|
385
|
+
# class Git
|
386
|
+
# command 'git' do
|
387
|
+
# subcommand 'clone' do
|
388
|
+
# option '--bare'
|
389
|
+
# # ...
|
390
|
+
# end
|
391
|
+
# end
|
392
|
+
# end
|
393
|
+
#
|
394
|
+
# @raise [ArgumentError]
|
395
|
+
# The subcommand name conflicts with a pre-existing internal method.
|
396
|
+
#
|
397
|
+
def self.subcommand(name,&block)
|
398
|
+
name = name.to_s
|
399
|
+
|
400
|
+
subcommand_class = Class.new(Command)
|
401
|
+
subcommand_class.command(name)
|
402
|
+
subcommand_class.class_eval(&block)
|
403
|
+
|
404
|
+
method_name = name.tr('-','_')
|
405
|
+
class_name = name.split(/[_-]+/).map(&:capitalize).join
|
406
|
+
|
407
|
+
self.subcommands[method_name.to_sym] = subcommand_class
|
408
|
+
const_set(class_name,subcommand_class)
|
409
|
+
|
410
|
+
if is_internal_method?(method_name)
|
411
|
+
raise(ArgumentError,"subcommand #{name.inspect} maps to method name ##{method_name} and cannot override the internal method with same name: ##{method_name}")
|
412
|
+
end
|
413
|
+
|
414
|
+
define_method(method_name) do |&block|
|
415
|
+
if block then @command_subcommand = subcommand_class.new(&block)
|
416
|
+
else @command_subcommand
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
define_method(:"#{method_name}=") do |options|
|
421
|
+
@command_subcommand = if options
|
422
|
+
subcommand_class.new(options)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
#
|
428
|
+
# Gets the value of an option or an argument.
|
429
|
+
#
|
430
|
+
# @param [Symbol] name
|
431
|
+
#
|
432
|
+
# @return [Object]
|
433
|
+
#
|
434
|
+
# @raise [ArgumentError]
|
435
|
+
# The given name was not match any option or argument.
|
436
|
+
#
|
437
|
+
def [](name)
|
438
|
+
name = name.to_s
|
439
|
+
|
440
|
+
if respond_to?(name)
|
441
|
+
send(name)
|
442
|
+
else
|
443
|
+
raise(ArgumentError,"#{self.class} does not define ##{name}")
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
#
|
448
|
+
# Sets an option or an argument with the given name.
|
449
|
+
#
|
450
|
+
# @param [Symbol] name
|
451
|
+
#
|
452
|
+
# @param [Object] value
|
453
|
+
#
|
454
|
+
# @return [Object]
|
455
|
+
#
|
456
|
+
# @raise [ArgumentError]
|
457
|
+
# The given name was not match any option or argument.
|
458
|
+
#
|
459
|
+
def []=(name,value)
|
460
|
+
if respond_to?("#{name}=")
|
461
|
+
send("#{name}=",value)
|
462
|
+
else
|
463
|
+
raise(ArgumentError,"#{self.class} does not define ##{name}=")
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
#
|
468
|
+
# Returns an Array of command-line arguments for the command.
|
469
|
+
#
|
470
|
+
# @return [Array<String>]
|
471
|
+
#
|
472
|
+
# @raise [ArgumentReqired]
|
473
|
+
# A required argument was not set.
|
474
|
+
#
|
475
|
+
def command_argv
|
476
|
+
argv = [@command_path || @command_name]
|
477
|
+
|
478
|
+
self.class.options.each do |name,option|
|
479
|
+
unless (value = self[name]).nil?
|
480
|
+
option.argv(argv,value)
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
if @command_subcommand
|
485
|
+
# a subcommand takes precedence over any command arguments
|
486
|
+
argv.concat(@command_subcommand.command_argv)
|
487
|
+
else
|
488
|
+
additional_args = []
|
489
|
+
|
490
|
+
self.class.arguments.each do |name,argument|
|
491
|
+
value = self[name]
|
492
|
+
|
493
|
+
if value.nil? && argument.required?
|
494
|
+
raise(ArgumentRequired,"argument #{name} is required")
|
495
|
+
else
|
496
|
+
argument.argv(additional_args,value)
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
if additional_args.any? { |arg| arg.start_with?('-') }
|
501
|
+
# append a '--' separator if any of the arguments start with a '-'
|
502
|
+
argv << '--'
|
503
|
+
end
|
504
|
+
|
505
|
+
argv.concat(additional_args)
|
506
|
+
end
|
507
|
+
|
508
|
+
return argv
|
509
|
+
end
|
510
|
+
|
511
|
+
#
|
512
|
+
# Escapes any shell control-characters so that it can be ran in a shell.
|
513
|
+
#
|
514
|
+
# @return [String]
|
515
|
+
# The shell-escaped command.
|
516
|
+
#
|
517
|
+
def command_string
|
518
|
+
escaped_command = Shellwords.shelljoin(command_argv)
|
519
|
+
|
520
|
+
unless @command_env.empty?
|
521
|
+
escaped_env = @command_env.map { |name,value|
|
522
|
+
"#{Shellwords.shellescape(name)}=#{Shellwords.shellescape(value)}"
|
523
|
+
}.join(' ')
|
524
|
+
|
525
|
+
escaped_command = "#{escaped_env} #{escaped_command}"
|
526
|
+
end
|
527
|
+
|
528
|
+
return escaped_command
|
529
|
+
end
|
530
|
+
|
531
|
+
#
|
532
|
+
# Initializes and runs the command.
|
533
|
+
#
|
534
|
+
# @return [Boolean, nil]
|
535
|
+
#
|
536
|
+
def run_command
|
537
|
+
system(@command_env,*command_argv)
|
538
|
+
end
|
539
|
+
|
540
|
+
#
|
541
|
+
# Runs the command in a shell and captures all stdout output.
|
542
|
+
#
|
543
|
+
# @return [String]
|
544
|
+
# The stdout output of the command.
|
545
|
+
#
|
546
|
+
def capture_command
|
547
|
+
`#{command_string}`
|
548
|
+
end
|
549
|
+
|
550
|
+
#
|
551
|
+
# Executes the command and returns an IO object to it.
|
552
|
+
#
|
553
|
+
# @return [IO]
|
554
|
+
#
|
555
|
+
def popen_command(mode=nil)
|
556
|
+
if mode then IO.popen(@command_env,command_argv,mode)
|
557
|
+
else IO.popen(@command_env,command_argv)
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
#
|
562
|
+
# Initializes and runs the command through sudo.
|
563
|
+
#
|
564
|
+
# @param [Hash{Symbol => Object}] sudo_params
|
565
|
+
# Additional keyword arguments for {Sudo#initialize}.
|
566
|
+
#
|
567
|
+
# @return [Boolean, nil]
|
568
|
+
#
|
569
|
+
def sudo_command(**sudo_kwargs,&block)
|
570
|
+
sudo_params = sudo_kwargs.merge(command: command_argv)
|
571
|
+
|
572
|
+
Sudo.run(sudo_params, command_env: @command_env, &block)
|
573
|
+
end
|
574
|
+
|
575
|
+
#
|
576
|
+
# @see #argv
|
577
|
+
#
|
578
|
+
def to_a
|
579
|
+
command_argv
|
580
|
+
end
|
581
|
+
|
582
|
+
#
|
583
|
+
# @see #shellescape
|
584
|
+
#
|
585
|
+
def to_s
|
586
|
+
command_string
|
587
|
+
end
|
588
|
+
|
589
|
+
private
|
590
|
+
|
591
|
+
#
|
592
|
+
# Determines if there is an internal method of the same name.
|
593
|
+
#
|
594
|
+
# @param [#to_sym] name
|
595
|
+
# The method name.
|
596
|
+
#
|
597
|
+
# @return [Boolean]
|
598
|
+
#
|
599
|
+
def self.is_internal_method?(name)
|
600
|
+
Command.instance_methods(false).include?(name.to_sym)
|
601
|
+
end
|
602
|
+
|
603
|
+
end
|
604
|
+
end
|
605
|
+
|
606
|
+
require 'command_mapper/sudo'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module CommandMapper
|
2
|
+
#
|
3
|
+
# Commaon base class for all {CommandMapper} exceptions.
|
4
|
+
#
|
5
|
+
class Error < RuntimeError
|
6
|
+
end
|
7
|
+
|
8
|
+
#
|
9
|
+
# Represents a argument or option value validation error.
|
10
|
+
#
|
11
|
+
class ValidationError < Error
|
12
|
+
end
|
13
|
+
|
14
|
+
#
|
15
|
+
# Indicates that a required argument was not set.
|
16
|
+
#
|
17
|
+
class ArgumentRequired < Error
|
18
|
+
end
|
19
|
+
end
|