cmdparse 1.0.5 → 2.0.0

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.
@@ -1,9 +1,9 @@
1
1
  #
2
2
  #--
3
3
  #
4
- # $Id: cmdparse.rb 328 2005-07-05 14:10:02Z thomas $
4
+ # $Id: cmdparse.rb 329 2005-08-14 15:39:05Z thomas $
5
5
  #
6
- # cmdparse: an advanced command line parser using optparse which supports commands
6
+ # cmdparse: advanced command line parser supporting commands
7
7
  # Copyright (C) 2004 Thomas Leitner
8
8
  #
9
9
  # This program is free software; you can redistribute it and/or modify it under the terms of the GNU
@@ -19,380 +19,408 @@
19
19
  #
20
20
  #++
21
21
  #
22
- # Look at the +CommandParser+ class for details and an example.
23
- #
24
22
 
25
- require 'optparse'
26
23
 
27
- # Some extension to the standard option parser class
28
- class OptionParser
24
+ # Namespace module for cmdparse.
25
+ module CmdParse
26
+
27
+ # The version of this cmdparse implemention
28
+ VERSION = [2, 0, 0]
29
+
30
+
31
+ # Base class for all cmdparse errors.
32
+ class ParseError < RuntimeError
33
+
34
+ # Sets the reason for a subclass.
35
+ def self.reason( reason, has_arguments = true )
36
+ (@@reason ||= {})[self] = [reason, has_arguments]
37
+ end
38
+
39
+ # Returns the reason plus the message.
40
+ def message
41
+ data = @@reason[self.class] || ['Unknown error', true]
42
+ data[0] + (data[1] ? ": " + super : '')
43
+ end
29
44
 
30
- if const_defined?( 'Officious' )
31
- Officious.delete( 'version' )
32
- Officious.delete( 'help' )
33
- else
34
- DefaultList.long.delete( 'version' )
35
- DefaultList.long.delete( 'help' )
36
45
  end
37
46
 
38
- # Returns the <tt>@banner</tt> value. Needed because the method <tt>OptionParser#banner</tt> does
39
- # not return the internal value of <tt>@banner</tt> but a modified one.
40
- def get_banner
41
- @banner
47
+ # This error is thrown when an invalid command is encountered.
48
+ class InvalidCommandError < ParseError
49
+ reason 'Invalid command'
42
50
  end
43
51
 
44
- # Returns the <tt>@program_name</tt> value. Needed because the method
45
- # <tt>OptionParser#program_name</tt> does not return the internal value of <tt>@program_name</tt>
46
- # but a modified one.
47
- def get_program_name
48
- @program_name
52
+ # This error is thrown when an invalid argument is encountered.
53
+ class InvalidArgumentError < ParseError
54
+ reason 'Invalid argument'
49
55
  end
50
56
 
51
- end
57
+ # This error is thrown when an invalid option is encountered.
58
+ class InvalidOptionError < ParseError
59
+ reason 'Invalid option'
60
+ end
52
61
 
62
+ # This error is thrown when no command was given and no default command was specified.
63
+ class NoCommandGivenError < ParseError
64
+ reason 'No command given', false
65
+ end
53
66
 
54
- # = CommandParser
55
- #
56
- # == Introduction
57
- #
58
- # +CommandParser+ is a class for analyzing the command line of a program. It uses the standard
59
- # +OptionParser+ class internally for parsing the options and additionally allows the
60
- # specification of commands. Programs which use commands as part of their command line interface
61
- # are, for example, Subversion's +svn+ program and Rubygem's +gem+ program.
62
- #
63
- # == Example
64
- #
65
- # require 'cmdparse'
66
- # require 'ostruct'
67
- #
68
- # class TestCommand < CommandParser::Command
69
- #
70
- # def initialize
71
- # super('test')
72
- # @internal = OpenStruct.new
73
- # @internal.function = nil
74
- # @internal.audible = false
75
- # options.separator "Options:"
76
- # options.on("-t", "--test FUNCTION", "Test only FUNCTION") do |func|
77
- # @internal.function = func
78
- # end
79
- # options.on("-a", "--[no-]audible", "Run audible") { |@internal.audible| }
80
- # end
81
- #
82
- # def description
83
- # "Executes various tests"
84
- # end
85
- #
86
- # def execute( commandParser, args )
87
- # puts "Test: "+ args.inspect
88
- # puts @internal.inspect
89
- # end
90
- #
91
- # end
92
- #
93
- # cmd = CommandParser.new
94
- # cmd.options do |opt|
95
- # opt.program_name = "testProgram"
96
- # opt.version = [0, 1, 0]
97
- # opt.release = "1.0"
98
- # opt.separator "Global options:"
99
- # opt.on("-r", "--require TEST", "Require the TEST")
100
- # opt.on("--delay N", Integer, "Delay test for N seconds before executing")
101
- # end
102
- # cmd.add_command TestCommand.new, true # sets this command as default command
103
- # cmd.add_command CommandParser::HelpCommand.new
104
- # cmd.add_command CommandParser::VersionCommand.new
105
- # cmd.parse!( ARGV )
106
- #
107
- class CommandParser
67
+ # This error is thrown when a command is added to another command which does not support commands.
68
+ class TakesNoCommandError < ParseError
69
+ reason 'This command takes no other commands', false
70
+ end
108
71
 
