commander-openflighthpc 1.0.0.pre.alpha1
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/.gitignore +7 -0
- data/.rspec +2 -0
- data/.rubocop.yml +44 -0
- data/.rubocop_todo.yml +77 -0
- data/.travis.yml +14 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +3 -0
- data/LICENSE +51 -0
- data/Manifest +38 -0
- data/README.md +492 -0
- data/Rakefile +13 -0
- data/bin/commander +104 -0
- data/commander-openflighthpc.gemspec +32 -0
- data/lib/commander.rb +36 -0
- data/lib/commander/blank.rb +7 -0
- data/lib/commander/command.rb +263 -0
- data/lib/commander/configure.rb +14 -0
- data/lib/commander/core_ext.rb +2 -0
- data/lib/commander/core_ext/array.rb +24 -0
- data/lib/commander/core_ext/object.rb +8 -0
- data/lib/commander/delegates.rb +27 -0
- data/lib/commander/help_formatters.rb +52 -0
- data/lib/commander/help_formatters/base.rb +24 -0
- data/lib/commander/help_formatters/terminal.rb +24 -0
- data/lib/commander/help_formatters/terminal/command_help.erb +35 -0
- data/lib/commander/help_formatters/terminal/help.erb +36 -0
- data/lib/commander/help_formatters/terminal/subcommand_help.erb +23 -0
- data/lib/commander/help_formatters/terminal_compact.rb +11 -0
- data/lib/commander/help_formatters/terminal_compact/command_help.erb +26 -0
- data/lib/commander/help_formatters/terminal_compact/help.erb +29 -0
- data/lib/commander/help_formatters/terminal_compact/subcommand_help.erb +15 -0
- data/lib/commander/import.rb +5 -0
- data/lib/commander/methods.rb +11 -0
- data/lib/commander/patches/decimal-integer.rb +17 -0
- data/lib/commander/patches/help_formatter_binding.rb +15 -0
- data/lib/commander/patches/implicit-short-tags.rb +75 -0
- data/lib/commander/patches/option_defaults.rb +23 -0
- data/lib/commander/patches/validate_inputs.rb +76 -0
- data/lib/commander/platform.rb +7 -0
- data/lib/commander/runner.rb +493 -0
- data/lib/commander/user_interaction.rb +551 -0
- data/lib/commander/version.rb +3 -0
- data/spec/command_spec.rb +157 -0
- data/spec/configure_spec.rb +37 -0
- data/spec/core_ext/array_spec.rb +18 -0
- data/spec/core_ext/object_spec.rb +19 -0
- data/spec/help_formatters/terminal_compact_spec.rb +69 -0
- data/spec/help_formatters/terminal_spec.rb +67 -0
- data/spec/methods_spec.rb +61 -0
- data/spec/patches/validate_inputs_spec.rb +84 -0
- data/spec/runner_spec.rb +672 -0
- data/spec/spec_helper.rb +79 -0
- data/spec/ui_spec.rb +30 -0
- metadata +183 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
module Commander
|
2
|
+
module Patches
|
3
|
+
module OptionDefaults
|
4
|
+
# I can't remember what this patch does, but I found it in the code
|
5
|
+
# base. It is better if this magic is kept separate
|
6
|
+
def option(*args, &block)
|
7
|
+
default = nil
|
8
|
+
args.delete_if do |v|
|
9
|
+
if v.is_a?(Hash) && v.key?(:default)
|
10
|
+
default = v[:default]
|
11
|
+
true
|
12
|
+
else
|
13
|
+
false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
opt = super
|
17
|
+
opt.tap do |h|
|
18
|
+
h.merge!( { default: default } ) unless default.nil?
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Commander
|
4
|
+
module Patches
|
5
|
+
# An error in the usage of a command; will happen in practise and error
|
6
|
+
# message should be shown along with command usage info.
|
7
|
+
class CommandUsageError < StandardError; end
|
8
|
+
|
9
|
+
module ValidateInputs
|
10
|
+
# This is used to switch the Patch off during the original test of
|
11
|
+
# Commander. It is VERY MUCH a hack but it works
|
12
|
+
PatchEnabled = true
|
13
|
+
|
14
|
+
def call(args = [])
|
15
|
+
return super unless PatchEnabled
|
16
|
+
return super if syntax_parts[0..1] == ['commander', 'help']
|
17
|
+
|
18
|
+
# Use defined syntax to validate how many args this command can be
|
19
|
+
# passed.
|
20
|
+
assert_correct_number_of_args!(args)
|
21
|
+
|
22
|
+
# Invoke original method.
|
23
|
+
super(args)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def assert_correct_number_of_args!(args)
|
29
|
+
return if primary_command_word == 'help'
|
30
|
+
if too_many_args?(args)
|
31
|
+
raise CommandUsageError, "excess arguments for command '#{primary_command_word}'"
|
32
|
+
elsif too_few_args?(args)
|
33
|
+
raise CommandUsageError, "insufficient arguments for command '#{primary_command_word}'"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def syntax_parts
|
38
|
+
@syntax_parts ||= syntax.split.tap do |parts|
|
39
|
+
while part = parts.shift do
|
40
|
+
break if part == primary_command_word || parts.length == 0
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def primary_command_word
|
46
|
+
name.split.last
|
47
|
+
end
|
48
|
+
|
49
|
+
def total_argument_count
|
50
|
+
syntax_parts.length
|
51
|
+
end
|
52
|
+
|
53
|
+
def optional_argument_count
|
54
|
+
syntax_parts.select do |part|
|
55
|
+
part[0] == '[' && part[-1] == ']'
|
56
|
+
end.length
|
57
|
+
end
|
58
|
+
|
59
|
+
def variable_arg?
|
60
|
+
syntax_parts.any? {|part| part[-4..-1] == '...]' || part[-3..-1] == '...'}
|
61
|
+
end
|
62
|
+
|
63
|
+
def required_argument_count
|
64
|
+
total_argument_count - optional_argument_count
|
65
|
+
end
|
66
|
+
|
67
|
+
def too_many_args?(args)
|
68
|
+
!variable_arg? && args.length > total_argument_count
|
69
|
+
end
|
70
|
+
|
71
|
+
def too_few_args?(args)
|
72
|
+
args.length < required_argument_count
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,493 @@
|
|
1
|
+
module Commander
|
2
|
+
class Runner
|
3
|
+
#--
|
4
|
+
# Exceptions
|
5
|
+
#++
|
6
|
+
|
7
|
+
class CommandError < StandardError; end
|
8
|
+
class InvalidCommandError < CommandError; end
|
9
|
+
|
10
|
+
##
|
11
|
+
# Array of commands.
|
12
|
+
|
13
|
+
attr_reader :commands
|
14
|
+
|
15
|
+
##
|
16
|
+
# Global options.
|
17
|
+
|
18
|
+
attr_reader :options
|
19
|
+
|
20
|
+
##
|
21
|
+
# Hash of help formatter aliases.
|
22
|
+
|
23
|
+
attr_reader :help_formatter_aliases
|
24
|
+
|
25
|
+
##
|
26
|
+
# Initialize a new command runner. Optionally
|
27
|
+
# supplying _args_ for mocking, or arbitrary usage.
|
28
|
+
|
29
|
+
def initialize(args = ARGV)
|
30
|
+
@args, @commands, @aliases, @options = args, {}, {}, []
|
31
|
+
@help_formatter_aliases = help_formatter_alias_defaults
|
32
|
+
@program = program_defaults
|
33
|
+
@always_trace = false
|
34
|
+
@never_trace = false
|
35
|
+
@silent_trace = false
|
36
|
+
@error_handler = nil
|
37
|
+
create_default_commands
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Return singleton Runner instance.
|
42
|
+
|
43
|
+
def self.instance
|
44
|
+
@singleton ||= new
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Run command parsing and execution process.
|
49
|
+
|
50
|
+
def run!
|
51
|
+
trace = @always_trace || false
|
52
|
+
require_program :version, :description
|
53
|
+
trap('INT') { abort program(:int_message) } if program(:int_message)
|
54
|
+
trap('INT') { program(:int_block).call } if program(:int_block)
|
55
|
+
global_option('-h', '--help', 'Display help documentation') do
|
56
|
+
args = @args - %w(-h --help)
|
57
|
+
command(:help).run(*args)
|
58
|
+
return
|
59
|
+
end
|
60
|
+
global_option('--version', 'Display version information') do
|
61
|
+
say version
|
62
|
+
return
|
63
|
+
end
|
64
|
+
global_option('--trace', 'Display backtrace when an error occurs') { trace = true } unless @never_trace || @always_trace
|
65
|
+
parse_global_options
|
66
|
+
remove_global_options options, @args
|
67
|
+
if trace
|
68
|
+
run_active_command
|
69
|
+
else
|
70
|
+
begin
|
71
|
+
run_active_command
|
72
|
+
rescue InvalidCommandError => e
|
73
|
+
error_handler&.call(e) ||
|
74
|
+
abort("#{e}. Use --help for more information")
|
75
|
+
rescue \
|
76
|
+
OptionParser::InvalidOption,
|
77
|
+
OptionParser::InvalidArgument,
|
78
|
+
OptionParser::MissingArgument => e
|
79
|
+
error_handler&.call(e) ||
|
80
|
+
abort(e.to_s)
|
81
|
+
rescue => e
|
82
|
+
error_handler&.call(e) ||
|
83
|
+
if @never_trace || @silent_trace
|
84
|
+
abort("error: #{e}.")
|
85
|
+
else
|
86
|
+
abort("error: #{e}. Use --trace to view backtrace")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# Return program version.
|
94
|
+
|
95
|
+
def version
|
96
|
+
format('%s %s', program(:name), program(:version))
|
97
|
+
end
|
98
|
+
|
99
|
+
##
|
100
|
+
# Enable tracing on all executions (bypasses --trace)
|
101
|
+
|
102
|
+
def always_trace!
|
103
|
+
@always_trace = true
|
104
|
+
@never_trace = false
|
105
|
+
@silent_trace = false
|
106
|
+
end
|
107
|
+
|
108
|
+
##
|
109
|
+
# Hide the trace option from the help menus and don't add it as a global option
|
110
|
+
|
111
|
+
def never_trace!
|
112
|
+
@always_trace = false
|
113
|
+
@never_trace = true
|
114
|
+
@silent_trace = false
|
115
|
+
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# Includes the trace option in the help but not in the error message
|
119
|
+
|
120
|
+
def silent_trace!
|
121
|
+
@always_trace = false
|
122
|
+
@never_trace = false
|
123
|
+
@silent_trace = true
|
124
|
+
end
|
125
|
+
|
126
|
+
##
|
127
|
+
# Set a handler to be used for advanced exception handling
|
128
|
+
|
129
|
+
def error_handler(&block)
|
130
|
+
@error_handler = block if block
|
131
|
+
@error_handler
|
132
|
+
end
|
133
|
+
|
134
|
+
##
|
135
|
+
# Assign program information.
|
136
|
+
#
|
137
|
+
# === Examples
|
138
|
+
#
|
139
|
+
# # Set data
|
140
|
+
# program :name, 'Commander'
|
141
|
+
# program :version, Commander::VERSION
|
142
|
+
# program :description, 'Commander utility program.'
|
143
|
+
# program :help, 'Copyright', '2008 TJ Holowaychuk'
|
144
|
+
# program :help, 'Anything', 'You want'
|
145
|
+
# program :int_message 'Bye bye!'
|
146
|
+
# program :help_formatter, :compact
|
147
|
+
# program :help_formatter, Commander::HelpFormatter::TerminalCompact
|
148
|
+
#
|
149
|
+
# # Get data
|
150
|
+
# program :name # => 'Commander'
|
151
|
+
#
|
152
|
+
# === Keys
|
153
|
+
#
|
154
|
+
# :version (required) Program version triple, ex: '0.0.1'
|
155
|
+
# :description (required) Program description
|
156
|
+
# :name Program name, defaults to basename of executable
|
157
|
+
# :help_formatter Defaults to Commander::HelpFormatter::Terminal
|
158
|
+
# :help Allows addition of arbitrary global help blocks
|
159
|
+
# :help_paging Flag for toggling help paging
|
160
|
+
# :int_message Message to display when interrupted (CTRL + C)
|
161
|
+
#
|
162
|
+
|
163
|
+
def program(key, *args, &block)
|
164
|
+
if key == :help && !args.empty?
|
165
|
+
@program[:help] ||= {}
|
166
|
+
@program[:help][args.first] = args.at(1)
|
167
|
+
elsif key == :help_formatter && !args.empty?
|
168
|
+
@program[key] = (@help_formatter_aliases[args.first] || args.first)
|
169
|
+
elsif block
|
170
|
+
@program[key] = block
|
171
|
+
else
|
172
|
+
unless args.empty?
|
173
|
+
@program[key] = args.count == 1 ? args[0] : args
|
174
|
+
end
|
175
|
+
@program[key]
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
##
|
180
|
+
# Creates and yields a command instance when a block is passed.
|
181
|
+
# Otherwise attempts to return the command, raising InvalidCommandError when
|
182
|
+
# it does not exist.
|
183
|
+
#
|
184
|
+
# === Examples
|
185
|
+
#
|
186
|
+
# command :my_command do |c|
|
187
|
+
# c.when_called do |args|
|
188
|
+
# # Code
|
189
|
+
# end
|
190
|
+
# end
|
191
|
+
#
|
192
|
+
|
193
|
+
def command(name, &block)
|
194
|
+
yield add_command(Commander::Command.new(name)) if block
|
195
|
+
@commands[name.to_s]
|
196
|
+
end
|
197
|
+
|
198
|
+
##
|
199
|
+
# Add a global option; follows the same syntax as Command#option
|
200
|
+
# This would be used for switches such as --version, --trace, etc.
|
201
|
+
|
202
|
+
def global_option(*args, &block)
|
203
|
+
switches, description = Runner.separate_switches_from_description(*args)
|
204
|
+
@options << {
|
205
|
+
args: args,
|
206
|
+
proc: block,
|
207
|
+
switches: switches,
|
208
|
+
description: description,
|
209
|
+
}
|
210
|
+
end
|
211
|
+
|
212
|
+
##
|
213
|
+
# Alias command _name_ with _alias_name_. Optionally _args_ may be passed
|
214
|
+
# as if they were being passed straight to the original command via the command-line.
|
215
|
+
|
216
|
+
def alias_command(alias_name, name, *args)
|
217
|
+
@commands[alias_name.to_s] = command name
|
218
|
+
@aliases[alias_name.to_s] = args
|
219
|
+
end
|
220
|
+
|
221
|
+
##
|
222
|
+
# Default command _name_ to be used when no other
|
223
|
+
# command is found in the arguments.
|
224
|
+
|
225
|
+
def default_command(name)
|
226
|
+
@default_command = name
|
227
|
+
end
|
228
|
+
|
229
|
+
##
|
230
|
+
# Add a command object to this runner.
|
231
|
+
|
232
|
+
def add_command(command)
|
233
|
+
@commands[command.name] = command
|
234
|
+
end
|
235
|
+
|
236
|
+
##
|
237
|
+
# Check if command _name_ is an alias.
|
238
|
+
|
239
|
+
def alias?(name)
|
240
|
+
@aliases.include? name.to_s
|
241
|
+
end
|
242
|
+
|
243
|
+
##
|
244
|
+
# Check if a command _name_ exists.
|
245
|
+
|
246
|
+
def command_exists?(name)
|
247
|
+
@commands[name.to_s]
|
248
|
+
end
|
249
|
+
|
250
|
+
#:stopdoc:
|
251
|
+
|
252
|
+
##
|
253
|
+
# Get active command within arguments passed to this runner.
|
254
|
+
|
255
|
+
def active_command
|
256
|
+
@__active_command ||= command(command_name_from_args)
|
257
|
+
end
|
258
|
+
|
259
|
+
##
|
260
|
+
# Attempts to locate a command name from within the arguments.
|
261
|
+
# Supports multi-word commands, using the largest possible match.
|
262
|
+
|
263
|
+
def command_name_from_args
|
264
|
+
@__command_name_from_args ||= (valid_command_names_from(*@args.dup).sort.last || @default_command)
|
265
|
+
end
|
266
|
+
|
267
|
+
##
|
268
|
+
# Returns array of valid command names found within _args_.
|
269
|
+
|
270
|
+
def valid_command_names_from(*args)
|
271
|
+
arg_string = args.delete_if { |value| value =~ /^-/ }.join ' '
|
272
|
+
commands.keys.find_all { |name| name if arg_string =~ /^#{name}\b/ }
|
273
|
+
end
|
274
|
+
|
275
|
+
##
|
276
|
+
# Help formatter instance.
|
277
|
+
|
278
|
+
def help_formatter
|
279
|
+
@__help_formatter ||= program(:help_formatter).new self
|
280
|
+
end
|
281
|
+
|
282
|
+
##
|
283
|
+
# Return arguments without the command name.
|
284
|
+
|
285
|
+
def args_without_command_name
|
286
|
+
removed = []
|
287
|
+
parts = command_name_from_args.split rescue []
|
288
|
+
@args.dup.delete_if do |arg|
|
289
|
+
removed << arg if parts.include?(arg) && !removed.include?(arg)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
##
|
294
|
+
# Returns hash of help formatter alias defaults.
|
295
|
+
|
296
|
+
def help_formatter_alias_defaults
|
297
|
+
{
|
298
|
+
compact: HelpFormatter::TerminalCompact,
|
299
|
+
}
|
300
|
+
end
|
301
|
+
|
302
|
+
##
|
303
|
+
# Returns hash of program defaults.
|
304
|
+
|
305
|
+
def program_defaults
|
306
|
+
{
|
307
|
+
help_formatter: HelpFormatter::Terminal,
|
308
|
+
name: File.basename($PROGRAM_NAME),
|
309
|
+
help_paging: true,
|
310
|
+
}
|
311
|
+
end
|
312
|
+
|
313
|
+
##
|
314
|
+
# Limit commands to those which are subcommands of the one that is active
|
315
|
+
def limit_commands_to_subcommands(command)
|
316
|
+
commands.reject! { |k, v|
|
317
|
+
(k.to_s == command.name) ? true : !k.to_s.start_with?("#{command.name} ")
|
318
|
+
}
|
319
|
+
end
|
320
|
+
|
321
|
+
##
|
322
|
+
# Creates default commands such as 'help' which is
|
323
|
+
# essentially the same as using the --help switch.
|
324
|
+
|
325
|
+
def create_default_commands
|
326
|
+
command :help do |c|
|
327
|
+
c.syntax = 'commander help [command]'
|
328
|
+
c.description = 'Display global or [command] help documentation'
|
329
|
+
c.example 'Display global help', 'command help'
|
330
|
+
c.example "Display help for 'foo'", 'command help foo'
|
331
|
+
c.when_called do |args, _options|
|
332
|
+
UI.enable_paging if program(:help_paging)
|
333
|
+
@help_commands = @commands.dup
|
334
|
+
if args.empty?
|
335
|
+
@help_options = @options.reject {|o| o[:switches].first == '--trace'}
|
336
|
+
@help_commands.reject! { |k, v| !!v.hidden }
|
337
|
+
say help_formatter.render
|
338
|
+
else
|
339
|
+
command = command args.join(' ')
|
340
|
+
begin
|
341
|
+
require_valid_command command
|
342
|
+
rescue InvalidCommandError => e
|
343
|
+
error_handler&.call(e) ||
|
344
|
+
abort("#{e}. Use --help for more information")
|
345
|
+
end
|
346
|
+
if command.sub_command_group?
|
347
|
+
limit_commands_to_subcommands(command)
|
348
|
+
say help_formatter.render_subcommand(command)
|
349
|
+
else
|
350
|
+
say help_formatter.render_command(command)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
##
|
358
|
+
# Raises InvalidCommandError when a _command_ is not found.
|
359
|
+
|
360
|
+
def require_valid_command(command = active_command)
|
361
|
+
fail InvalidCommandError, 'invalid command', caller if command.nil?
|
362
|
+
end
|
363
|
+
|
364
|
+
##
|
365
|
+
# Removes global _options_ from _args_. This prevents an invalid
|
366
|
+
# option error from occurring when options are parsed
|
367
|
+
# again for the command.
|
368
|
+
|
369
|
+
def remove_global_options(options, args)
|
370
|
+
# TODO: refactor with flipflop, please TJ ! have time to refactor me !
|
371
|
+
options.each do |option|
|
372
|
+
switches = option[:switches].dup
|
373
|
+
next if switches.empty?
|
374
|
+
|
375
|
+
if (switch_has_arg = switches.any? { |s| s =~ /[ =]/ })
|
376
|
+
switches.map! { |s| s[0, s.index('=') || s.index(' ') || s.length] }
|
377
|
+
end
|
378
|
+
|
379
|
+
switches = expand_optionally_negative_switches(switches)
|
380
|
+
|
381
|
+
past_switch, arg_removed = false, false
|
382
|
+
args.delete_if do |arg|
|
383
|
+
if switches.any? { |s| s == arg }
|
384
|
+
arg_removed = !switch_has_arg
|
385
|
+
past_switch = true
|
386
|
+
elsif past_switch && !arg_removed && arg !~ /^-/
|
387
|
+
arg_removed = true
|
388
|
+
else
|
389
|
+
arg_removed = true
|
390
|
+
false
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
# expand switches of the style '--[no-]blah' into both their
|
397
|
+
# '--blah' and '--no-blah' variants, so that they can be
|
398
|
+
# properly detected and removed
|
399
|
+
def expand_optionally_negative_switches(switches)
|
400
|
+
switches.reduce([]) do |memo, val|
|
401
|
+
if val =~ /\[no-\]/
|
402
|
+
memo << val.gsub(/\[no-\]/, '')
|
403
|
+
memo << val.gsub(/\[no-\]/, 'no-')
|
404
|
+
else
|
405
|
+
memo << val
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
##
|
411
|
+
# Parse global command options.
|
412
|
+
|
413
|
+
def parse_global_options
|
414
|
+
parser = options.inject(OptionParser.new) do |options, option|
|
415
|
+
options.on(*option[:args], &global_option_proc(option[:switches], &option[:proc]))
|
416
|
+
end
|
417
|
+
|
418
|
+
options = @args.dup
|
419
|
+
begin
|
420
|
+
parser.parse!(options)
|
421
|
+
rescue OptionParser::InvalidOption => e
|
422
|
+
# Remove the offending args and retry.
|
423
|
+
options = options.reject { |o| e.args.include?(o) }
|
424
|
+
retry
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
##
|
429
|
+
# Returns a proc allowing for commands to inherit global options.
|
430
|
+
# This functionality works whether a block is present for the global
|
431
|
+
# option or not, so simple switches such as --verbose can be used
|
432
|
+
# without a block, and used throughout all commands.
|
433
|
+
|
434
|
+
def global_option_proc(switches, &block)
|
435
|
+
lambda do |value|
|
436
|
+
unless active_command.nil?
|
437
|
+
active_command.proxy_options << [Runner.switch_to_sym(switches.last), value]
|
438
|
+
end
|
439
|
+
yield value if block && !value.nil?
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
##
|
444
|
+
# Raises a CommandError when the program any of the _keys_ are not present, or empty.
|
445
|
+
|
446
|
+
def require_program(*keys)
|
447
|
+
keys.each do |key|
|
448
|
+
fail CommandError, "program #{key} required" if program(key).nil? || program(key).empty?
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
##
|
453
|
+
# Return switches and description separated from the _args_ passed.
|
454
|
+
|
455
|
+
def self.separate_switches_from_description(*args)
|
456
|
+
switches = args.find_all { |arg| arg.to_s =~ /^-/ }
|
457
|
+
description = args.last if args.last.is_a?(String) && !args.last.match(/^-/)
|
458
|
+
[switches, description]
|
459
|
+
end
|
460
|
+
|
461
|
+
##
|
462
|
+
# Attempts to generate a method name symbol from +switch+.
|
463
|
+
# For example:
|
464
|
+
#
|
465
|
+
# -h # => :h
|
466
|
+
# --trace # => :trace
|
467
|
+
# --some-switch # => :some_switch
|
468
|
+
# --[no-]feature # => :feature
|
469
|
+
# --file FILE # => :file
|
470
|
+
# --list of,things # => :list
|
471
|
+
#
|
472
|
+
|
473
|
+
def self.switch_to_sym(switch)
|
474
|
+
switch.scan(/[\-\]](\w+)/).join('_').to_sym rescue nil
|
475
|
+
end
|
476
|
+
|
477
|
+
##
|
478
|
+
# Run the active command.
|
479
|
+
|
480
|
+
def run_active_command
|
481
|
+
require_valid_command
|
482
|
+
if alias? command_name_from_args
|
483
|
+
active_command.run(*(@aliases[command_name_from_args.to_s] + args_without_command_name))
|
484
|
+
else
|
485
|
+
active_command.run(*args_without_command_name)
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
def say(*args) #:nodoc:
|
490
|
+
$terminal.say(*args)
|
491
|
+
end
|
492
|
+
end
|
493
|
+
end
|