configurable 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History +19 -0
- data/MIT-LICENSE +17 -15
- data/README +112 -40
- data/lib/config_parser.rb +159 -96
- data/lib/config_parser/option.rb +10 -3
- data/lib/config_parser/switch.rb +1 -1
- data/lib/config_parser/utils.rb +52 -18
- data/lib/configurable.rb +128 -36
- data/lib/configurable/class_methods.rb +171 -192
- data/lib/configurable/config.rb +97 -0
- data/lib/configurable/config_hash.rb +198 -0
- data/lib/configurable/indifferent_access.rb +1 -1
- data/lib/configurable/module_methods.rb +7 -17
- data/lib/configurable/nest_config.rb +78 -0
- data/lib/configurable/ordered_hash_patch.rb +85 -0
- data/lib/configurable/utils.rb +25 -32
- data/lib/configurable/validation.rb +69 -31
- data/lib/configurable/version.rb +7 -0
- metadata +13 -8
- data/lib/configurable/delegate.rb +0 -103
- data/lib/configurable/delegate_hash.rb +0 -226
data/History
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
== 0.6.0 / 2009-12-05
|
2
|
+
|
3
|
+
* minor bug fixes in interface
|
4
|
+
* added on! to ConfigParser to specify override options
|
5
|
+
* updates to use latest Lazydoc
|
6
|
+
* added scrub to DelegateHash#to_hash, to remove keys
|
7
|
+
set to the default value
|
8
|
+
* added strbol validation
|
9
|
+
* nil long options are now respected
|
10
|
+
* refactored default_config to defaults in ConfigParser
|
11
|
+
* refactored Validation register syntax
|
12
|
+
* added scan method to ConfigParser
|
13
|
+
* ConfigParser can no longer ignore unknown options
|
14
|
+
* added configurable option breaks to ConfigParser
|
15
|
+
* refactored Delegate to Config, and simplified
|
16
|
+
implementation for nesting to use NestConfig
|
17
|
+
* refactored :duplicate_default attribute to :dup
|
18
|
+
* added undef_config and remove_config methods
|
19
|
+
|
1
20
|
== 0.5.0 / 2009-05-25
|
2
21
|
|
3
22
|
* fixed io validation to not duplicate IOs
|
data/MIT-LICENSE
CHANGED
@@ -1,19 +1,21 @@
|
|
1
1
|
Copyright (c) 2008-2009, Regents of the University of Colorado.
|
2
2
|
|
3
|
-
|
4
|
-
software and associated documentation files (the "Software"), to deal in the Software
|
5
|
-
without restriction, including without limitation the rights to use, copy, modify, merge,
|
6
|
-
publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
7
|
-
to whom the Software is furnished to do so, subject to the following conditions:
|
3
|
+
Copyright (c) 2009, Simon Chiang.
|
8
4
|
|
9
|
-
|
10
|
-
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README
CHANGED
@@ -9,45 +9,103 @@ are inheritable, delegate to methods, and have hash-like access. Configurable
|
|
9
9
|
maps configurations to the command line through ConfigParser and is used by
|
10
10
|
the Tap[http://tap.rubyforge.org] framework.
|
11
11
|
|
12
|
-
Check out these links for development
|
12
|
+
Check out these links for development and bug tracking.
|
13
13
|
|
14
14
|
* Website[http://tap.rubyforge.org/configurable]
|
15
15
|
* Github[http://github.com/bahuvrihi/configurable/tree/master]
|
16
|
-
* Lighthouse[http://bahuvrihi.lighthouseapp.com/projects/21202-configurable/tickets?q=state%3Aopen]
|
17
16
|
* {Google Group}[http://groups.google.com/group/ruby-on-tap]
|
18
17
|
|
19
|
-
|
18
|
+
==== Minimal Example
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
method (an alias for the {Validation}[link:classes/Configurable/Validation.html]
|
24
|
-
module).
|
20
|
+
Include Configurable and declare configurations using the config method.
|
21
|
+
Configs now have accessors and initialize with the default value.
|
25
22
|
|
26
23
|
class ConfigClass
|
27
24
|
include Configurable
|
25
|
+
config :key, 'default', :short => 'k' # a sample config
|
26
|
+
end
|
27
|
+
|
28
|
+
c = ConfigClass.new
|
29
|
+
c.key # => 'default'
|
30
|
+
c.key = 'new value'
|
31
|
+
c.config[:key] # => 'new value'
|
32
|
+
|
33
|
+
Use a ConfigParser to parse configurations from the command line. Non-class
|
34
|
+
options may be defined with (mostly) the same syntax as {OptionParser}[http://www.ruby-doc.org/core/classes/OptionParser.html]:
|
35
|
+
|
36
|
+
parser = ConfigParser.new
|
37
|
+
|
38
|
+
# add class configurations
|
39
|
+
parser.add(ConfigClass.configurations)
|
40
|
+
|
41
|
+
# define an option a-la OptionParser
|
42
|
+
parser.on '-s', '--long ARGUMENT', 'description' do |value|
|
43
|
+
parser[:long] = value
|
44
|
+
end
|
45
|
+
|
46
|
+
parser.parse "one two --key value -s VALUE three"
|
47
|
+
# => ['one', 'two', 'three']
|
48
|
+
|
49
|
+
parser.config
|
50
|
+
# => {
|
51
|
+
# :key => 'value',
|
52
|
+
# :long => 'VALUE'
|
53
|
+
# }
|
54
|
+
|
55
|
+
"\n" + parser.to_s
|
56
|
+
# => %Q{
|
57
|
+
# -k, --key KEY a sample config
|
58
|
+
# -s, --long ARGUMENT description
|
59
|
+
# }
|
28
60
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
61
|
+
== Usage
|
62
|
+
|
63
|
+
The config method is used to declare class configurations. A block may be
|
64
|
+
provided to validate/transform inputs; many standard validations are available
|
65
|
+
through the 'c' method (an alias for the
|
66
|
+
{Validation}[link:classes/Configurable/Validation.html] module).
|
67
|
+
|
68
|
+
class ConfigClass
|
69
|
+
include Configurable
|
70
|
+
|
71
|
+
# basic #
|
72
|
+
|
73
|
+
config :key, 'default' # a simple config
|
74
|
+
config :flag, false, &c.flag # a flag config
|
75
|
+
config :switch, false, &c.switch # a --[no-]switch config
|
76
|
+
config :num, 10, &c.integer # integer only
|
77
|
+
|
78
|
+
# fancy #
|
79
|
+
|
80
|
+
config :range, 1..10, &c.range # specifies a range
|
81
|
+
config :select, 'a', &c.select('a','b') # value must be 'a' or 'b'
|
82
|
+
config :list, [], &c.list # allows a list of entries
|
83
|
+
|
84
|
+
# custom #
|
85
|
+
|
86
|
+
config :upcase, 'default' do |value| # custom transformation
|
35
87
|
value.upcase
|
36
88
|
end
|
37
|
-
|
38
|
-
|
39
|
-
|
89
|
+
|
90
|
+
config :alt, 'default', # alternative flags
|
91
|
+
:short => 's',
|
92
|
+
:long => 'long',
|
93
|
+
:arg_name => 'CUSTOM'
|
94
|
+
|
95
|
+
# Initializes a new instance, setting the overriding configs.
|
96
|
+
def initialize(config={})
|
97
|
+
initialize_config(config)
|
40
98
|
end
|
41
99
|
end
|
42
100
|
|
43
|
-
|
44
|
-
|
101
|
+
ConfigParser uses the config declarations to parse configurations and to make
|
102
|
+
a documented help string:
|
45
103
|
|
46
104
|
parser = ConfigParser.new
|
47
105
|
parser.add(ConfigClass.configurations)
|
48
106
|
|
49
|
-
parser.parse "
|
50
|
-
# => ['
|
107
|
+
parser.parse "a b --key=value --flag --no-switch --num 8 c"
|
108
|
+
# => ['a', 'b', 'c']
|
51
109
|
|
52
110
|
parser.config
|
53
111
|
# => {
|
@@ -55,40 +113,49 @@ them into a documented help string:
|
|
55
113
|
# :flag => true,
|
56
114
|
# :switch => false,
|
57
115
|
# :num => '8',
|
58
|
-
# :range =>
|
59
|
-
# :
|
116
|
+
# :range => 1..10,
|
117
|
+
# :select => 'a',
|
118
|
+
# :list => [],
|
119
|
+
# :upcase => 'default',
|
120
|
+
# :alt => 'default'
|
60
121
|
# }
|
61
122
|
|
62
123
|
"\n" + parser.to_s
|
63
124
|
# => %Q{
|
64
|
-
#
|
125
|
+
# --key KEY a simple config
|
65
126
|
# --flag a flag config
|
66
127
|
# --[no-]switch a --[no-]switch config
|
67
128
|
# --num NUM integer only
|
68
|
-
# --range RANGE range
|
129
|
+
# --range RANGE specifies a range
|
130
|
+
# --select SELECT value must be 'a' or 'b'
|
131
|
+
# --list LIST allows a list of entries
|
69
132
|
# --upcase UPCASE custom transformation
|
133
|
+
# -s, --long CUSTOM alternative flags
|
70
134
|
# }
|
71
135
|
|
72
136
|
Configurable classes typically call initialize_config to set configurations
|
73
137
|
during initialization. The validation/transformation blocks are called as
|
74
|
-
configurations are set.
|
75
|
-
|
138
|
+
configurations are set. Notice how the :num and :upcase configs are translated
|
139
|
+
on the instance:
|
76
140
|
|
77
141
|
c = ConfigClass.new(parser.config)
|
78
142
|
c.config.to_hash
|
79
143
|
# => {
|
80
144
|
# :key => 'value',
|
81
|
-
# :flag => true,
|
82
|
-
# :switch => false,
|
83
|
-
# :num => 8,
|
84
|
-
# :range =>
|
85
|
-
# :
|
145
|
+
# :flag => true,
|
146
|
+
# :switch => false,
|
147
|
+
# :num => 8, # no longer a string
|
148
|
+
# :range => 1..10,
|
149
|
+
# :select => 'a',
|
150
|
+
# :list => [],
|
151
|
+
# :upcase => 'DEFAULT', # no longer downcase
|
152
|
+
# :alt => 'default'
|
86
153
|
# }
|
87
154
|
|
88
155
|
Configurations automatically generate accessors (the blocks are basically
|
89
|
-
writer methods),
|
90
|
-
|
91
|
-
|
156
|
+
writer methods), and may be accessed through the config object. Configurations
|
157
|
+
are validated every time they are set, regardless of whether they are set
|
158
|
+
through an accessor or config.
|
92
159
|
|
93
160
|
c.upcase # => 'DEFAULT'
|
94
161
|
|
@@ -97,6 +164,10 @@ whether they are set through an accessor or config.
|
|
97
164
|
|
98
165
|
c.upcase = 'fiNal Value'
|
99
166
|
c.config[:upcase] # => 'FINAL VALUE'
|
167
|
+
|
168
|
+
c.select = 'b' # ok
|
169
|
+
c.select = 'c' # !> ValidationError
|
170
|
+
c.config[:select] = 'c' # !> ValidationError
|
100
171
|
|
101
172
|
By default config treats string and symbol keys identically, making YAML an
|
102
173
|
obvious choice for configuration files.
|
@@ -108,28 +179,29 @@ obvious choice for configuration files.
|
|
108
179
|
}
|
109
180
|
|
110
181
|
c.reconfigure(YAML.load(yaml_str))
|
111
|
-
c.config.to_hash
|
182
|
+
c.config.to_hash
|
112
183
|
# => {
|
113
184
|
# :key => 'a new value',
|
114
185
|
# :flag => false,
|
115
186
|
# :switch => false,
|
116
187
|
# :num => 8,
|
117
188
|
# :range => 1..100,
|
118
|
-
# :
|
189
|
+
# :select => 'b',
|
190
|
+
# :list => [],
|
191
|
+
# :upcase => 'FINAL VALUE',
|
192
|
+
# :alt => 'default'
|
119
193
|
# }
|
120
194
|
|
121
195
|
See the Configurable module for more details.
|
122
196
|
|
123
197
|
== Installation
|
124
198
|
|
125
|
-
Configurable is available as a gem on
|
199
|
+
Configurable is available as a gem on Gemcutter[http://gemcutter.org/gems/configurable].
|
126
200
|
|
127
201
|
% gem install configurable
|
128
202
|
|
129
203
|
== Info
|
130
204
|
|
131
|
-
|
132
|
-
Developer:: {Simon Chiang}[http://bahuvrihi.wordpress.com], {Biomolecular Structure Program}[http://biomol.uchsc.edu/], {Hansen Lab}[http://hsc-proteomics.uchsc.edu/hansenlab/]
|
133
|
-
Support:: CU Denver School of Medicine Deans Academic Enrichment Fund
|
205
|
+
Developer:: {Simon Chiang}[http://bahuvrihi.wordpress.com]
|
134
206
|
License:: {MIT-Style}[link:files/MIT-LICENSE.html]
|
135
207
|
|
data/lib/config_parser.rb
CHANGED
@@ -147,10 +147,36 @@ class ConfigParser
|
|
147
147
|
|
148
148
|
result
|
149
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
|
150
172
|
end
|
151
173
|
|
152
174
|
include Utils
|
153
|
-
|
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
|
+
|
154
180
|
# A hash of (switch, Option) pairs mapping command line
|
155
181
|
# switches like '-s' or '--long' to the Option that
|
156
182
|
# handles them.
|
@@ -159,16 +185,29 @@ class ConfigParser
|
|
159
185
|
# The hash receiving configurations produced by parse.
|
160
186
|
attr_accessor :config
|
161
187
|
|
162
|
-
# A hash of default configurations merged into config during parse.
|
163
|
-
|
164
|
-
|
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
|
+
|
165
197
|
# Initializes a new ConfigParser and passes it to the block, if given.
|
166
|
-
def initialize(config={})
|
167
|
-
@
|
198
|
+
def initialize(config={}, default_parse_options={})
|
199
|
+
@registry = []
|
168
200
|
@switches = {}
|
169
201
|
@config = config
|
170
|
-
@
|
171
|
-
|
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
|
+
|
172
211
|
yield(self) if block_given?
|
173
212
|
end
|
174
213
|
|
@@ -187,24 +226,36 @@ class ConfigParser
|
|
187
226
|
def nested_config
|
188
227
|
ConfigParser.nest(config)
|
189
228
|
end
|
190
|
-
|
229
|
+
|
191
230
|
# Returns an array of the options registered with self.
|
192
231
|
def options
|
193
|
-
@
|
232
|
+
@registry.select do |opt|
|
194
233
|
opt.kind_of?(Option)
|
195
234
|
end
|
196
235
|
end
|
197
|
-
|
236
|
+
|
198
237
|
# Adds a separator string to self, used in to_s.
|
199
238
|
def separator(str)
|
200
|
-
@
|
239
|
+
@registry << str
|
201
240
|
end
|
202
241
|
|
203
|
-
# Registers the option with self by adding opt to options and mapping
|
204
|
-
#
|
205
|
-
|
206
|
-
|
207
|
-
|
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
|
+
|
208
259
|
opt.switches.each do |switch|
|
209
260
|
case @switches[switch]
|
210
261
|
when opt then next
|
@@ -253,46 +304,12 @@ class ConfigParser
|
|
253
304
|
# end
|
254
305
|
#
|
255
306
|
def on(*args, &block)
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
# determine the kind of argument specified
|
264
|
-
key = case switch
|
265
|
-
when SHORT_OPTION then :short
|
266
|
-
when LONG_OPTION then :long
|
267
|
-
else :desc
|
268
|
-
end
|
269
|
-
|
270
|
-
# check for conflicts
|
271
|
-
if attributes[key]
|
272
|
-
raise ArgumentError, "conflicting #{key} options: [#{attributes[key].inspect}, #{arg.inspect}]"
|
273
|
-
end
|
274
|
-
|
275
|
-
# set the option attributes
|
276
|
-
case key
|
277
|
-
when :long, :short
|
278
|
-
attributes[key] = switch
|
279
|
-
attributes[:arg_name] = arg_name if arg_name
|
280
|
-
else
|
281
|
-
attributes[key] = arg
|
282
|
-
end
|
283
|
-
end
|
284
|
-
|
285
|
-
# check if a switch-style option is specified
|
286
|
-
klass = case
|
287
|
-
when attributes[:long].to_s =~ /^--\[no-\](.*)$/
|
288
|
-
attributes[:long] = "--#{$1}"
|
289
|
-
Switch
|
290
|
-
else
|
291
|
-
Option
|
292
|
-
end
|
293
|
-
|
294
|
-
# instantiate and register the new option
|
295
|
-
register klass.new(attributes, &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
|
296
313
|
end
|
297
314
|
|
298
315
|
# Defines and registers a config-style option with self. Define does not
|
@@ -345,10 +362,10 @@ class ConfigParser
|
|
345
362
|
# key is already set by a different option.
|
346
363
|
def define(key, default_value=nil, attributes={})
|
347
364
|
# check for conflicts and register
|
348
|
-
if
|
365
|
+
if defaults.has_key?(key)
|
349
366
|
raise ArgumentError, "already set by a different option: #{key.inspect}"
|
350
367
|
end
|
351
|
-
|
368
|
+
defaults[key] = default_value
|
352
369
|
|
353
370
|
# ensure setup does not modifiy input attributes
|
354
371
|
attributes = attributes.dup
|
@@ -370,8 +387,7 @@ class ConfigParser
|
|
370
387
|
end
|
371
388
|
|
372
389
|
# Adds a hash of delegates (for example the configurations for a Configurable
|
373
|
-
# class) to self.
|
374
|
-
# attribute, then added like:
|
390
|
+
# class) to self. Configs are added like:
|
375
391
|
#
|
376
392
|
# define(key, delegate.default, delegate.attributes)
|
377
393
|
#
|
@@ -379,16 +395,18 @@ class ConfigParser
|
|
379
395
|
#
|
380
396
|
# When you nest Configurable classes, a special syntax is necessary to
|
381
397
|
# specify nested configurations in a flat format compatible with the
|
382
|
-
# command line. As such, nested delegates
|
383
|
-
#
|
384
|
-
# key as a prefix. For instance:
|
385
|
-
#
|
386
|
-
# delegate_hash = DelegateHash.new(:key => Delegate.new(:reader))
|
387
|
-
# delegates = {}
|
388
|
-
# delegates[:nest] = Delegate.new(:reader, :writer=, delegate_hash)
|
398
|
+
# command line. As such, nested delegates are recursively added with
|
399
|
+
# their key as a prefix. For instance:
|
389
400
|
#
|
401
|
+
# class NestClass
|
402
|
+
# include Configurable
|
403
|
+
# nest :nest do
|
404
|
+
# config :key, 'value'
|
405
|
+
# end
|
406
|
+
# end
|
407
|
+
#
|
390
408
|
# psr = ConfigParser.new
|
391
|
-
# psr.add(
|
409
|
+
# psr.add(NestClass.configurations)
|
392
410
|
# psr.parse('--nest:key value')
|
393
411
|
#
|
394
412
|
# psr.config # => {'nest:key' => 'value'}
|
@@ -402,14 +420,14 @@ class ConfigParser
|
|
402
420
|
def add(delegates, nesting=nil)
|
403
421
|
delegates.each_pair do |key, delegate|
|
404
422
|
key = nesting ? "#{nesting}:#{key}" : key
|
405
|
-
default = delegate.default(false)
|
406
423
|
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
424
|
+
case delegate[:type]
|
425
|
+
when :hidden
|
426
|
+
next
|
427
|
+
when :nest
|
428
|
+
add(delegate.nest_class.configurations, key)
|
411
429
|
else
|
412
|
-
define(key, default, delegate.attributes)
|
430
|
+
define(key, delegate.default, delegate.attributes)
|
413
431
|
end
|
414
432
|
end
|
415
433
|
end
|
@@ -432,63 +450,108 @@ class ConfigParser
|
|
432
450
|
|
433
451
|
# Same as parse, but removes parsed args from argv.
|
434
452
|
def parse!(argv=ARGV, options={})
|
435
|
-
options = {
|
436
|
-
:clear_config => true,
|
437
|
-
:add_defaults => true,
|
438
|
-
:ignore_unknown_options => false
|
439
|
-
}.merge(options)
|
440
|
-
|
441
|
-
config.clear if options[:clear_config]
|
442
453
|
argv = Shellwords.shellwords(argv) if argv.kind_of?(String)
|
454
|
+
|
443
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]
|
444
466
|
|
467
|
+
option_break = options[:option_break]
|
445
468
|
while !argv.empty?
|
446
469
|
arg = argv.shift
|
447
470
|
|
448
471
|
# determine if the arg is an option
|
449
472
|
unless arg.kind_of?(String) && arg[0] == ?-
|
450
|
-
|
473
|
+
yield(arg)
|
451
474
|
next
|
452
475
|
end
|
453
476
|
|
454
477
|
# add the remaining args and break
|
455
478
|
# for the option break
|
456
|
-
if
|
457
|
-
|
479
|
+
if option_break === arg
|
480
|
+
argv.unshift(arg) if options[:keep_break]
|
458
481
|
break
|
459
482
|
end
|
460
483
|
|
461
484
|
# split the arg...
|
462
485
|
# switch= $1
|
463
|
-
# value = $
|
486
|
+
# value = $2
|
464
487
|
arg =~ LONG_OPTION || arg =~ SHORT_OPTION || arg =~ ALT_SHORT_OPTION
|
465
488
|
|
466
489
|
# lookup the option
|
467
490
|
unless option = @switches[$1]
|
468
|
-
|
469
|
-
args << arg
|
470
|
-
next
|
471
|
-
end
|
472
|
-
|
473
|
-
raise "unknown option: #{$1}"
|
491
|
+
raise "unknown option: #{$1 || arg}"
|
474
492
|
end
|
475
493
|
|
476
|
-
option.parse($1, $
|
494
|
+
option.parse($1, $2, argv)
|
477
495
|
end
|
478
496
|
|
479
|
-
|
497
|
+
defaults.each_pair do |key, default|
|
480
498
|
config[key] = default unless config.has_key?(key)
|
481
499
|
end if options[:add_defaults]
|
482
500
|
|
483
|
-
argv.replace(args)
|
484
501
|
argv
|
485
502
|
end
|
486
503
|
|
487
504
|
# Converts the options and separators in self into a help string suitable for
|
488
505
|
# display on the command line.
|
489
506
|
def to_s
|
490
|
-
@
|
507
|
+
@registry.collect do |option|
|
491
508
|
option.to_s.rstrip
|
492
509
|
end.join("\n") + "\n"
|
493
510
|
end
|
511
|
+
|
512
|
+
protected
|
513
|
+
|
514
|
+
# helper to parse an option from an argv. new_option is used
|
515
|
+
# by on and on! to generate options
|
516
|
+
def new_option(argv, &block) # :nodoc:
|
517
|
+
attributes = argv.last.kind_of?(Hash) ? argv.pop : {}
|
518
|
+
argv.each do |arg|
|
519
|
+
# split switch arguments... descriptions
|
520
|
+
# still won't match as a switch even
|
521
|
+
# after a split
|
522
|
+
switch, arg_name = arg.kind_of?(String) ? arg.split(' ', 2) : arg
|
523
|
+
|
524
|
+
# determine the kind of argument specified
|
525
|
+
key = case switch
|
526
|
+
when SHORT_OPTION then :short
|
527
|
+
when LONG_OPTION then :long
|
528
|
+
else :desc
|
529
|
+
end
|
530
|
+
|
531
|
+
# check for conflicts
|
532
|
+
if attributes[key]
|
533
|
+
raise ArgumentError, "conflicting #{key} options: [#{attributes[key].inspect}, #{arg.inspect}]"
|
534
|
+
end
|
535
|
+
|
536
|
+
# set the option attributes
|
537
|
+
case key
|
538
|
+
when :long, :short
|
539
|
+
attributes[key] = switch
|
540
|
+
attributes[:arg_name] = arg_name if arg_name
|
541
|
+
else
|
542
|
+
attributes[key] = arg
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
# check if a switch-style option is specified
|
547
|
+
klass = case
|
548
|
+
when attributes[:long].to_s =~ /^--\[no-\](.*)$/
|
549
|
+
attributes[:long] = "--#{$1}"
|
550
|
+
Switch
|
551
|
+
else
|
552
|
+
Option
|
553
|
+
end
|
554
|
+
|
555
|
+
klass.new(attributes, &block)
|
556
|
+
end
|
494
557
|
end
|