murano-cli-commander 4.4.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +44 -0
  5. data/.rubocop_todo.yml +77 -0
  6. data/.travis.yml +14 -0
  7. data/DEVELOPMENT +15 -0
  8. data/Gemfile +3 -0
  9. data/History.rdoc +446 -0
  10. data/LICENSE +22 -0
  11. data/Manifest +38 -0
  12. data/README.md +475 -0
  13. data/Rakefile +13 -0
  14. data/bin/murano-cli-commander +104 -0
  15. data/lib/murano-cli-commander.rb +35 -0
  16. data/lib/murano-cli-commander/blank.rb +7 -0
  17. data/lib/murano-cli-commander/command.rb +214 -0
  18. data/lib/murano-cli-commander/configure.rb +14 -0
  19. data/lib/murano-cli-commander/core_ext.rb +2 -0
  20. data/lib/murano-cli-commander/core_ext/array.rb +24 -0
  21. data/lib/murano-cli-commander/core_ext/object.rb +8 -0
  22. data/lib/murano-cli-commander/delegates.rb +25 -0
  23. data/lib/murano-cli-commander/help_formatters.rb +49 -0
  24. data/lib/murano-cli-commander/help_formatters/base.rb +24 -0
  25. data/lib/murano-cli-commander/help_formatters/terminal.rb +19 -0
  26. data/lib/murano-cli-commander/help_formatters/terminal/command_help.erb +35 -0
  27. data/lib/murano-cli-commander/help_formatters/terminal/help.erb +36 -0
  28. data/lib/murano-cli-commander/help_formatters/terminal_compact.rb +11 -0
  29. data/lib/murano-cli-commander/help_formatters/terminal_compact/command_help.erb +27 -0
  30. data/lib/murano-cli-commander/help_formatters/terminal_compact/help.erb +29 -0
  31. data/lib/murano-cli-commander/import.rb +5 -0
  32. data/lib/murano-cli-commander/methods.rb +11 -0
  33. data/lib/murano-cli-commander/platform.rb +7 -0
  34. data/lib/murano-cli-commander/runner.rb +455 -0
  35. data/lib/murano-cli-commander/user_interaction.rb +551 -0
  36. data/lib/murano-cli-commander/version.rb +3 -0
  37. data/murano-cli-commander.gemspec +36 -0
  38. data/spec/command_spec.rb +169 -0
  39. data/spec/configure_spec.rb +37 -0
  40. data/spec/core_ext/array_spec.rb +18 -0
  41. data/spec/core_ext/object_spec.rb +19 -0
  42. data/spec/help_formatters/terminal_compact_spec.rb +69 -0
  43. data/spec/help_formatters/terminal_spec.rb +67 -0
  44. data/spec/methods_spec.rb +61 -0
  45. data/spec/runner_spec.rb +646 -0
  46. data/spec/spec_helper.rb +78 -0
  47. data/spec/ui_spec.rb +30 -0
  48. metadata +163 -0