109
- # The version of the command parser
110
- VERSION = [1, 0, 5]
111
72
 
112
- # This error is thrown when an invalid command is encountered.
113
- class InvalidCommandError < OptionParser::ParseError
114
- const_set( :Reason, 'invalid command'.freeze )
115
- end
73
+ # Base class for all parser wrappers.
74
+ class ParserWrapper
75
+
76
+ # Returns the parser instance for the object and, if a block is a given, yields the instance.
77
+ def instance
78
+ yield @instance if block_given?
79
+ @instance
80
+ end
81
+
82
+ # Parses the arguments in order, i.e. stops at the first non-option argument, and returns all
83
+ # remaining arguments.
84
+ def order( args )
85
+ raise InvalidOptionError.new( args[0] ) if args[0] =~ /^-/
86
+ args
87
+ end
88
+
89
+ # Permutes the arguments so that all options anywhere on the command line are parsed and the
90
+ # remaining non-options are returned.
91
+ def permute( args )
92
+ raise InvalidOptionError.new( args[0] ) if args.any? {|a| a =~ /^-/}
93
+ args
94
+ end
95
+
96
+ # Returns a summary string of the options.
97
+ def summarize
98
+ ""
99
+ end
116
100
 
117
- # This error is thrown when no command was given and no default command was specified.
118
- class NoCommandGivenError < OptionParser::ParseError
119
- const_set( :Reason, 'no command given'.freeze )
120
101
  end
121
102
 
103
+ # Require default option parser wrapper
104
+ require 'cmdparse/wrappers/optparse'
105
+
106
+
122
107
  # Base class for the commands. This class implements all needed methods so that it can be used by
123
- # the +OptionParser+ class.
108
+ # the +CommandParser+ class.
124
109
  class Command
125
110
 
126
111
  # The name of the command
127
112
  attr_reader :name
128
113
 
129
- # The command line options, an instance of +OptionParser+.
130
- attr_reader :options
114
+ # A short description of the command.
115
+ attr_accessor :short_desc
116
+
117
+ # A detailed description of the command
118
+ attr_accessor :description
119
+
120
+ # The wrapper for parsing the command line options.
121
+ attr_accessor :options
122
+
123
+ # Returns the name of the default command.
124
+ attr_reader :default_command
125
+
126
+ # Sets or returns the super command of this command. The super command is either a +Command+
127
+ # instance for normal commands or a +CommandParser+ instance for the root command.
128
+ attr_accessor :super_command
131
129
 
132
- # Initializes the command and assignes it a +name+.
133
- def initialize( name )
130
+ # Returns the list of commands for this command.
131
+ attr_reader :commands
132
+
133
+ # Initializes the command called +name+. The parameter +has_commands+ specifies if this command
134
+ # takes other commands as argument.
135
+ def initialize( name, has_commands )
134
136
  @name = name
135
- @options = OptionParser.new
137
+ @options = ParserWrapper.new
138
+ @has_commands = has_commands
139
+ @commands = {}
140
+ @default_command = nil
141
+ end
142
+
143
+ # Returns +true+ if this command supports sub commands.
144
+ def has_commands?
145
+ @has_commands
146
+ end
147
+
148
+ # Adds a command to the command list if this command takes other commands as argument. If the
149
+ # optional parameter +default+ is true, then this command is used when no command is specified
150
+ # on the command line.
151
+ def add_command( command, default = false )
152
+ raise TakesNoCommandError.new( @name ) if !has_commands?
153
+ @commands[command.name] = command
154
+ @default_command = command.name if default
155
+ command.super_command = self
156
+ command.init
136
157
  end
