murano-cli-commander 4.4.10

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 (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