@@ -0,0 +1,49 @@
1
+ module Commander
2
+ module HelpFormatter
3
+ autoload :Base, 'murano-cli-commander/help_formatters/base'
4
+ autoload :Terminal, 'murano-cli-commander/help_formatters/terminal'
5
+ autoload :TerminalCompact, 'murano-cli-commander/help_formatters/terminal_compact'
6
+
7
+ class Context
8
+ def initialize(target)
9
+ @target = target
10
+ end
11
+
12
+ def get_binding
13
+ @target.instance_eval { binding }.tap do |bind|
14
+ decorate_binding(bind)
15
+ end
16
+ end
17
+
18
+ # No-op, override in subclasses.
19
+ def decorate_binding(_bind)
20
+ end
21
+ end
22
+
23
+ class ProgramContext < Context
24
+ def decorate_binding(bind)
25
+ bind.eval("max_command_length = #{max_command_length(bind)}")
26
+ bind.eval("max_aliases_length = #{max_aliases_length(bind)}")
27
+ end
28
+
29
+ def max_command_length(bind)
30
+ max_key_length(bind.eval('@commands'))
31
+ end
32
+
33
+ def max_aliases_length(bind)
34
+ max_key_length(bind.eval('@aliases'))
35
+ end
36
+
37
+ def max_key_length(hash, default = 20)
38
+ longest = hash.keys.max_by(&:size)
39
+ longest ? longest.size : default
40
+ end
41
+ end
42
+
43
+ module_function
44
+
45
+ def indent(amount, text)
46
+ text.to_s.gsub("\n", "\n" + (' ' * amount))
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,24 @@
1
+ module Commander
2
+ ##
3
+ # = Help Formatter
4
+ #
5
+ # Commander's help formatters control the output when
6
+ # either the help command, or --help switch are called.
7
+ # The default formatter is Commander::HelpFormatter::Terminal.
8
+
9
+ module HelpFormatter
10
+ class Base
11
+ def initialize(runner)
12
+ @runner = runner
13
+ end
14
+
15
+ def render
16
+ 'Implement global help here'
17
+ end
18
+
19
+ def render_command(command)
20
+ "Implement help for #{command.name} here"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ require 'erb'
2
+
3
+ module Commander
4
+ module HelpFormatter
5
+ class Terminal < Base
6
+ def render
7
+ template(:help).result(ProgramContext.new(@runner).get_binding)
8
+ end
9
+
10
+ def render_command(command)
11
+ template(:command_help).result(Context.new(command).get_binding)
12
+ end
13
+
14
+ def template(name)
15
+ ERB.new(File.read(File.join(File.dirname(__FILE__), 'terminal', "#{name}.erb")), nil, '-')
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,35 @@
1
+
2
+ <%= $terminal.color "NAME", :bold %>:
3
+
4
+ <%= @name %>
5
+ <% if @syntax -%>
6
+
7
+ <%= $terminal.color "SYNOPSIS", :bold %>:
8
+
9
+ <%= @syntax -%>
10
+
11
+ <% end -%>
12
+
13
+ <%= $terminal.color "DESCRIPTION", :bold %>:
14
+
15
+ <%= Commander::HelpFormatter.indent 4, (@description || @summary || 'No description.') -%>
16
+
17
+ <% unless @examples.empty? -%>
18
+
19
+ <%= $terminal.color "EXAMPLES", :bold %>:
20
+ <% for description, command in @examples -%>
21
+
22
+ # <%= description %>
23
+ <%= command %>
24
+ <% end -%>
25
+ <% end -%>
26
+ <% unless @options.empty? -%>
27
+
28
+ <%= $terminal.color "OPTIONS", :bold %>:
29
+ <% for option in @options -%>
30
+
31
+ <%= option[:switches].join ', ' %>
32
+ <%= Commander::HelpFormatter.indent 8, option[:description] %>
33
+ <% end -%>
34
+ <% end -%>
35
+
@@ -0,0 +1,36 @@
1
+ <%= $terminal.color "NAME", :bold %>:
2
+
3
+ <%= program :name %>
4
+
5
+ <%= $terminal.color "DESCRIPTION", :bold %>:
6
+
7
+ <%= Commander::HelpFormatter.indent 4, program(:description) %>
8
+
9
+ <%= $terminal.color "COMMANDS", :bold %>:
10
+ <% for name, command in @commands.sort -%>
11
+ <% unless alias? name %>
12
+ <%= "%-#{max_command_length}s %s" % [command.name, command.summary || command.description] -%>
13
+ <% end -%>
14
+ <% end %>
15
+ <% unless @aliases.empty? %>
16
+ <%= $terminal.color "ALIASES", :bold %>:
17
+ <% for alias_name, args in @aliases.sort %>
18
+ <%= "%-#{max_aliases_length}s %s %s" % [alias_name, command(alias_name).name, args.join(' ')] -%>
19
+ <% end %>
20
+ <% end %>
21
+ <% unless @options.empty? -%>
22
+ <%= $terminal.color "GLOBAL OPTIONS", :bold %>:
23
+ <% for option in @options -%>
24
+
25
+ <%= option[:switches].join ', ' %>
26
+ <%= option[:description] %>
27
+ <% end -%>
28
+ <% end -%>
29
+ <% if program :help -%>
30
+ <% for title, body in program(:help) %>
31
+ <%= $terminal.color title.to_s.upcase, :bold %>:
32
+
33
+ <%= body %>
34
+ <% end -%>
35
+ <% end -%>
36
+
@@ -0,0 +1,11 @@
1
+ require 'erb'
2
+
3
+ module Commander
4
+ module HelpFormatter
5
+ class TerminalCompact < Terminal
6
+ def template(name)
7
+ ERB.new(File.read(File.join(File.dirname(__FILE__), 'terminal_compact', "#{name}.erb")), nil, '-')
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,27 @@
1
+
2
+ <%= @name %>
3
+ <% if @syntax -%>
4
+
5
+ Usage: <%= @syntax %>
6
+ <% end -%>
7
+ <% if @description || @summary -%>
8
+
9
+ <%= @description || @summary %>
10
+ <% end -%>
11
+ <% unless @examples.empty? -%>
12
+
13
+ Examples:
14
+ <% for description, command in @examples -%>
15
+
16
+ # <%= description %>
17
+ <%= command %>
18
+ <% end -%>
19
+ <% end -%>
20
+ <% unless @options.empty? -%>
21
+
22
+ Options:
23
+ <% for option in @options -%>
24
+ <%= "%-20s %s" % [option[:switches].join(', '), option[:description]] %>
25
+ <% end -%>
26
+ <% end -%>
27
+
@@ -0,0 +1,29 @@
1
+ <%= program :name %>
2
+
3
+ <%= program :description %>
4
+
5
+ Commands:
6
+ <% for name, command in @commands.sort -%>
7
+ <% unless alias? name -%>
8
+ <%= "%-#{max_command_length}s %s" % [command.name, command.summary || command.description] %>
9
+ <% end -%>
10
+ <% end -%>
11
+ <% unless @aliases.empty? %>
12
+ Aliases:
13
+ <% for alias_name, args in @aliases.sort -%>
14
+ <%= "%-#{max_aliases_length}s %s %s" % [alias_name, command(alias_name).name, args.join(' ')] %>
15
+ <% end -%>
16
+ <% end %>
17
+ <% unless @options.empty? -%>
18
+ Global Options:
19
+ <% for option in @options -%>
20
+ <%= "%-20s %s" % [option[:switches].join(', '), option[:description]] -%>
21
+ <% end -%>
22
+ <% end -%>
23
+ <% if program :help -%>
24
+ <% for title, body in program(:help) %>
25
+ <%= title %>:
26
+ <%= body %>
27
+ <% end %>
28
+ <% end -%>
29
+
@@ -0,0 +1,5 @@
1
+ require 'murano-cli-commander'
2
+
3
+ include Commander::Methods
4
+
5
+ at_exit { run! }
@@ -0,0 +1,11 @@
1
+ module Commander
2
+ module Methods
3
+ include Commander::UI
4
+ include Commander::UI::AskForClass
5
+ include Commander::Delegates
6
+
7
+ if $stdin.tty? && (cols = $terminal.output_cols) >= 40
8
+ $terminal.wrap_at = cols - 5
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module Commander
2
+ module Platform
3
+ def self.jruby?
4
+ defined?(RUBY_ENGINE) && (RUBY_ENGINE == 'jruby')
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,455 @@
1
+ require 'optparse'
2
+
3
+ module Commander
4
+ class Runner
5
+ #--
6
+ # Exceptions
7
+ #++
8
+
9
+ class CommandError < StandardError; end
10
+ class InvalidCommandError < CommandError; end
11
+
12
+ ##
13
+ # Array of commands.
14
+
15
+ attr_reader :commands
16
+
17
+ ##
18
+ # Global options.
19
+
20
+ attr_reader :options
21
+
22
+ ##
23
+ # Hash of help formatter aliases.
24
+
25
+ attr_reader :help_formatter_aliases
26
+
27
+ ##
28
+ # Initialize a new command runner. Optionally
29
+ # supplying _args_ for mocking, or arbitrary usage.
30
+
31
+ def initialize(args = ARGV)
32
+ @args, @commands, @aliases, @options = args, {}, {}, []
33
+ @help_formatter_aliases = help_formatter_alias_defaults
34
+ @program = program_defaults
35
+ @always_trace = false
36
+ @never_trace = false
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('-v', '--version', 'Display version information') do
61
+ say version
62
+ return
63
+ end
64
+ global_option('-t', '--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
+ abort "#{e}. Use --help for more information"
74
+ rescue \
75
+ OptionParser::InvalidOption,
76
+ OptionParser::InvalidArgument,
77
+ OptionParser::MissingArgument => e
78
+ abort e.to_s
79
+ rescue => e
80
+ if @never_trace
81
+ abort "error: #{e}."
82
+ else
83
+ abort "error: #{e}. Use --trace to view backtrace"
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ ##
90
+ # Return program version.
91
+
92
+ def version
93
+ format('%s %s', program(:name), program(:version))
94
+ end
95
+
96
+ ##
97
+ # Enable tracing on all executions (bypasses --trace)
98
+
99
+ def always_trace!
100
+ @always_trace = true
101
+ @never_trace = false
102
+ end
103
+
104
+ ##
105
+ # Hide the trace option from the help menus and don't add it as a global option
106
+
107
+ def never_trace!
108
+ @never_trace = true
109
+ @always_trace = false
110
+ end
111
+
112
+ ##
113
+ # Assign program information.
114
+ #
115
+ # === Examples
116
+ #
117
+ # # Set data
118
+ # program :name, 'Commander'
119
+ # program :version, Commander::VERSION
120
+ # program :description, 'Commander utility program.'
121
+ # program :help, 'Copyright', '2008 TJ Holowaychuk'
122
+ # program :help, 'Anything', 'You want'
123
+ # program :int_message 'Bye bye!'
124
+ # program :help_formatter, :compact
125
+ # program :help_formatter, Commander::HelpFormatter::TerminalCompact
126
+ #
127
+ # # Get data
128
+ # program :name # => 'Commander'
129
+ #
130
+ # === Keys
131
+ #
132
+ # :version (required) Program version triple, ex: '0.0.1'
133
+ # :description (required) Program description
134
+ # :name Program name, defaults to basename of executable
135
+ # :help_formatter Defaults to Commander::HelpFormatter::Terminal
136
+ # :help Allows addition of arbitrary global help blocks
137
+ # :help_paging Flag for toggling help paging
138
+ # :int_message Message to display when interrupted (CTRL + C)
139
+ #
140
+
141
+ def program(key, *args, &block)
142
+ if key == :help && !args.empty?
143
+ @program[:help] ||= {}
144
+ @program[:help][args.first] = args.at(1)
145
+ elsif key == :help_formatter && !args.empty?
146
+ @program[key] = (@help_formatter_aliases[args.first] || args.first)
147
+ elsif block
148
+ @program[key] = block
149
+ else
150
+ unless args.empty?
151
+ @program[key] = args.count == 1 ? args[0] : args
152
+ end
153
+ @program[key]
154
+ end
155
+ end
156
+
157
+ ##
158
+ # Creates and yields a command instance when a block is passed.
159
+ # Otherwise attempts to return the command, raising InvalidCommandError when
160
+ # it does not exist.
161
+ #
162
+ # === Examples
163
+ #
164
+ # command :my_command do |c|
165
+ # c.when_called do |args|
166
+ # # Code
167
+ # end
168
+ # end
169
+ #
170
+
171
+ def command(name, &block)
172
+ yield add_command(Commander::Command.new(name)) if block
173
+ @commands[name.to_s]
174
+ end
175
+
176
+ ##
177
+ # Add a global option; follows the same syntax as Command#option
178
+ # This would be used for switches such as --version, --trace, etc.
179
+
180
+ def global_option(*args, &block)
181
+ switches, description = Runner.separate_switches_from_description(*args)
182
+ @options << {
183
+ args: args,
184
+ proc: block,
185
+ switches: switches,
186
+ description: description,
187
+ }
188
+ end
189
+
190
+ ##
191
+ # Alias command _name_ with _alias_name_. Optionally _args_ may be passed
192
+ # as if they were being passed straight to the original command via the command-line.
193
+
194
+ def alias_command(alias_name, name, *args)
195
+ @commands[alias_name.to_s] = command name
196
+ @aliases[alias_name.to_s] = args
197
+ end
198
+
199
+ ##
200
+ # Default command _name_ to be used when no other
201
+ # command is found in the arguments.
202
+
203
+ def default_command(name)
204
+ @default_command = name
205
+ end
206
+
207
+ ##
208
+ # Add a command object to this runner.
209
+
210
+ def add_command(command)
211
+ @commands[command.name] = command
212
+ end
213
+
214
+ ##
215
+ # Check if command _name_ is an alias.
216
+
217
+ def alias?(name)
218
+ @aliases.include? name.to_s
219
+ end
220
+
221
+ ##
222
+ # Check if a command _name_ exists.
223
+
224
+ def command_exists?(name)
225
+ @commands[name.to_s]
226
+ end
227
+
228
+ #:stopdoc:
229
+
230
+ ##
231
+ # Get active command within arguments passed to this runner.
232
+
233
+ def active_command
234
+ @__active_command ||= command(command_name_from_args)
235
+ end
236
+
237
+ ##
238
+ # Attempts to locate a command name from within the arguments.
239
+ # Supports multi-word commands, using the largest possible match.
240
+
241
+ def command_name_from_args
242
+ @__command_name_from_args ||= (valid_command_names_from(*@args.dup).sort.last || @default_command)
243
+ end
244
+
245
+ ##
246
+ # Returns array of valid command names found within _args_.
247
+
248
+ def valid_command_names_from(*args)
249
+ arg_string = args.delete_if { |value| value =~ /^-/ }.join ' '
250
+ commands.keys.find_all { |name| name if arg_string =~ /^#{name}\b/ }
251
+ end
252
+
253
+ ##
254
+ # Help formatter instance.
255
+
256
+ def help_formatter
257
+ @__help_formatter ||= program(:help_formatter).new self
258
+ end
259
+
260
+ ##
261
+ # Return arguments without the command name.
262
+
263
+ def args_without_command_name
264
+ removed = []
265
+ parts = command_name_from_args.split rescue []
266
+ @args.dup.delete_if do |arg|
267
+ removed << arg if parts.include?(arg) && !removed.include?(arg)
268
+ end
269
+ end
270
+
271
+ ##
272
+ # Returns hash of help formatter alias defaults.
273
+
274
+ def help_formatter_alias_defaults
275
+ {
276
+ compact: HelpFormatter::TerminalCompact,
277
+ }
278
+ end
279
+
280
+ ##
281
+ # Returns hash of program defaults.
282
+
283
+ def program_defaults
284
+ {
285
+ help_formatter: HelpFormatter::Terminal,
286
+ name: File.basename($PROGRAM_NAME),
287
+ help_paging: true,
288
+ }
289
+ end
290
+
291
+ ##
292
+ # Creates default commands such as 'help' which is
293
+ # essentially the same as using the --help switch.
294
+
295
+ def create_default_commands
296
+ command :help do |c|
297
+ c.syntax = 'murano-cli-commander help [command]'
298
+ c.description = 'Display global or [command] help documentation'
299
+ c.example 'Display global help', 'command help'
300
+ c.example "Display help for 'foo'", 'command help foo'
301
+ c.when_called do |args, _options|
302
+ UI.enable_paging if program(:help_paging)
303
+ if args.empty?
304
+ say help_formatter.render
305
+ else
306
+ command = command args.join(' ')
307
+ begin
308
+ require_valid_command command
309
+ rescue InvalidCommandError => e
310
+ abort "#{e}. Use --help for more information"
311
+ end
312
+ say help_formatter.render_command(command)
313
+ end
314
+ end
315
+ end
316
+ end
317
+
318
+ ##
319
+ # Raises InvalidCommandError when a _command_ is not found.
320
+
321
+ def require_valid_command(command = active_command)
322
+ fail InvalidCommandError, 'invalid command', caller if command.nil?
323
+ end
324
+
325
+ ##
326
+ # Removes global _options_ from _args_. This prevents an invalid
327
+ # option error from occurring when options are parsed
328
+ # again for the command.
329
+
330
+ def remove_global_options(options, args)
331
+ # TODO: refactor with flipflop, please TJ ! have time to refactor me !
332
+ options.each do |option|
333
+ switches = option[:switches].dup
334
+ next if switches.empty?
335
+
336
+ if (switch_has_arg = switches.any? { |s| s =~ /[ =]/ })
337
+ switches.map! { |s| s[0, s.index('=') || s.index(' ') || s.length] }
338
+ end
339
+
340
+ switches = expand_optionally_negative_switches(switches)
341
+
342
+ past_switch, arg_removed = false, false
343
+ args.delete_if do |arg|
344
+ # (lb): If arg is '', this any? block matches the first one it checks.
345
+ if !arg.empty? && switches.any? { |s| s[0, arg.length] == arg }
346
+ arg_removed = !switch_has_arg
347
+ past_switch = true
348
+ elsif past_switch && !arg_removed && arg !~ /^-/
349
+ arg_removed = true
350
+ else
351
+ arg_removed = true
352
+ false
353
+ end
354
+ end
355
+ end
356
+ end
357
+
358
+ # expand switches of the style '--[no-]blah' into both their
359
+ # '--blah' and '--no-blah' variants, so that they can be
360
+ # properly detected and removed
361
+ def expand_optionally_negative_switches(switches)
362
+ switches.reduce([]) do |memo, val|
363
+ if val =~ /\[no-\]/
364
+ memo << val.gsub(/\[no-\]/, '')
365
+ memo << val.gsub(/\[no-\]/, 'no-')
366
+ else
367
+ memo << val
368
+ end
369
+ end
370
+ end
371
+
372
+ ##
373
+ # Parse global command options.
374
+
375
+ def parse_global_options
376
+ parser = options.inject(OptionParser.new) do |options, option|
377
+ options.on(*option[:args], &global_option_proc(option[:switches], &option[:proc]))
378
+ end
379
+
380
+ options = @args.dup
381
+ begin
382
+ parser.parse!(options)
383
+ rescue OptionParser::InvalidOption => e
384
+ # Remove the offending args and retry.
385
+ options = options.reject { |o| e.args.include?(o) }
386
+ retry
387
+ end
388
+ end
389
+
390
+ ##
391
+ # Returns a proc allowing for commands to inherit global options.
392
+ # This functionality works whether a block is present for the global
393
+ # option or not, so simple switches such as --verbose can be used
394
+ # without a block, and used throughout all commands.
395
+
396
+ def global_option_proc(switches, &block)
397
+ lambda do |value|
398
+ unless active_command.nil?
399
+ active_command.proxy_options << [Runner.switch_to_sym(switches.last), value]
400
+ end
401
+ yield value if block && !value.nil?
402
+ end
403
+ end
404
+
405
+ ##
406
+ # Raises a CommandError when the program any of the _keys_ are not present, or empty.
407
+
408
+ def require_program(*keys)
409
+ keys.each do |key|
410
+ fail CommandError, "program #{key} required" if program(key).nil? || program(key).empty?
411
+ end
412
+ end
413
+
414
+ ##
415
+ # Return switches and description separated from the _args_ passed.
416
+
417
+ def self.separate_switches_from_description(*args)
418
+ switches = args.find_all { |arg| arg.to_s =~ /^-/ }
419
+ description = args.last if args.last.is_a?(String) && !args.last.match(/^-/)
420
+ [switches, description]
421
+ end
422
+
423
+ ##
424
+ # Attempts to generate a method name symbol from +switch+.
425
+ # For example:
426
+ #
427
+ # -h # => :h
428
+ # --trace # => :trace
429
+ # --some-switch # => :some_switch
430
+ # --[no-]feature # => :feature
431
+ # --file FILE # => :file
432
+ # --list of,things # => :list
433
+ #
434
+
435
+ def self.switch_to_sym(switch)
436
+ switch.scan(/[\-\]](\w+)/).join('_').to_sym rescue nil
437
+ end
438
+
439
+ ##
440
+ # Run the active command.
441
+
442
+ def run_active_command
443
+ require_valid_command
444
+ if alias? command_name_from_args
445
+ active_command.run(*(@aliases[command_name_from_args.to_s] + args_without_command_name))
446
+ else
447
+ active_command.run(*args_without_command_name)
448
+ end
449
+ end
450
+
451
+ def say(*args) #:nodoc:
452
+ $terminal.say(*args)
453
+ end
454
+ end
455
+ end