137
158
 
138
- # For sorting commands by name
159
+ # For sorting commands by name.
139
160
  def <=>( other )
140
161
  @name <=> other.name
141
162
  end
142
163
 
143
- # Should be overridden by specific implementations. This method is called after the command is
144
- # added to a +CommandParser+ instance.
145
- def init( commandParser )
164
+ # Returns the +CommandParser+ instance for this command or +nil+ if this command was not
165
+ # assigned to a +CommandParser+ instance.
166
+ def commandparser
167
+ cmd = super_command
168
+ cmd = cmd.super_command while !cmd.nil? && !cmd.kind_of?( CommandParser )
169
+ cmd
146
170
  end
147
171
 
148
- # Default method for showing the help for the command.
149
- def show_help( commandParser )
150
- @options.program_name = commandParser.options.program_name if @options.get_program_name.nil?
151
- puts "#{@name}: #{description}"
152
- puts usage
153
- puts ""
154
- puts options.summarize
172
+ # Returns a list of super commands, ie.:
173
+ # [command, super_command, super_super_command, ...]
174
+ def super_commands
175
+ cmds = []
176
+ cmd = self
177
+ while !cmd.nil? && !cmd.super_command.kind_of?( CommandParser )
178
+ cmds << cmd
179
+ cmd = cmd.super_command
180
+ end
181
+ cmds
182
+ end
183
+
184
+ # This method is called when the command is added to a +Command+ instance.
185
+ def init; end
186
+
187
+ # Set the given +block+ as execution block. See also: +execute+.
188
+ def set_execution_block( &block )
189
+ @exec_block = block
155
190
  end
156
191
 
157
- # Should be overridden by specific implementations. Defines the description of the command.
158
- def description
159
- '<no description given>'
192
+ # Invokes the block set by +set_execution_block+. This method is called by the +CommandParser+
193
+ # instance if this command was specified on the command line.
194
+ def execute( args )
195
+ @exec_block.call( args )
160
196
  end
161
197
 
162
- # Defines the usage line for the command. Can be overridden if a more specific usage line is needed.
198
+ # Defines the usage line for the command.
163
199
  def usage
164
- "Usage: #{@options.program_name} [global options] #{@name} [options] args"
200
+ tmp = "Usage: #{commandparser.program_name}"
201
+ tmp << " [options] " if !commandparser.options.instance_of?( ParserWrapper )
202
+ tmp << super_commands.reverse.collect do |c|
203
+ t = c.name
204
+ t << " [options]" if !c.options.instance_of?( ParserWrapper )
205
+ t
206
+ end.join(' ')
207
+ tmp << (has_commands? ? " COMMAND [options] [ARGS]" : " [ARGS]")
208
+ end
209
+
210
+ # Default method for showing the help for the command.
211
+ def show_help
212
+ puts "#{@name}: #{short_desc}"
213
+ puts description if description
214
+ puts
215
+ puts usage
216
+ puts
217
+ if has_commands?
218
+ list_commands
219
+ puts
220
+ end
221
+ unless (summary = options.summarize).empty?
222
+ puts summary
223
+ puts
224
+ end
165
225
  end
166
226
 
167
- # Must be overridden by specific implementations. This method is called by the +CommandParser+
168
- # if this command was specified on the command line.
169
- def execute( commandParser, args )
170
- raise NotImplementedError
227
+ #######
228
+ private
229
+ #######
230
+
231
+ def list_commands( level = 1, command = self )
232
+ puts "Available commands:" if level == 1
233
+ command.commands.sort.each do |name, cmd|
234
+ print " "*level + name.ljust( 15 ) + cmd.short_desc.to_s
235
+ print " (=default command)" if name == command.default_command
236
+ print "\n"
237
+ list_commands( level + 1, cmd ) if cmd.has_commands?
238
+ end
171
239
  end
172
240
 
173
241
  end
174
242
 
175
243
 
