configurable 0.5.0 → 0.6.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/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
|