cmdparse 1.0.5 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +40 -0
- data/README +12 -12
- data/Rakefile +11 -11
- data/TODO +1 -0
- data/VERSION +1 -1
- data/doc/config.yaml +2 -0
- data/doc/plugin/extract.rb +1 -1
- data/doc/src/about.page +9 -1
- data/doc/src/default.template +4 -4
- data/doc/src/documentation.page +72 -36
- data/doc/src/download.page +10 -10
- data/doc/src/features.page +4 -4
- data/doc/src/index.page +12 -1
- data/lib/cmdparse.rb +298 -270
- data/lib/cmdparse/wrappers/optparse.rb +65 -0
- data/net.rb +88 -0
- metadata +12 -10
- data/test.rb +0 -42
data/lib/cmdparse.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
#
|
2
2
|
#--
|
3
3
|
#
|
4
|
-
# $Id: cmdparse.rb
|
4
|
+
# $Id: cmdparse.rb 329 2005-08-14 15:39:05Z thomas $
|
5
5
|
#
|
6
|
-
# cmdparse:
|
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
|
-
#
|
28
|
-
|
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
|
-
#
|
39
|
-
|
40
|
-
|
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
|
-
#
|
45
|
-
|
46
|
-
|
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
|
-
|
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
|
-
#
|
55
|
-
|
56
|
-
|
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
|
-
#
|
113
|
-
class
|
114
|
-
|
115
|
-
|
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 +
|
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
|
-
#
|
130
|
-
|
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
|
-
#
|
133
|
-
|
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 =
|
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
|
-
#
|
144
|
-
#
|
145
|
-
def
|
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
|
-
#
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
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
|
-
#
|
158
|
-
|
159
|
-
|
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.
|
198
|
+
# Defines the usage line for the command.
|
163
199
|
def usage
|
164
|
-
"Usage: #{
|
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
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
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
|
177
|
-
#
|
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
|
186
|
-
|
187
|
-
|
188
|
-
|
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: #{
|
269
|
+
"Usage: #{commandparser.program_name} help [COMMAND SUBCOMMAND ...]"
|
199
270
|
end
|
200
271
|
|
201
|
-
def execute(
|
272
|
+
def execute( args )
|
202
273
|
if args.length > 0
|
203
|
-
|
204
|
-
|
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
|
283
|
+
raise InvalidArgumentError, args.unshift( arg ).join(' ')
|
207
284
|
end
|
208
285
|
else
|
209
|
-
show_program_help
|
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
|
217
|
-
|
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
|
-
|
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
|
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
|
239
|
-
# program.
|
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
|
-
|
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
|
250
|
-
|
251
|
-
|
252
|
-
|
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: #{
|
329
|
+
"Usage: #{commandparser.program_name} version"
|
263
330
|
end
|
264
331
|
|
265
|
-
def execute(
|
266
|
-
|
267
|
-
|
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
|
-
#
|
286
|
-
|
342
|
+
# The main class for creating a command based CLI program.
|
343
|
+
class CommandParser
|
287
344
|
|
288
|
-
|
289
|
-
|
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
|
-
|
299
|
-
|
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
|
-
|
309
|
-
|
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
|
-
|
317
|
-
|
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
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
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
|
-
|
336
|
-
|
337
|
-
|
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
|
-
|
344
|
-
|
345
|
-
|
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
|
-
|
374
|
-
|
375
|
-
|
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
|
-
|
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
|
-
|