176
- # The default help command.It adds the options "-h" and "--help" to the global +CommandParser+
177
- # options. When specified on the command line, it can show the main help or an individual command
178
- # help.
244
+ # The default help command. It adds the options "-h" and "--help" to the global options of the
245
+ # associated +CommandParser+. When the command is specified on the command line, it can show the
246
+ # main help or individual command help.
179
247
  class HelpCommand < Command
180
248
 
181
249
  def initialize
182
- super( 'help' )
250
+ super( 'help', false )
251
+ self.short_desc = 'Provide help for individual commands'
252
+ self.description = 'This command prints the program help if no arguments are given. ' \
253
+ 'If one or more command names are given as arguments, these arguments are interpreted ' \
254
+ 'as a hierachy of commands and the help for the right most command is show.'
183
255
  end
184
256
 
185
- def init( commandParser )
186
- commandParser.options do |opt|
187
- opt.on_tail( "-h", "--help [command]", "Show help" ) do |cmd|
188
- execute( commandParser, cmd.nil? ? [] : [cmd] )
257
+ def init
258
+ case commandparser.main_command.options
259
+ when OptionParserWrapper
260
+ commandparser.main_command.options.instance do |opt|
261
+ opt.on_tail( "-h", "--help", "Show help" ) do
262
+ execute( [] )
263
+ end
189
264
  end
190
265
  end
191
266
  end
192
267
 
193
- def description
194
- 'Provides help for the individual commands'
195
- end
196
-
197
268
  def usage
198
- "Usage: #{@options.program_name} help COMMAND"
269
+ "Usage: #{commandparser.program_name} help [COMMAND SUBCOMMAND ...]"
199
270
  end
200
271
 
201
- def execute( commandParser, args )
272
+ def execute( args )
202
273
  if args.length > 0
203
- if commandParser.commands.include?( args[0] )
204
- commandParser.commands[args[0]].show_help( commandParser )
274
+ cmd = commandparser.main_command
275
+ arg = args.shift
276
+ while !arg.nil? && cmd.commands.keys.include?( arg )
277
+ cmd = cmd.commands[arg]
278
+ arg = args.shift
279
+ end
280
+ if arg.nil?
281
+ cmd.show_help
205
282
  else
206
- raise OptionParser::InvalidArgument, args[0]
283
+ raise InvalidArgumentError, args.unshift( arg ).join(' ')
207
284
  end
208
285
  else
209
- show_program_help( commandParser )
286
+ show_program_help
210
287
  end
211
288
  exit
212
289
  end
213
290
 
291
+ #######
214
292
  private
293
+ #######
215
294
 
216
- def show_program_help( commandParser )
217
- if commandParser.options.get_banner.nil?
218
- puts "Usage: #{commandParser.options.program_name} [global options] <command> [options] [args]"
219
- else
220
- puts commandParser.options.banner
221
- end
295
+ def show_program_help
296
+ puts "Usage: #{commandparser.program_name} [options] COMMAND [options] [COMMAND [options] ...] [args]"
222
297
  puts ""
223
- puts "Available commands:"
224
- width = commandParser.commands.keys.max {|a,b| a.length <=> b.length }.length
225
- commandParser.commands.sort.each do |name, command|
226
- print commandParser.options.summary_indent + name.ljust( width + 4 ) + command.description
227
- print " (=default command)" if name == commandParser.default
228
- print "\n"
229
- end
298
+ list_commands( 1, commandparser.main_command )
230
299
  puts ""
231
- puts commandParser.options.summarize
300
+ puts commandparser.main_command.options.summarize
301
+ puts
232
302
  end
233
303
 
234
304
  end
235
305
 
236
306
 
237
- # The default version command. It adds the options "-v" and "--version" to the global
238
- # +CommandParser+ options. When specified on the command line, it shows the version of the
239
- # program. The output can be controlled by options.
307
+ # The default version command. It adds the options "-v" and "--version" to the global options of
308
+ # the associated +CommandParser+. When specified on the command line, it shows the version of the
309
+ # program.
240
310
  class VersionCommand < Command
241
311
 
242
312
  def initialize
243
- super( 'version' )
244
- @fullversion = false
245
- options.separator "Options:"
246
- options.on( "-f", "--full", "Show the full version string" ) { @fullversion = true }
313
+ super( 'version', false )
314
+ self.short_desc = "Show the version of the program"
247
315
  end
