configurable 0.7.0 → 1.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.
- 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
|