gli 1.6.0 → 2.0.0.rc3
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.
- data/.gitignore +11 -0
- data/.rvmrc +1 -0
- data/.travis.yml +10 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +201 -0
- data/ObjectModel.graffle +1191 -0
- data/README.rdoc +60 -10
- data/Rakefile +145 -0
- data/bin/gli +12 -30
- data/bin/report_on_rake_results +10 -0
- data/bin/test_all_rubies.sh +6 -0
- data/features/gli_executable.feature +84 -0
- data/features/gli_init.feature +219 -0
- data/features/step_definitions/gli_executable_steps.rb +12 -0
- data/features/step_definitions/gli_init_steps.rb +11 -0
- data/features/step_definitions/todo_steps.rb +69 -0
- data/features/support/env.rb +49 -0
- data/features/todo.feature +182 -0
- data/gli.cheat +95 -0
- data/gli.gemspec +34 -0
- data/lib/gli.rb +11 -571
- data/lib/gli/app.rb +184 -0
- data/lib/gli/app_support.rb +226 -0
- data/lib/gli/command.rb +107 -95
- data/lib/gli/command_line_option.rb +34 -0
- data/lib/gli/command_line_token.rb +13 -9
- data/lib/gli/command_support.rb +200 -0
- data/lib/gli/commands/compound_command.rb +42 -0
- data/lib/gli/commands/help.rb +63 -0
- data/lib/gli/commands/help_modules/command_help_format.rb +134 -0
- data/lib/gli/commands/help_modules/global_help_format.rb +61 -0
- data/lib/gli/commands/help_modules/list_formatter.rb +22 -0
- data/lib/gli/commands/help_modules/options_formatter.rb +50 -0
- data/lib/gli/commands/help_modules/text_wrapper.rb +53 -0
- data/lib/gli/commands/initconfig.rb +67 -0
- data/lib/{support → gli/commands}/scaffold.rb +150 -34
- data/lib/gli/dsl.rb +194 -0
- data/lib/gli/exceptions.rb +13 -4
- data/lib/gli/flag.rb +30 -41
- data/lib/gli/gli_option_parser.rb +98 -0
- data/lib/gli/option_parser_factory.rb +44 -0
- data/lib/gli/options.rb +2 -1
- data/lib/gli/switch.rb +19 -51
- data/lib/gli/terminal.rb +30 -20
- data/lib/gli/version.rb +5 -0
- data/test/apps/README.md +2 -0
- data/test/apps/todo/Gemfile +2 -0
- data/test/apps/todo/README.rdoc +6 -0
- data/test/apps/todo/Rakefile +23 -0
- data/test/apps/todo/bin/todo +52 -0
- data/test/apps/todo/lib/todo/commands/create.rb +22 -0
- data/test/apps/todo/lib/todo/commands/list.rb +53 -0
- data/test/apps/todo/lib/todo/commands/ls.rb +47 -0
- data/test/apps/todo/lib/todo/version.rb +3 -0
- data/test/apps/todo/test/tc_nothing.rb +14 -0
- data/test/apps/todo/todo.gemspec +23 -0
- data/test/apps/todo/todo.rdoc +5 -0
- data/test/config.yaml +10 -0
- data/test/fake_std_out.rb +30 -0
- data/test/gli.reek +122 -0
- data/test/init_simplecov.rb +8 -0
- data/test/option_test_helper.rb +13 -0
- data/test/roodi.yaml +18 -0
- data/test/tc_command.rb +260 -0
- data/test/tc_compount_command.rb +22 -0
- data/test/tc_flag.rb +56 -0
- data/test/tc_gli.rb +611 -0
- data/test/tc_help.rb +223 -0
- data/test/tc_options.rb +31 -0
- data/test/tc_subcommands.rb +162 -0
- data/test/tc_switch.rb +57 -0
- data/test/tc_terminal.rb +97 -0
- data/test/test_helper.rb +13 -0
- metadata +318 -49
- data/lib/gli_version.rb +0 -3
- data/lib/support/help.rb +0 -179
- data/lib/support/initconfig.rb +0 -34
- data/lib/support/rdoc.rb +0 -119
data/lib/gli.rb
CHANGED
@@ -1,581 +1,21 @@
|
|
1
|
+
require 'gli/option_parser_factory.rb'
|
2
|
+
require 'gli/gli_option_parser.rb'
|
3
|
+
require 'gli/app_support.rb'
|
4
|
+
require 'gli/app.rb'
|
5
|
+
require 'gli/command_support.rb'
|
1
6
|
require 'gli/command.rb'
|
2
7
|
require 'gli/command_line_token.rb'
|
8
|
+
require 'gli/command_line_option.rb'
|
3
9
|
require 'gli/copy_options_to_aliases.rb'
|
4
10
|
require 'gli/exceptions.rb'
|
5
11
|
require 'gli/flag.rb'
|
6
12
|
require 'gli/options.rb'
|
7
13
|
require 'gli/switch.rb'
|
8
|
-
require '
|
9
|
-
require '
|
10
|
-
require '
|
11
|
-
require '
|
12
|
-
require '
|
14
|
+
require 'gli/dsl.rb'
|
15
|
+
require 'gli/version.rb'
|
16
|
+
require 'gli/commands/help'
|
17
|
+
require 'gli/commands/compound_command'
|
18
|
+
require 'gli/commands/initconfig'
|
13
19
|
|
14
|
-
# A means to define and parse a command line interface that works as
|
15
|
-
# Git's does, in that you specify global options, a command name, command
|
16
|
-
# specific options, and then command arguments.
|
17
20
|
module GLI
|
18
|
-
extend self
|
19
|
-
include CopyOptionsToAliases
|
20
|
-
|
21
|
-
@@program_name = $0.split(/\//)[-1]
|
22
|
-
@@post_block = nil
|
23
|
-
@@pre_block = nil
|
24
|
-
@@error_block = nil
|
25
|
-
@@config_file = nil
|
26
|
-
@@use_openstruct = false
|
27
|
-
@@version = nil
|
28
|
-
@@stderr = $stderr
|
29
|
-
@@program_desc = nil
|
30
|
-
@@skips_pre = false
|
31
|
-
@@skips_post = false
|
32
|
-
@@default_command = :help
|
33
|
-
|
34
|
-
# Override the device of stderr; exposed only for testing
|
35
|
-
def error_device=(e) #:nodoc:
|
36
|
-
@@stderr = e
|
37
|
-
end
|
38
|
-
|
39
|
-
# Reset the GLI module internal data structures; mostly useful for testing
|
40
|
-
def reset # :nodoc:
|
41
|
-
switches.clear
|
42
|
-
flags.clear
|
43
|
-
commands.clear
|
44
|
-
@@version = nil
|
45
|
-
@@config_file = nil
|
46
|
-
@@use_openstruct = false
|
47
|
-
@@prog_desc = nil
|
48
|
-
@@default_command = :help
|
49
|
-
clear_nexts
|
50
|
-
|
51
|
-
desc 'Show this message'
|
52
|
-
switch :help
|
53
|
-
end
|
54
|
-
|
55
|
-
# Sets a default command to run when none is specified on the command line. Note that
|
56
|
-
# if you use this, you won't be able to pass arguments, flags, or switches
|
57
|
-
# to the command when run in default mode. All flags and switches are treated
|
58
|
-
# as global, and any argument will be interpretted as the command name and likely
|
59
|
-
# fail.
|
60
|
-
#
|
61
|
-
# +command+:: Command to run as default
|
62
|
-
def default_command(command)
|
63
|
-
@@default_command = command.to_sym
|
64
|
-
end
|
65
|
-
|
66
|
-
# Describe the next switch, flag, or command. This should be a
|
67
|
-
# short, one-line description
|
68
|
-
#
|
69
|
-
# +description+:: A String of the short descripiton of the switch, flag, or command following
|
70
|
-
def desc(description); @@next_desc = description; end
|
71
|
-
|
72
|
-
# Describe the overall application/programm. This should be a one-sentence summary
|
73
|
-
# of what your program does that will appear in the help output.
|
74
|
-
#
|
75
|
-
# +description+:: A String of the short description of your program's purpose
|
76
|
-
def program_desc(description=nil)
|
77
|
-
if description
|
78
|
-
@@program_desc = description
|
79
|
-
end
|
80
|
-
@@program_desc
|
81
|
-
end
|
82
|
-
|
83
|
-
# Use this if the following command should not have the pre block executed.
|
84
|
-
# By default, the pre block is executed before each command and can result in
|
85
|
-
# aborting the call. Using this will avoid that behavior for the following command
|
86
|
-
def skips_pre
|
87
|
-
@@skips_pre = true
|
88
|
-
end
|
89
|
-
|
90
|
-
# Use this if the following command should not have the post block executed.
|
91
|
-
# By default, the post block is executed after each command.
|
92
|
-
# Using this will avoid that behavior for the following command
|
93
|
-
def skips_post
|
94
|
-
@@skips_post = true
|
95
|
-
end
|
96
|
-
|
97
|
-
# Provide a longer, more detailed description. This
|
98
|
-
# will be reformatted and wrapped to fit in the terminal's columns
|
99
|
-
#
|
100
|
-
# +long_desc+:: A String that is s longer description of the switch, flag, or command following.
|
101
|
-
def long_desc(long_desc); @@next_long_desc = long_desc; end
|
102
|
-
|
103
|
-
# Describe the argument name of the next flag. It's important to keep
|
104
|
-
# this VERY short and, ideally, without any spaces (see Example).
|
105
|
-
#
|
106
|
-
# +name+:: A String that *briefly* describes the argument given to the following command or flag.
|
107
|
-
#
|
108
|
-
# Example:
|
109
|
-
# desc 'Set the filename'
|
110
|
-
# arg_name 'file_name'
|
111
|
-
# flag [:f,:filename]
|
112
|
-
#
|
113
|
-
# Produces:
|
114
|
-
# -f, --filename=file_name Set the filename
|
115
|
-
def arg_name(name); @@next_arg_name = name; end
|
116
|
-
|
117
|
-
# set the default value of the next flag
|
118
|
-
#
|
119
|
-
# +val+:: A String reprensenting the default value to be used for the following flag if the user doesn't specify one
|
120
|
-
# and, when using a config file, the config also doesn't specify one
|
121
|
-
def default_value(val); @@next_default_value = val; end
|
122
|
-
|
123
|
-
# Create a flag, which is a switch that takes an argument
|
124
|
-
#
|
125
|
-
# +names+:: a String or Symbol, or an Array of String or Symbol that represent all the different names
|
126
|
-
# and aliases for this flag.
|
127
|
-
#
|
128
|
-
# Example:
|
129
|
-
#
|
130
|
-
# desc 'Set the filename'
|
131
|
-
# flag [:f,:filename,'file-name']
|
132
|
-
#
|
133
|
-
# Produces:
|
134
|
-
#
|
135
|
-
# -f, --filename, --file-name=arg Set the filename
|
136
|
-
def flag(*names)
|
137
|
-
names = [names].flatten
|
138
|
-
verify_unused(names,flags,switches,"in global options")
|
139
|
-
flag = Flag.new(names,@@next_desc,@@next_arg_name,@@next_default_value,@@next_long_desc)
|
140
|
-
flags[flag.name] = flag
|
141
|
-
clear_nexts
|
142
|
-
end
|
143
|
-
|
144
|
-
# Create a switch, which is a command line flag that takes no arguments (thus, it _switches_ something on)
|
145
|
-
#
|
146
|
-
# +names+:: a String or Symbol, or an Array of String or Symbol that represent all the different names
|
147
|
-
# and aliases for this switch.
|
148
|
-
def switch(*names)
|
149
|
-
names = [names].flatten
|
150
|
-
verify_unused(names,flags,switches,"in global options")
|
151
|
-
switch = Switch.new(names,@@next_desc,@@next_long_desc)
|
152
|
-
switches[switch.name] = switch
|
153
|
-
clear_nexts
|
154
|
-
end
|
155
|
-
|
156
|
-
# Sets that this app uses a config file as well as the name of the config file.
|
157
|
-
#
|
158
|
-
# +filename+:: A String representing the path to the file to use for the config file. If it's an absolute
|
159
|
-
# path, this is treated as the path to the file. If it's *not*, it's treated as relative to the user's home
|
160
|
-
# directory as produced by <code>File.expand_path('~')</code>.
|
161
|
-
def config_file(filename)
|
162
|
-
if filename =~ /^\//
|
163
|
-
@@config_file = filename
|
164
|
-
else
|
165
|
-
@@config_file = File.join(File.expand_path('~'),filename)
|
166
|
-
end
|
167
|
-
commands[:initconfig] = InitConfig.new(@@config_file)
|
168
|
-
@@config_file
|
169
|
-
end
|
170
|
-
|
171
|
-
# Define a new command. This takes a block that will be given an instance of the Command that was created.
|
172
|
-
# You then may call methods on this object to define aspects of that Command.
|
173
|
-
#
|
174
|
-
# +names+:: a String or Symbol, or an Array of String or Symbol that represent all the different names and aliases for this command.
|
175
|
-
#
|
176
|
-
def command(*names)
|
177
|
-
command = Command.new([names].flatten,@@next_desc,@@next_arg_name,@@next_long_desc,@@skips_pre,@@skips_post)
|
178
|
-
commands[command.name] = command
|
179
|
-
yield command
|
180
|
-
clear_nexts
|
181
|
-
end
|
182
|
-
|
183
|
-
# Define a block to run after command line arguments are parsed
|
184
|
-
# but before any command is run. If this block raises an exception
|
185
|
-
# the command specified will not be executed.
|
186
|
-
# The block will receive the global-options,command,options, and arguments
|
187
|
-
# If this block evaluates to true, the program will proceed; otherwise
|
188
|
-
# the program will end immediately
|
189
|
-
def pre(&a_proc)
|
190
|
-
@@pre_block = a_proc
|
191
|
-
end
|
192
|
-
|
193
|
-
# Define a block to run after the command was executed, <b>only
|
194
|
-
# if there was not an error</b>.
|
195
|
-
# The block will receive the global-options,command,options, and arguments
|
196
|
-
def post(&a_proc)
|
197
|
-
@@post_block = a_proc
|
198
|
-
end
|
199
|
-
|
200
|
-
# Define a block to run if an error occurs.
|
201
|
-
# The block will receive any Exception that was caught.
|
202
|
-
# It should evaluate to false to avoid the built-in error handling (which basically just
|
203
|
-
# prints out a message). GLI uses a variety of exceptions that you can use to find out what
|
204
|
-
# errors might've occurred during command-line parsing:
|
205
|
-
# * GLI::CustomExit
|
206
|
-
# * GLI::UnknownCommandArgument
|
207
|
-
# * GLI::UnknownGlobalArgument
|
208
|
-
# * GLI::UnknownCommand
|
209
|
-
# * GLI::BadCommandLine
|
210
|
-
def on_error(&a_proc)
|
211
|
-
@@error_block = a_proc
|
212
|
-
end
|
213
|
-
|
214
|
-
# Indicate the version of your application
|
215
|
-
#
|
216
|
-
# +version+:: String containing the version of your application.
|
217
|
-
def version(version)
|
218
|
-
@@version = version
|
219
|
-
end
|
220
|
-
|
221
|
-
# Call this with +true+ will cause the +global_options+ and
|
222
|
-
# +options+ passed to your code to be wrapped in
|
223
|
-
# Options, which is a subclass of +OpenStruct+ that adds
|
224
|
-
# <tt>[]</tt> and <tt>[]=</tt> methods.
|
225
|
-
#
|
226
|
-
# +use_openstruct+:: a Boolean indicating if we should use OpenStruct instead of Hashes
|
227
|
-
def use_openstruct(use_openstruct)
|
228
|
-
@@use_openstruct = use_openstruct
|
229
|
-
end
|
230
|
-
|
231
|
-
# Runs whatever command is needed based on the arguments.
|
232
|
-
#
|
233
|
-
# +args+:: the command line ARGV array
|
234
|
-
#
|
235
|
-
# Returns a number that would be a reasonable exit code
|
236
|
-
def run(args) #:nodoc:
|
237
|
-
rdoc = RDocCommand.new
|
238
|
-
commands[:rdoc] = rdoc if !commands[:rdoc]
|
239
|
-
commands[:help] = DefaultHelpCommand.new(@@version,rdoc) if !commands[:help]
|
240
|
-
exit_code = 0
|
241
|
-
begin
|
242
|
-
config = parse_config
|
243
|
-
override_defaults_based_on_config(config)
|
244
|
-
global_options,command,options,arguments = parse_options(args)
|
245
|
-
copy_options_to_aliased_versions(global_options,command,options)
|
246
|
-
global_options = convert_to_openstruct?(global_options)
|
247
|
-
options = convert_to_openstruct?(options)
|
248
|
-
if proceed?(global_options,command,options,arguments)
|
249
|
-
command = commands[@@default_command] if !command
|
250
|
-
command.execute(global_options,options,arguments)
|
251
|
-
if !command.skips_post && @@post_block
|
252
|
-
@@post_block.call(global_options,command,options,arguments)
|
253
|
-
end
|
254
|
-
end
|
255
|
-
rescue Exception => ex
|
256
|
-
|
257
|
-
@@stderr.puts error_message(ex) if regular_error_handling?(ex)
|
258
|
-
|
259
|
-
exit_code = if ex.respond_to? :exit_code
|
260
|
-
ex.exit_code
|
261
|
-
else
|
262
|
-
-2
|
263
|
-
end
|
264
|
-
raise ex if ENV['GLI_DEBUG'] == 'true'
|
265
|
-
end
|
266
|
-
exit_code
|
267
|
-
end
|
268
|
-
|
269
|
-
# True if we should proceed with executing the command; this calls
|
270
|
-
# the pre block if it's defined
|
271
|
-
def proceed?(global_options,command,options,arguments) #:nodoc:
|
272
|
-
if command && command.skips_pre
|
273
|
-
true
|
274
|
-
elsif @@pre_block
|
275
|
-
@@pre_block.call(global_options,command,options,arguments)
|
276
|
-
else
|
277
|
-
true
|
278
|
-
end
|
279
|
-
end
|
280
|
-
|
281
|
-
# Returns true if we should proceed with GLI's basic error handling.
|
282
|
-
# This calls the error block if the user provided one
|
283
|
-
def regular_error_handling?(ex) #:nodoc:
|
284
|
-
if @@error_block
|
285
|
-
@@error_block.call(ex)
|
286
|
-
else
|
287
|
-
true
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
# Returns a String of the error message to show the user
|
292
|
-
# +ex+:: The exception we caught that launched the error handling routines
|
293
|
-
def error_message(ex) #:nodoc:
|
294
|
-
msg = "error: #{ex.message}"
|
295
|
-
case ex
|
296
|
-
when UnknownCommand
|
297
|
-
msg += ". Use '#{program_name} help' for a list of commands"
|
298
|
-
when UnknownCommandArgument
|
299
|
-
msg += ". Use '#{program_name} help #{ex.command.name}' for a list of command options"
|
300
|
-
when UnknownGlobalArgument
|
301
|
-
msg += ". Use '#{program_name} help' for a list of global options"
|
302
|
-
end
|
303
|
-
msg
|
304
|
-
end
|
305
|
-
|
306
|
-
# Simpler means of exiting with a custom exit code. This will
|
307
|
-
# raise a CustomExit with the given message and exit code, which will ultimatley
|
308
|
-
# cause your application to exit with the given exit_code as its exit status
|
309
|
-
def exit_now!(message,exit_code)
|
310
|
-
raise CustomExit.new(message,exit_code)
|
311
|
-
end
|
312
|
-
|
313
|
-
# Set or get the name of the program, if you don't want the default (which is
|
314
|
-
# the name of the command line program). This
|
315
|
-
# is only used currently in the help and rdoc commands.
|
316
|
-
#
|
317
|
-
# +override+:: A String that represents the name of the program to use, other than the default.
|
318
|
-
#
|
319
|
-
# Returns the current program name, as a String
|
320
|
-
def program_name(override=nil)
|
321
|
-
if override
|
322
|
-
@@program_name = override
|
323
|
-
end
|
324
|
-
@@program_name
|
325
|
-
end
|
326
|
-
|
327
|
-
alias :d :desc
|
328
|
-
alias :f :flag
|
329
|
-
alias :s :switch
|
330
|
-
alias :c :command
|
331
|
-
|
332
|
-
# Possibly returns a copy of the passed-in Hash as an instance of GLI::Option.
|
333
|
-
# By default, it will *not*. However by putting <tt>use_openstruct true</tt>
|
334
|
-
# in your CLI definition, it will
|
335
|
-
def convert_to_openstruct?(options) # :nodoc:
|
336
|
-
@@use_openstruct ? Options.new(options) : options
|
337
|
-
end
|
338
|
-
|
339
|
-
# Copies all options in both global_options and options to keys for the aliases of those flags.
|
340
|
-
# For example, if a flag works with either -f or --flag, this will copy the value from [:f] to [:flag]
|
341
|
-
# to allow the user to access the options by any alias
|
342
|
-
def copy_options_to_aliased_versions(global_options,command,options) # :nodoc:
|
343
|
-
copy_options_to_aliases(global_options)
|
344
|
-
command.copy_options_to_aliases(options)
|
345
|
-
end
|
346
|
-
|
347
|
-
def parse_config # :nodoc:
|
348
|
-
return nil if @@config_file.nil?
|
349
|
-
require 'yaml'
|
350
|
-
if File.exist?(@@config_file)
|
351
|
-
File.open(@@config_file) { |file| YAML::load(file) }
|
352
|
-
else
|
353
|
-
{}
|
354
|
-
end
|
355
|
-
end
|
356
|
-
|
357
|
-
# Returns an array of four values:
|
358
|
-
# * global options (as a Hash)
|
359
|
-
# * Command
|
360
|
-
# * command options (as a Hash)
|
361
|
-
# * arguments (as an Array)
|
362
|
-
def parse_options(args) # :nodoc:
|
363
|
-
global_options,command,options,arguments = parse_options_helper(args.clone,Hash.new,nil,Hash.new,Array.new)
|
364
|
-
flags.each { |name,flag| global_options[name] = flag.default_value if !global_options[name] }
|
365
|
-
command.flags.each { |name,flag| options[name] = flag.default_value if !options[name] }
|
366
|
-
return [global_options,command,options,arguments]
|
367
|
-
end
|
368
|
-
|
369
|
-
# Finds the index of the first non-flag
|
370
|
-
# argument or -1 if there wasn't one.
|
371
|
-
def find_non_flag_index(args) # :nodoc:
|
372
|
-
args.each_with_index do |item,index|
|
373
|
-
return index if item =~ /^[^\-]/
|
374
|
-
return index-1 if item =~ /^\-\-$/
|
375
|
-
end
|
376
|
-
-1
|
377
|
-
end
|
378
|
-
|
379
|
-
def flag_switch_index(args)
|
380
|
-
args.each_with_index do |item,index|
|
381
|
-
return index if item =~ /^[\-]/
|
382
|
-
end
|
383
|
-
-1
|
384
|
-
end
|
385
|
-
|
386
|
-
def clear_nexts # :nodoc:
|
387
|
-
@@next_desc = nil
|
388
|
-
@@next_arg_name = nil
|
389
|
-
@@next_default_value = nil
|
390
|
-
@@next_long_desc = nil
|
391
|
-
@@skips_pre = false
|
392
|
-
@@skips_post = false
|
393
|
-
end
|
394
|
-
|
395
|
-
clear_nexts
|
396
|
-
|
397
|
-
def flags # :nodoc:
|
398
|
-
@@flags ||= {}
|
399
|
-
end
|
400
|
-
def switches # :nodoc:
|
401
|
-
@@switches ||= {}
|
402
|
-
end
|
403
|
-
def commands # :nodoc:
|
404
|
-
@@commands ||= {}
|
405
|
-
end
|
406
|
-
|
407
|
-
# Recursive helper for parsing command line options
|
408
|
-
# <code>args</code>:: the arguments that have yet to be processed
|
409
|
-
# <code>global_options</code>:: the global options hash
|
410
|
-
# <code>command</code>:: the Command that has been identified (or nil if not identified yet)
|
411
|
-
# <code>command_options</code>:: options for Command
|
412
|
-
# <code>arguments</code>:: the arguments for Command
|
413
|
-
#
|
414
|
-
# This works by finding the first non-switch/flag argument, and taking that sublist and trying to pick out
|
415
|
-
# flags and switches. After this is done, one of the following is true:
|
416
|
-
# * the sublist is empty - in this case, go again, as there might be more flags to parse
|
417
|
-
# * the sublist has a flag left in it - unknown flag; we bail
|
418
|
-
# * the sublist has a non-flag left in it - this is the command (or the start of the arguments list)
|
419
|
-
#
|
420
|
-
# This sort of does the same thing in two phases; in the first phase, the command hasn't been identified, so
|
421
|
-
# we are looking for global switches and flags, ending when we get the command.
|
422
|
-
#
|
423
|
-
# Once the command has been found, we start looking for command-specific flags and switches.
|
424
|
-
# When those have been found, we know the rest of the argument list is arguments for the command
|
425
|
-
def parse_options_helper(args,global_options,command,command_options,arguments) # :nodoc:
|
426
|
-
non_flag_i = find_non_flag_index(args)
|
427
|
-
all_flags = false
|
428
|
-
if non_flag_i == 0
|
429
|
-
# no flags
|
430
|
-
if !command
|
431
|
-
command_name = args.shift
|
432
|
-
command = find_command(command_name)
|
433
|
-
raise UnknownCommand.new("Unknown command '#{command_name}'") if !command
|
434
|
-
return parse_options_helper(args,
|
435
|
-
global_options,
|
436
|
-
command,
|
437
|
-
Hash.new,
|
438
|
-
arguments)
|
439
|
-
elsif((index = flag_switch_index(args)) >= 0)
|
440
|
-
try_me = args[0..index-1]
|
441
|
-
rest = args[index..args.length]
|
442
|
-
new_args = rest + try_me
|
443
|
-
return parse_options_helper(new_args,
|
444
|
-
global_options,
|
445
|
-
command,
|
446
|
-
Hash.new,
|
447
|
-
arguments)
|
448
|
-
else
|
449
|
-
return global_options,command,command_options,arguments + args
|
450
|
-
end
|
451
|
-
elsif non_flag_i == -1
|
452
|
-
all_flags = true
|
453
|
-
end
|
454
|
-
|
455
|
-
try_me = args[0..non_flag_i]
|
456
|
-
rest = args[(non_flag_i+1)..args.length]
|
457
|
-
if all_flags
|
458
|
-
try_me = args
|
459
|
-
rest = []
|
460
|
-
end
|
461
|
-
|
462
|
-
# Suck up whatever options we can
|
463
|
-
switch_hash = switches
|
464
|
-
flag_hash = flags
|
465
|
-
options = global_options
|
466
|
-
if command
|
467
|
-
switch_hash = command.switches
|
468
|
-
flag_hash = command.flags
|
469
|
-
options = command_options
|
470
|
-
end
|
471
|
-
|
472
|
-
switch_hash.each do |name,switch|
|
473
|
-
value = switch.get_value!(try_me)
|
474
|
-
options[name] = value if !options[name]
|
475
|
-
end
|
476
|
-
|
477
|
-
flag_hash.each do |name,flag|
|
478
|
-
value = flag.get_value!(try_me)
|
479
|
-
# So, there's a case where the first time we request the value for a flag,
|
480
|
-
# we get the default and not the user-provided value. The next time we request
|
481
|
-
# it, we want to override it with the real value.
|
482
|
-
# HOWEVER, sometimes this happens in reverse, so we want to err on taking the
|
483
|
-
# user-provided, non-default value where possible.
|
484
|
-
if value
|
485
|
-
if options[name]
|
486
|
-
options[name] = value if options[name] == flag.default_value
|
487
|
-
else
|
488
|
-
options[name] = value
|
489
|
-
end
|
490
|
-
end
|
491
|
-
end
|
492
|
-
|
493
|
-
if try_me.empty?
|
494
|
-
return [global_options,command,command_options,arguments] if rest.empty?
|
495
|
-
# If we have no more options we've parsed them all
|
496
|
-
# and rest may have more
|
497
|
-
return parse_options_helper(rest,global_options,command,command_options,arguments)
|
498
|
-
else
|
499
|
-
if command
|
500
|
-
check = rest
|
501
|
-
check = rest + try_me if all_flags
|
502
|
-
check.each() do |arg|
|
503
|
-
if arg =~ /^\-\-$/
|
504
|
-
try_me.delete arg
|
505
|
-
break
|
506
|
-
end
|
507
|
-
raise UnknownCommandArgument.new("Unknown option #{arg}",command) if arg =~ /^\-/
|
508
|
-
end
|
509
|
-
return [global_options,command,command_options,try_me + rest]
|
510
|
-
else
|
511
|
-
# Now we have our command name
|
512
|
-
command_name = try_me.shift
|
513
|
-
raise UnknownGlobalArgument.new("Unknown option #{command_name}") if command_name =~ /^\-/
|
514
|
-
|
515
|
-
command = find_command(command_name)
|
516
|
-
raise UnknownCommand.new("Unknown command '#{command_name}'") if !command
|
517
|
-
|
518
|
-
return parse_options_helper(rest,
|
519
|
-
global_options,
|
520
|
-
command,
|
521
|
-
Hash.new,
|
522
|
-
arguments)
|
523
|
-
end
|
524
|
-
end
|
525
|
-
end
|
526
|
-
|
527
|
-
def find_command(name) # :nodoc:
|
528
|
-
sym = name.to_sym
|
529
|
-
return commands[name.to_sym] if commands[sym]
|
530
|
-
commands.each do |command_name,command|
|
531
|
-
return command if (command.aliases && command.aliases.include?(sym))
|
532
|
-
end
|
533
|
-
nil
|
534
|
-
end
|
535
|
-
|
536
|
-
# Checks that the names passed in have not been used in another flag or option
|
537
|
-
def verify_unused(names,flags,switches,context) # :nodoc:
|
538
|
-
names.each do |name|
|
539
|
-
verify_unused_in_option(name,flags,"flag",context)
|
540
|
-
verify_unused_in_option(name,switches,"switch",context)
|
541
|
-
end
|
542
|
-
end
|
543
|
-
|
544
|
-
private
|
545
|
-
|
546
|
-
def verify_unused_in_option(name,option_like,type,context) # :nodoc:
|
547
|
-
return if name.to_s == 'help'
|
548
|
-
raise ArgumentError.new("#{name} has already been specified as a #{type} #{context}") if option_like[name]
|
549
|
-
option_like.each do |one_option_name,one_option|
|
550
|
-
if one_option.aliases
|
551
|
-
raise ArgumentError.new("#{name} has already been specified as an alias of #{type} #{one_option_name} #{context}") if one_option.aliases.include? name
|
552
|
-
end
|
553
|
-
end
|
554
|
-
end
|
555
|
-
|
556
|
-
# Sets the default values for flags based on the configuration
|
557
|
-
def override_defaults_based_on_config(config)
|
558
|
-
config ||= {}
|
559
|
-
config['commands'] ||= {}
|
560
|
-
|
561
|
-
override_default(flags,config)
|
562
|
-
override_default(switches,config)
|
563
|
-
|
564
|
-
commands.each do |command_name,command|
|
565
|
-
command_config = config['commands'][command_name] || {}
|
566
|
-
|
567
|
-
override_default(command.flags,command_config)
|
568
|
-
override_default(command.switches,command_config)
|
569
|
-
end
|
570
|
-
end
|
571
|
-
|
572
|
-
def override_default(tokens,config)
|
573
|
-
tokens.each do |name,token|
|
574
|
-
token.default_value=config[name] if config[name]
|
575
|
-
end
|
576
|
-
end
|
577
|
-
|
578
|
-
desc 'Show this message'
|
579
|
-
switch :help
|
580
|
-
|
581
21
|
end
|