248
316
 
249
- def init( commandParser )
250
- commandParser.options do |opt|
251
- opt.on_tail( "--version", "-v", "Show the version of the program" ) do
252
- execute( commandParser, [] )
317
+ def init
318
+ case commandparser.main_command.options
319
+ when OptionParserWrapper
320
+ commandparser.main_command.options.instance do |opt|
321
+ opt.on_tail( "--version", "-v", "Show the version of the program" ) do
322
+ execute( [] )
323
+ end
253
324
  end
254
325
  end
255
326
  end
256
327
 
257
- def description
258
- "Shows the version of the program"
259
- end
260
-
261
328
  def usage
262
- "Usage: #{@options.program_name} version [options]"
329
+ "Usage: #{commandparser.program_name} version"
263
330
  end
264
331
 
265
- def execute( commandParser, args )
266
- if @fullversion
267
- version = commandParser.options.ver
268
- else
269
- version = commandParser.options.version
270
- version = version.join( '.' ) if version.instance_of? Array
271
- end
272
- version = "<NO VERSION SPECIFIED>" if version.nil?
332
+ def execute( args )
333
+ version = commandparser.program_version
334
+ version = version.join( '.' ) if version.instance_of?( Array )
273
335
  puts version
274
336
  exit
275
337
  end
276
338
 
277
339
  end
278
340
 
279
- # Holds the registered commands.
280
- attr_reader :commands
281
-
282
- # Returns the name of the default command.
283
- attr_reader :default
284
341
 
285
- # Are Exceptions be handled gracefully? I.e. by printing error message and help screen?
286
- attr_reader :handleExceptions
342
+ # The main class for creating a command based CLI program.
343
+ class CommandParser
287
344
 
288
- # Create a new CommandParser object. The optional argument +handleExceptions+ specifies if the
289
- # object should handle exceptions gracefully.
290
- def initialize( handleExceptions = false )
291
- @options = OptionParser.new
292
- @commands = {}
293
- @default = nil
294
- @parsed = {}
295
- @handleExceptions = handleExceptions
296
- end
345
+ # The top level command representing the program itself.
346
+ attr_reader :main_command
297
347
 
298
- # If called with a block, this method yields the global options of the +CommandParser+. If no
299
- # block is specified, it returns the global options.
300
- def options # :yields: options
301
- if block_given?
302
- yield @options
303
- else
304
- @options
305
- end
306
- end
348
+ # The name of the program.
349
+ attr_accessor :program_name
307
350
 
308
- # Adds a command to the command list. If the optional parameter +default+ is true, then this
309
- # command is used when no command is specified on the command line.
310
- def add_command( command, default = false )
311
- @commands[command.name] = command
312
- @default = command.name if default
313
- command.init( self )
314
- end
351
+ # The version of the program.
352
+ attr_accessor :program_version
315
353
 
316
- # Parses the global options.
317
- def parse_global_options!( args )
318
- @options.order!( args )
319
- end
354
+ # Are Exceptions be handled gracefully? I.e. by printing error message and the help screen?
355
+ attr_reader :handle_exceptions
320
356
 
321
- # Parses the command.
322
- def parse_command!( args )
323
- @parsed[:command] = args.shift
324
- if @parsed[:command].nil?
325
- if @default.nil?
326
- raise NoCommandGivenError
327
- else
328
- @parsed[:command] = @default
329
- end
330
- else
331
- raise InvalidCommandError.new( @parsed[:command] ) unless commands.include?( @parsed[:command] )
357
+ # Create a new CommandParser object. The optional argument +handleExceptions+ specifies if the
358
+ # object should handle exceptions gracefully.
359
+ def initialize( handleExceptions = false )
360
+ @main_command = Command.new( 'mainCommand', true )
361
+ @main_command.super_command = self
362
+ @program_name = $0
363
+ @program_version = "0.0.0"
364
+ @handle_exceptions = handleExceptions
332
365
  end
333
- end
334
366
 
335
- # Parses the local options. Attention: The command has to be parsed (invoke method
336
- # +parse_command!+) before this method can be invoked.
337
- def parse_local_options!( args )
338
- if @parsed[:command]
339
- commands[@parsed[:command]].options.permute!( args ) unless commands[@parsed[:command]].options.nil?
367
+ # Returns the wrapper for parsing the global options.
368
+ def options
369
+ @main_command.options
340
370
  end
