configurable 0.7.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Help/Command Line.rdoc +141 -0
- data/Help/Config Syntax.rdoc +229 -0
- data/Help/Config Types.rdoc +143 -0
- data/{History → History.rdoc} +9 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +144 -0
- data/lib/configurable.rb +7 -270
- data/lib/configurable/class_methods.rb +344 -367
- data/lib/configurable/config_classes.rb +3 -0
- data/lib/configurable/config_classes/list_config.rb +26 -0
- data/lib/configurable/config_classes/nest_config.rb +50 -0
- data/lib/configurable/config_classes/scalar_config.rb +91 -0
- data/lib/configurable/config_hash.rb +87 -112
- data/lib/configurable/config_types.rb +6 -0
- data/lib/configurable/config_types/boolean_type.rb +22 -0
- data/lib/configurable/config_types/float_type.rb +11 -0
- data/lib/configurable/config_types/integer_type.rb +11 -0
- data/lib/configurable/config_types/nest_type.rb +39 -0
- data/lib/configurable/config_types/object_type.rb +58 -0
- data/lib/configurable/config_types/string_type.rb +15 -0
- data/lib/configurable/conversions.rb +91 -0
- data/lib/configurable/module_methods.rb +0 -1
- data/lib/configurable/version.rb +1 -5
- metadata +73 -30
- data/README +0 -207
- data/lib/cdoc.rb +0 -413
- data/lib/cdoc/cdoc_html_generator.rb +0 -38
- data/lib/cdoc/cdoc_html_template.rb +0 -42
- data/lib/config_parser.rb +0 -563
- data/lib/config_parser/option.rb +0 -108
- data/lib/config_parser/switch.rb +0 -44
- data/lib/config_parser/utils.rb +0 -177
- data/lib/configurable/config.rb +0 -97
- data/lib/configurable/indifferent_access.rb +0 -35
- data/lib/configurable/nest_config.rb +0 -78
- data/lib/configurable/ordered_hash_patch.rb +0 -85
- data/lib/configurable/utils.rb +0 -186
- data/lib/configurable/validation.rb +0 -768
@@ -1,42 +0,0 @@
|
|
1
|
-
require 'rdoc/generators/template/html/html'
|
2
|
-
|
3
|
-
#
|
4
|
-
# Add a template for documenting configurations. Do so by inserting in the
|
5
|
-
# template into the content regions used to template html.
|
6
|
-
# (see 'rdoc/generators/html_generator' line 864)
|
7
|
-
#
|
8
|
-
[
|
9
|
-
RDoc::Page::BODY,
|
10
|
-
RDoc::Page::FILE_PAGE,
|
11
|
-
RDoc::Page::METHOD_LIST].each do |content|
|
12
|
-
|
13
|
-
# this substitution method duplicates the attribute template for configurations
|
14
|
-
# (see rdoc\generators\template\html line 523)
|
15
|
-
#
|
16
|
-
#IF:attributes
|
17
|
-
# <div id="attribute-list">
|
18
|
-
# <h3 class="section-bar">Attributes</h3>
|
19
|
-
#
|
20
|
-
# <div class="name-list">
|
21
|
-
# <table>
|
22
|
-
#START:attributes
|
23
|
-
# <tr class="top-aligned-row context-row">
|
24
|
-
# <td class="context-item-name">%name%</td>
|
25
|
-
#IF:rw
|
26
|
-
# <td class="context-item-value"> [%rw%] </td>
|
27
|
-
#ENDIF:rw
|
28
|
-
#IFNOT:rw
|
29
|
-
# <td class="context-item-value"> </td>
|
30
|
-
#ENDIF:rw
|
31
|
-
# <td class="context-item-desc">%a_desc%</td>
|
32
|
-
# </tr>
|
33
|
-
#END:attributes
|
34
|
-
# </table>
|
35
|
-
# </div>
|
36
|
-
# </div>
|
37
|
-
#ENDIF:attributes
|
38
|
-
#
|
39
|
-
content.gsub!(/IF:attributes.*?ENDIF:attributes/m) do |match|
|
40
|
-
match + "\n\n" + match.gsub(/attributes/, 'configurations').gsub(/Attributes/, 'Configurations')
|
41
|
-
end
|
42
|
-
end
|
data/lib/config_parser.rb
DELETED
@@ -1,563 +0,0 @@
|
|
1
|
-
require 'config_parser/option'
|
2
|
-
require 'config_parser/switch'
|
3
|
-
|
4
|
-
autoload(:Shellwords, 'shellwords')
|
5
|
-
|
6
|
-
# ConfigParser is the Configurable equivalent of
|
7
|
-
# {OptionParser}[http://www.ruby-doc.org/core/classes/OptionParser.html]
|
8
|
-
# and uses a similar, simplified (see below) syntax to declare options.
|
9
|
-
#
|
10
|
-
# opts = {}
|
11
|
-
# parser = ConfigParser.new do |psr|
|
12
|
-
# psr.on "-s", "--long LONG", "a standard option" do |value|
|
13
|
-
# opts[:long] = value
|
14
|
-
# end
|
15
|
-
#
|
16
|
-
# psr.on "--[no-]switch", "a switch" do |value|
|
17
|
-
# opts[:switch] = value
|
18
|
-
# end
|
19
|
-
#
|
20
|
-
# psr.on "--flag", "a flag" do
|
21
|
-
# # note: no value is parsed; the block
|
22
|
-
# # only executes if the flag is found
|
23
|
-
# opts[:flag] = true
|
24
|
-
# end
|
25
|
-
# end
|
26
|
-
#
|
27
|
-
# parser.parse("a b --long arg --switch --flag c") # => ['a', 'b', 'c']
|
28
|
-
# opts # => {:long => 'arg', :switch => true, :flag => true}
|
29
|
-
#
|
30
|
-
# ConfigParser formalizes this pattern of setting values in a hash as they
|
31
|
-
# occur, and adds the ability to specify default values. The syntax is
|
32
|
-
# not quite as friendly as for ordinary options, but meshes well with
|
33
|
-
# Configurable classes:
|
34
|
-
#
|
35
|
-
# psr = ConfigParser.new
|
36
|
-
# psr.define(:key, 'default', :desc => 'a standard option')
|
37
|
-
#
|
38
|
-
# psr.parse('a b --key option c') # => ['a', 'b', 'c']
|
39
|
-
# psr.config # => {:key => 'option'}
|
40
|
-
#
|
41
|
-
# psr.parse('a b c') # => ['a', 'b', 'c']
|
42
|
-
# psr.config # => {:key => 'default'}
|
43
|
-
#
|
44
|
-
# And now directly from a Configurable class, the equivalent of the
|
45
|
-
# original example:
|
46
|
-
#
|
47
|
-
# class ConfigClass
|
48
|
-
# include Configurable
|
49
|
-
#
|
50
|
-
# config :long, 'default', :short => 's' # a standard option
|
51
|
-
# config :switch, false, &c.switch # a switch
|
52
|
-
# config :flag, false, &c.flag # a flag
|
53
|
-
# end
|
54
|
-
#
|
55
|
-
# psr = ConfigParser.new
|
56
|
-
# psr.add(ConfigClass.configurations)
|
57
|
-
#
|
58
|
-
# psr.parse("a b --long arg --switch --flag c") # => ['a', 'b', 'c']
|
59
|
-
# psr.config # => {:long => 'arg', :switch => true, :flag => true}
|
60
|
-
#
|
61
|
-
# psr.parse("a b --long=arg --no-switch c") # => ['a', 'b', 'c']
|
62
|
-
# psr.config # => {:long => 'arg', :switch => false, :flag => false}
|
63
|
-
#
|
64
|
-
# psr.parse("a b -sarg c") # => ['a', 'b', 'c']
|
65
|
-
# psr.config # => {:long => 'arg', :switch => false, :flag => false}
|
66
|
-
#
|
67
|
-
# As you might expect, config attributes are used by ConfigParser to
|
68
|
-
# correctly build a corresponding option. In configurations like :switch,
|
69
|
-
# the block implies the {:type => :switch} attribute and so the
|
70
|
-
# config is made into a switch-style option by ConfigParser.
|
71
|
-
#
|
72
|
-
# Use the to_s method to convert a ConfigParser into command line
|
73
|
-
# documentation:
|
74
|
-
#
|
75
|
-
# "\nconfigurations:\n#{psr.to_s}"
|
76
|
-
# # => %q{
|
77
|
-
# # configurations:
|
78
|
-
# # -s, --long LONG a standard option
|
79
|
-
# # --[no-]switch a switch
|
80
|
-
# # --flag a flag
|
81
|
-
# # }
|
82
|
-
#
|
83
|
-
# ==== Simplifications
|
84
|
-
#
|
85
|
-
# ConfigParser simplifies the OptionParser syntax for 'on'. ConfigParser does
|
86
|
-
# not support automatic conversion of values, gets rid of 'optional' arguments
|
87
|
-
# for options, and only supports a single description string. Hence:
|
88
|
-
#
|
89
|
-
# psr = ConfigParser.new
|
90
|
-
#
|
91
|
-
# # incorrect, raises error as this will look
|
92
|
-
# # like multiple descriptions are specified
|
93
|
-
# psr.on("--delay N",
|
94
|
-
# Float,
|
95
|
-
# "Delay N seconds before executing") # !> ArgumentError
|
96
|
-
#
|
97
|
-
# # correct
|
98
|
-
# psr.on("--delay N", "Delay N seconds before executing") do |value|
|
99
|
-
# value.to_f
|
100
|
-
# end
|
101
|
-
#
|
102
|
-
# # this ALWAYS requires the argument and raises
|
103
|
-
# # an error because multiple descriptions are
|
104
|
-
# # specified
|
105
|
-
# psr.on("-i", "--inplace [EXTENSION]",
|
106
|
-
# "Edit ARGV files in place",
|
107
|
-
# " (make backup if EXTENSION supplied)") # !> ArgumentError
|
108
|
-
#
|
109
|
-
# # correct
|
110
|
-
# psr.on("-i", "--inplace EXTENSION",
|
111
|
-
# "Edit ARGV files in place\n (make backup if EXTENSION supplied)")
|
112
|
-
#
|
113
|
-
#
|
114
|
-
class ConfigParser
|
115
|
-
class << self
|
116
|
-
# Splits and nests compound keys of a hash.
|
117
|
-
#
|
118
|
-
# ConfigParser.nest('key' => 1, 'compound:key' => 2)
|
119
|
-
# # => {
|
120
|
-
# # 'key' => 1,
|
121
|
-
# # 'compound' => {'key' => 2}
|
122
|
-
# # }
|
123
|
-
#
|
124
|
-
# Nest does not do any consistency checking, so be aware that results will
|
125
|
-
# be ambiguous for overlapping compound keys.
|
126
|
-
#
|
127
|
-
# ConfigParser.nest('key' => {}, 'key:overlap' => 'value')
|
128
|
-
# # =? {'key' => {}}
|
129
|
-
# # =? {'key' => {'overlap' => 'value'}}
|
130
|
-
#
|
131
|
-
def nest(hash, split_char=":")
|
132
|
-
result = {}
|
133
|
-
hash.each_pair do |compound_key, value|
|
134
|
-
if compound_key.kind_of?(String)
|
135
|
-
keys = compound_key.split(split_char)
|
136
|
-
|
137
|
-
unless keys.length == 1
|
138
|
-
nested_key = keys.pop
|
139
|
-
nested_hash = keys.inject(result) {|target, key| target[key] ||= {}}
|
140
|
-
nested_hash[nested_key] = value
|
141
|
-
next
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
result[compound_key] = value
|
146
|
-
end
|
147
|
-
|
148
|
-
result
|
149
|
-
end
|
150
|
-
|
151
|
-
# Generates a new parser bound to a specific config. All this really
|
152
|
-
# means is that each time the parser calls parse, configurations will
|
153
|
-
# be added to the config without clearing the config, or adding default
|
154
|
-
# values. This can be useful in signaling situations where a config
|
155
|
-
# needs to be updated multiple times.
|
156
|
-
#
|
157
|
-
# psr = ConfigParser.bind
|
158
|
-
# psr.define('a', 'default')
|
159
|
-
# psr.define('b', 'default')
|
160
|
-
#
|
161
|
-
# psr.parse %w{--a value}
|
162
|
-
# psr.config # => {"a" => "value"}
|
163
|
-
#
|
164
|
-
# psr.parse %w{--b value}
|
165
|
-
# psr.config # => {"a" => "value", "b" => "value"}
|
166
|
-
#
|
167
|
-
def bind(config={})
|
168
|
-
parser = new(config, :clear_config => false, :add_defaults => false)
|
169
|
-
yield(parser) if block_given?
|
170
|
-
parser
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
include Utils
|
175
|
-
|
176
|
-
# Returns an array of the options registered with self, in the order in
|
177
|
-
# which they were added. Separators are also stored in the registry.
|
178
|
-
attr_reader :registry
|
179
|
-
|
180
|
-
# A hash of (switch, Option) pairs mapping command line
|
181
|
-
# switches like '-s' or '--long' to the Option that
|
182
|
-
# handles them.
|
183
|
-
attr_reader :switches
|
184
|
-
|
185
|
-
# The hash receiving configurations produced by parse.
|
186
|
-
attr_accessor :config
|
187
|
-
|
188
|
-
# A hash of default configurations merged into config during parse. These
|
189
|
-
# defaults are defined as options are added to self (via define, add, etc)
|
190
|
-
# and do not need to be manually specified.
|
191
|
-
attr_reader :defaults
|
192
|
-
|
193
|
-
# A hash of default parsing options that adjust the behavior of parse
|
194
|
-
# (see parse).
|
195
|
-
attr_reader :default_parse_options
|
196
|
-
|
197
|
-
# Initializes a new ConfigParser and passes it to the block, if given.
|
198
|
-
def initialize(config={}, default_parse_options={})
|
199
|
-
@registry = []
|
200
|
-
@switches = {}
|
201
|
-
@config = config
|
202
|
-
@defaults = {}
|
203
|
-
@default_parse_options = {
|
204
|
-
:clear_config => true,
|
205
|
-
:add_defaults => true,
|
206
|
-
:ignore_unknown_options => false,
|
207
|
-
:option_break => OPTION_BREAK,
|
208
|
-
:keep_break => false
|
209
|
-
}.merge(default_parse_options)
|
210
|
-
|
211
|
-
yield(self) if block_given?
|
212
|
-
end
|
213
|
-
|
214
|
-
# Returns the config value for key.
|
215
|
-
def [](key)
|
216
|
-
config[key]
|
217
|
-
end
|
218
|
-
|
219
|
-
# Sets the config value for key.
|
220
|
-
def []=(key, value)
|
221
|
-
config[key] = value
|
222
|
-
end
|
223
|
-
|
224
|
-
# Returns the nested form of config (see ConfigParser.nest). Primarily
|
225
|
-
# useful when nested configurations have been added with add.
|
226
|
-
def nested_config
|
227
|
-
ConfigParser.nest(config)
|
228
|
-
end
|
229
|
-
|
230
|
-
# Returns an array of the options registered with self.
|
231
|
-
def options
|
232
|
-
@registry.select do |opt|
|
233
|
-
opt.kind_of?(Option)
|
234
|
-
end
|
235
|
-
end
|
236
|
-
|
237
|
-
# Adds a separator string to self, used in to_s.
|
238
|
-
def separator(str)
|
239
|
-
@registry << str
|
240
|
-
end
|
241
|
-
|
242
|
-
# Registers the option with self by adding opt to options and mapping the
|
243
|
-
# opt switches. Raises an error for conflicting switches.
|
244
|
-
#
|
245
|
-
# If override is specified, options with conflicting switches are removed
|
246
|
-
# and no error is raised. Note that this may remove multiple options.
|
247
|
-
def register(opt, override=false)
|
248
|
-
if override
|
249
|
-
existing = opt.switches.collect do |switch|
|
250
|
-
@switches.delete(switch)
|
251
|
-
end
|
252
|
-
@registry -= existing
|
253
|
-
end
|
254
|
-
|
255
|
-
unless @registry.include?(opt)
|
256
|
-
@registry << opt
|
257
|
-
end
|
258
|
-
|
259
|
-
opt.switches.each do |switch|
|
260
|
-
case @switches[switch]
|
261
|
-
when opt then next
|
262
|
-
when nil then @switches[switch] = opt
|
263
|
-
else raise ArgumentError, "switch is already mapped to a different option: #{switch}"
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
opt
|
268
|
-
end
|
269
|
-
|
270
|
-
# Constructs an Option using args and registers it with self. Args may
|
271
|
-
# contain (in any order) a short switch, a long switch, and a description
|
272
|
-
# string. Either the short or long switch may signal that the option
|
273
|
-
# should take an argument by providing an argument name.
|
274
|
-
#
|
275
|
-
# psr = ConfigParser.new
|
276
|
-
#
|
277
|
-
# # this option takes an argument
|
278
|
-
# psr.on('-s', '--long ARG_NAME', 'description') do |value|
|
279
|
-
# # ...
|
280
|
-
# end
|
281
|
-
#
|
282
|
-
# # so does this one
|
283
|
-
# psr.on('-o ARG_NAME', 'description') do |value|
|
284
|
-
# # ...
|
285
|
-
# end
|
286
|
-
#
|
287
|
-
# # this option does not
|
288
|
-
# psr.on('-f', '--flag') do
|
289
|
-
# # ...
|
290
|
-
# end
|
291
|
-
#
|
292
|
-
# A switch-style option can be specified by prefixing the long switch with
|
293
|
-
# '--[no-]'. Switch options will pass true to the block for the positive
|
294
|
-
# form and false for the negative form.
|
295
|
-
#
|
296
|
-
# psr.on('--[no-]switch') do |value|
|
297
|
-
# # ...
|
298
|
-
# end
|
299
|
-
#
|
300
|
-
# Args may also contain a trailing hash defining all or part of the option:
|
301
|
-
#
|
302
|
-
# psr.on('-k', :long => '--key', :desc => 'description')
|
303
|
-
# # ...
|
304
|
-
# end
|
305
|
-
#
|
306
|
-
def on(*args, &block)
|
307
|
-
register new_option(args, &block)
|
308
|
-
end
|
309
|
-
|
310
|
-
# Same as on, but overrides options with overlapping switches.
|
311
|
-
def on!(*args, &block)
|
312
|
-
register new_option(args, &block), true
|
313
|
-
end
|
314
|
-
|
315
|
-
# Defines and registers a config-style option with self. Define does not
|
316
|
-
# take a block; the default value will be added to config, and any parsed
|
317
|
-
# value will override the default. Normally the key will be turned into
|
318
|
-
# the long switch; specify an alternate long, a short, description, etc
|
319
|
-
# using attributes.
|
320
|
-
#
|
321
|
-
# psr = ConfigParser.new
|
322
|
-
# psr.define(:one, 'default')
|
323
|
-
# psr.define(:two, 'default', :long => '--long', :short => '-s')
|
324
|
-
#
|
325
|
-
# psr.parse("--one one --long two")
|
326
|
-
# psr.config # => {:one => 'one', :two => 'two'}
|
327
|
-
#
|
328
|
-
# Define support several types of configurations that define a special
|
329
|
-
# block to handle the values parsed from the command line. See the
|
330
|
-
# 'setup_<type>' methods in Utils. Any type with a corresponding setup
|
331
|
-
# method is valid:
|
332
|
-
#
|
333
|
-
# psr = ConfigParser.new
|
334
|
-
# psr.define(:flag, false, :type => :flag)
|
335
|
-
# psr.define(:switch, false, :type => :switch)
|
336
|
-
# psr.define(:list, [], :type => :list)
|
337
|
-
#
|
338
|
-
# psr.parse("--flag --switch --list one --list two --list three")
|
339
|
-
# psr.config # => {:flag => true, :switch => true, :list => ['one', 'two', 'three']}
|
340
|
-
#
|
341
|
-
# New, valid types may be added by implementing new setup_<type> methods
|
342
|
-
# following this pattern:
|
343
|
-
#
|
344
|
-
# module SpecialType
|
345
|
-
# def setup_special(key, default_value, attributes)
|
346
|
-
# # modify attributes if necessary
|
347
|
-
# attributes[:long] = "--#{key}"
|
348
|
-
# attributes[:arg_name] = 'ARG_NAME'
|
349
|
-
#
|
350
|
-
# # return a block handling the input
|
351
|
-
# lambda {|input| config[key] = input.reverse }
|
352
|
-
# end
|
353
|
-
# end
|
354
|
-
#
|
355
|
-
# psr = ConfigParser.new.extend SpecialType
|
356
|
-
# psr.define(:opt, false, :type => :special)
|
357
|
-
#
|
358
|
-
# psr.parse("--opt value")
|
359
|
-
# psr.config # => {:opt => 'eulav'}
|
360
|
-
#
|
361
|
-
# The :hidden type causes no configuration to be defined. Raises an error if
|
362
|
-
# key is already set by a different option.
|
363
|
-
def define(key, default_value=nil, attributes={})
|
364
|
-
# check for conflicts and register
|
365
|
-
if defaults.has_key?(key)
|
366
|
-
raise ArgumentError, "already set by a different option: #{key.inspect}"
|
367
|
-
end
|
368
|
-
defaults[key] = default_value
|
369
|
-
|
370
|
-
# ensure setup does not modifiy input attributes
|
371
|
-
attributes = attributes.dup
|
372
|
-
|
373
|
-
block = case attributes[:type]
|
374
|
-
when :switch then setup_switch(key, default_value, attributes)
|
375
|
-
when :flag then setup_flag(key, default_value, attributes)
|
376
|
-
when :list, :list_select then setup_list(key, attributes)
|
377
|
-
when :hidden then return nil
|
378
|
-
else
|
379
|
-
if respond_to?("setup_#{attributes[:type]}")
|
380
|
-
send("setup_#{attributes[:type]}", key, default_value, attributes)
|
381
|
-
else
|
382
|
-
setup_option(key, attributes)
|
383
|
-
end
|
384
|
-
end
|
385
|
-
|
386
|
-
on(attributes, &block)
|
387
|
-
end
|
388
|
-
|
389
|
-
# Adds a hash of delegates (for example the configurations for a Configurable
|
390
|
-
# class) to self. Configs are added like:
|
391
|
-
#
|
392
|
-
# define(key, delegate.default, delegate.attributes)
|
393
|
-
#
|
394
|
-
# ==== Nesting
|
395
|
-
#
|
396
|
-
# When you nest Configurable classes, a special syntax is necessary to
|
397
|
-
# specify nested configurations in a flat format compatible with the
|
398
|
-
# command line. As such, nested delegates are recursively added with
|
399
|
-
# their key as a prefix. For instance:
|
400
|
-
#
|
401
|
-
# class NestClass
|
402
|
-
# include Configurable
|
403
|
-
# nest :nest do
|
404
|
-
# config :key, 'value'
|
405
|
-
# end
|
406
|
-
# end
|
407
|
-
#
|
408
|
-
# psr = ConfigParser.new
|
409
|
-
# psr.add(NestClass.configurations)
|
410
|
-
# psr.parse('--nest:key value')
|
411
|
-
#
|
412
|
-
# psr.config # => {'nest:key' => 'value'}
|
413
|
-
# psr.nested_config # => {'nest' => {'key' => 'value'}}
|
414
|
-
#
|
415
|
-
# Side note: The fact that all the keys end up as strings underscores
|
416
|
-
# the importance of having indifferent access for delegates. If you
|
417
|
-
# set use_indifferent_access(false), be prepared to symbolize nested
|
418
|
-
# keys as necessary.
|
419
|
-
#
|
420
|
-
def add(delegates, nesting=nil)
|
421
|
-
delegates.each_pair do |key, delegate|
|
422
|
-
key = nesting ? "#{nesting}:#{key}" : key
|
423
|
-
|
424
|
-
case delegate[:type]
|
425
|
-
when :hidden
|
426
|
-
next
|
427
|
-
when :nest
|
428
|
-
add(delegate.nest_class.configurations, key)
|
429
|
-
else
|
430
|
-
define(key, delegate.default, delegate.attributes)
|
431
|
-
end
|
432
|
-
end
|
433
|
-
end
|
434
|
-
|
435
|
-
# Parses options from argv in a non-destructive manner and returns an
|
436
|
-
# array of arguments remaining after options have been removed. If a
|
437
|
-
# string argv is provided, it will be splits into an array using
|
438
|
-
# Shellwords.
|
439
|
-
#
|
440
|
-
# ==== Options
|
441
|
-
#
|
442
|
-
# clear_config:: clears the currently parsed configs (true)
|
443
|
-
# add_defaults:: adds the default values to config (true)
|
444
|
-
# ignore_unknown_options:: causes unknown options to be ignored (false)
|
445
|
-
#
|
446
|
-
def parse(argv=ARGV, options={})
|
447
|
-
argv = argv.dup unless argv.kind_of?(String)
|
448
|
-
parse!(argv, options)
|
449
|
-
end
|
450
|
-
|
451
|
-
# Same as parse, but removes parsed args from argv.
|
452
|
-
def parse!(argv=ARGV, options={})
|
453
|
-
argv = Shellwords.shellwords(argv) if argv.kind_of?(String)
|
454
|
-
|
455
|
-
args = []
|
456
|
-
remainder = scan(argv, options) {|arg| args << arg}
|
457
|
-
args.concat(remainder)
|
458
|
-
argv.replace(args)
|
459
|
-
|
460
|
-
argv
|
461
|
-
end
|
462
|
-
|
463
|
-
def scan(argv=ARGV, options={})
|
464
|
-
options = default_parse_options.merge(options)
|
465
|
-
config.clear if options[:clear_config]
|
466
|
-
|
467
|
-
option_break = options[:option_break]
|
468
|
-
while !argv.empty?
|
469
|
-
arg = argv.shift
|
470
|
-
|
471
|
-
# determine if the arg is an option
|
472
|
-
unless arg.kind_of?(String) && arg[0] == ?-
|
473
|
-
yield(arg)
|
474
|
-
next
|
475
|
-
end
|
476
|
-
|
477
|
-
# add the remaining args and break
|
478
|
-
# for the option break
|
479
|
-
if option_break === arg
|
480
|
-
argv.unshift(arg) if options[:keep_break]
|
481
|
-
break
|
482
|
-
end
|
483
|
-
|
484
|
-
# split the arg...
|
485
|
-
# switch= $1
|
486
|
-
# value = $2
|
487
|
-
arg =~ LONG_OPTION || arg =~ SHORT_OPTION || arg =~ ALT_SHORT_OPTION
|
488
|
-
|
489
|
-
# lookup the option
|
490
|
-
unless option = @switches[$1]
|
491
|
-
raise "unknown option: #{$1 || arg}"
|
492
|
-
end
|
493
|
-
|
494
|
-
option.parse($1, $2, argv)
|
495
|
-
end
|
496
|
-
|
497
|
-
defaults.each_pair do |key, default|
|
498
|
-
config[key] = default unless config.has_key?(key)
|
499
|
-
end if options[:add_defaults]
|
500
|
-
|
501
|
-
argv
|
502
|
-
end
|
503
|
-
|
504
|
-
def warn_ignored_args(args)
|
505
|
-
if args && !args.empty?
|
506
|
-
warn "ignoring args: #{args.inspect}"
|
507
|
-
end
|
508
|
-
end
|
509
|
-
|
510
|
-
# Converts the options and separators in self into a help string suitable for
|
511
|
-
# display on the command line.
|
512
|
-
def to_s
|
513
|
-
@registry.collect do |option|
|
514
|
-
option.to_s.rstrip
|
515
|
-
end.join("\n") + "\n"
|
516
|
-
end
|
517
|
-
|
518
|
-
protected
|
519
|
-
|
520
|
-
# helper to parse an option from an argv. new_option is used
|
521
|
-
# by on and on! to generate options
|
522
|
-
def new_option(argv, &block) # :nodoc:
|
523
|
-
attributes = argv.last.kind_of?(Hash) ? argv.pop : {}
|
524
|
-
argv.each do |arg|
|
525
|
-
# split switch arguments... descriptions
|
526
|
-
# still won't match as a switch even
|
527
|
-
# after a split
|
528
|
-
switch, arg_name = arg.kind_of?(String) ? arg.split(' ', 2) : arg
|
529
|
-
|
530
|
-
# determine the kind of argument specified
|
531
|
-
key = case switch
|
532
|
-
when SHORT_OPTION then :short
|
533
|
-
when LONG_OPTION then :long
|
534
|
-
else :desc
|
535
|
-
end
|
536
|
-
|
537
|
-
# check for conflicts
|
538
|
-
if attributes[key]
|
539
|
-
raise ArgumentError, "conflicting #{key} options: [#{attributes[key].inspect}, #{arg.inspect}]"
|
540
|
-
end
|
541
|
-
|
542
|
-
# set the option attributes
|
543
|
-
case key
|
544
|
-
when :long, :short
|
545
|
-
attributes[key] = switch
|
546
|
-
attributes[:arg_name] = arg_name if arg_name
|
547
|
-
else
|
548
|
-
attributes[key] = arg
|
549
|
-
end
|
550
|
-
end
|
551
|
-
|
552
|
-
# check if a switch-style option is specified
|
553
|
-
klass = case
|
554
|
-
when attributes[:long].to_s =~ /^--\[no-\](.*)$/
|
555
|
-
attributes[:long] = "--#{$1}"
|
556
|
-
Switch
|
557
|
-
else
|
558
|
-
Option
|
559
|
-
end
|
560
|
-
|
561
|
-
klass.new(attributes, &block)
|
562
|
-
end
|
563
|
-
end
|