341
- end
342
371
 
343
- # Calls +parse+ - implemented to mimic OptionParser
344
- def permute( args ); parse( args ); end
345
- # Calls +parse!+ - implemented to mimic OptionParser
346
- def permute!( args ); parse!( args ); end
347
- # Calls +parse+ - implemented to mimic OptionParser
348
- def order( args ); parse( args ); end
349
- # Calls +parse!+ - implemented to mimic OptionParser
350
- def order!( args ); parse!( args ); end
351
- # see CommandParser#parse!
352
- def parse( args ); parse!( args.dup ); end
353
-
354
- # Parses the given argument. First it tries to parse global arguments if given. After that the
355
- # command name is analyzied and the options for the specific commands parsed. If +execCommand+ is
356
- # true, the command is executed immediately. If false, the <tt>CommandParser#execute</tt> has to
357
- # be called to execute the command. The optional +parse+ parameter specifies what should be
358
- # parsed. If <tt>:global</tt> is included in the +parse+ array, global options are parsed; if
359
- # <tt>:command</tt> is included, the command is parsed and if <tt>:local</tt> is included, the
360
- # local options are parsed.
361
- def parse!( args, execCommand = true, parse = [:global, :command, :local] )
362
- begin
363
- context = :global
364
- parse_global_options!( args ) if parse.include?( :global )
365
- parse_command!( args ) if parse.include?( :command )
366
-
367
- context = :local
368
- parse_local_options!( args ) if parse.include?( :local )
369
- rescue OptionParser::ParseError => e
370
- handle_exception( e, context )
372
+ # Sets the wrapper for parsing the global options.
373
+ def options=( wrapper )
374
+ @main_command.options = wrapper
371
375
  end
372
376
 
373
- @parsed[:args] = args
374
- execute if execCommand
375
- end
376
-
377
- # Executes the command. The method +CommandParser#parse!+ has to be called before this one!
378
- def execute
379
- begin
380
- commands[@parsed[:command]].execute( self, @parsed[:args] ) if @parsed[:command]
381
- rescue OptionParser::ParseError => e
382
- handle_exception( e, :local )
377
+ # Adds a top level command.
378
+ def add_command( *args )
379
+ @main_command.add_command( *args )
383
380
  end
384
- end
385
381
 
386
- private
382
+ # Parses the command line arguments. If a block is specified, the current hierarchy level and
383
+ # the name of the current command is yielded after the options for the level have been parsed.
384
+ def parse( argv = ARGV ) # :yields: level, commandName
385
+ level = 0
386
+ command = @main_command
387
+
388
+ while !command.nil?
389
+ argv = if command.has_commands? || ENV.include?( 'POSIXLY_CORRECT' )
390
+ command.options.order( argv )
391
+ else
392
+ command.options.permute( argv )
393
+ end
394
+ yield( level, command.name ) if block_given?
395
+
396
+ if command.has_commands?
397
+ cmdName, argv = argv[0], argv[1..-1] || []
398
+
399
+ if cmdName.nil?
400
+ if command.default_command.nil?
401
+ raise NoCommandGivenError
402
+ else
403
+ cmdName = command.default_command
404
+ end
405
+ else
406
+ raise InvalidCommandError.new( cmdName ) unless command.commands.include?( cmdName )
407
+ end
408
+
409
+ command = command.commands[cmdName]
410
+ level += 1
411
+ else
412
+ command.execute( argv )
413
+ command = nil
414
+ end
415
+ end
416
+ rescue ParseError, OptionParser::ParseError => e
417
+ raise if !@handle_exceptions
418
+ puts "Error while parsing command line:\n " + e.message
419
+ puts
420
+ @main_command.commands['help'].execute( command.super_commands.reverse.collect {|c| c.name} ) if @main_command.commands['help']
421
+ exit
422
+ end
387
423
 
388
- def handle_exception( exception, context )
389
- raise unless @handleExceptions
390
- s = (context == :global ? "global" : "command specific")
391
- puts "Error parsing #{s} options:\n " + exception.message
392
- puts
393
- commands['help'].execute( self, (context == :global ? [] : [@parsed[:command]]) ) if commands['help']
394
- exit
395
424
  end
396
425
 
397
426
  end
398
-