configurable 0.4.2 → 0.5.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 +18 -0
- data/lib/config_parser.rb +46 -19
- data/lib/configurable.rb +8 -9
- data/lib/configurable/class_methods.rb +20 -22
- data/lib/configurable/delegate_hash.rb +30 -7
- data/lib/configurable/module_methods.rb +30 -0
- data/lib/configurable/validation.rb +130 -56
- metadata +4 -3
data/History
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
== 0.5.0 / 2009-05-25
|
2
|
+
|
3
|
+
* fixed io validation to not duplicate IOs
|
4
|
+
* fixed :hidden type for nested configurations
|
5
|
+
* open_io now returns block result
|
6
|
+
* updated Validation parsing of strings into ranges
|
7
|
+
* DelegateHash now has the ability to rebind a receiver
|
8
|
+
* list validation now accepts validation block
|
9
|
+
* added api validation
|
10
|
+
* configs may now be specified in modules and included
|
11
|
+
into classes
|
12
|
+
* ConfigParser can now ignore unknown options
|
13
|
+
* converted num to numeric in Validation (for consistency)
|
14
|
+
* changed 'values' to 'options' in select and list_select
|
15
|
+
* expanded io/open_io to allow integer file descriptors
|
16
|
+
* reworked mapping in DelegateHash to respect indifferent
|
17
|
+
access
|
18
|
+
|
1
19
|
== 0.4.2 / 2009-03-30
|
2
20
|
|
3
21
|
* set delegate default no longer freezes value
|
data/lib/config_parser.rb
CHANGED
@@ -156,22 +156,32 @@ class ConfigParser
|
|
156
156
|
# handles them.
|
157
157
|
attr_reader :switches
|
158
158
|
|
159
|
-
# The
|
160
|
-
|
159
|
+
# The hash receiving configurations produced by parse.
|
160
|
+
attr_accessor :config
|
161
161
|
|
162
|
-
# A hash of default configurations merged into
|
162
|
+
# A hash of default configurations merged into config during parse.
|
163
163
|
attr_reader :default_config
|
164
164
|
|
165
165
|
# Initializes a new ConfigParser and passes it to the block, if given.
|
166
|
-
def initialize
|
166
|
+
def initialize(config={})
|
167
167
|
@options = []
|
168
168
|
@switches = {}
|
169
|
-
@config =
|
169
|
+
@config = config
|
170
170
|
@default_config = {}
|
171
171
|
|
172
172
|
yield(self) if block_given?
|
173
173
|
end
|
174
174
|
|
175
|
+
# Returns the config value for key.
|
176
|
+
def [](key)
|
177
|
+
config[key]
|
178
|
+
end
|
179
|
+
|
180
|
+
# Sets the config value for key.
|
181
|
+
def []=(key, value)
|
182
|
+
config[key] = value
|
183
|
+
end
|
184
|
+
|
175
185
|
# Returns the nested form of config (see ConfigParser.nest). Primarily
|
176
186
|
# useful when nested configurations have been added with add.
|
177
187
|
def nested_config
|
@@ -395,7 +405,9 @@ class ConfigParser
|
|
395
405
|
default = delegate.default(false)
|
396
406
|
|
397
407
|
if delegate.is_nest?
|
398
|
-
|
408
|
+
unless delegate[:type] == :hidden
|
409
|
+
add(default.delegates, key)
|
410
|
+
end
|
399
411
|
else
|
400
412
|
define(key, default, delegate.attributes)
|
401
413
|
end
|
@@ -407,12 +419,27 @@ class ConfigParser
|
|
407
419
|
# string argv is provided, it will be splits into an array using
|
408
420
|
# Shellwords.
|
409
421
|
#
|
410
|
-
#
|
411
|
-
# as self.config.
|
422
|
+
# ==== Options
|
412
423
|
#
|
413
|
-
|
414
|
-
|
415
|
-
|
424
|
+
# clear_config:: clears the currently parsed configs (true)
|
425
|
+
# add_defaults:: adds the default values to config (true)
|
426
|
+
# ignore_unknown_options:: causes unknown options to be ignored (false)
|
427
|
+
#
|
428
|
+
def parse(argv=ARGV, options={})
|
429
|
+
argv = argv.dup unless argv.kind_of?(String)
|
430
|
+
parse!(argv, options)
|
431
|
+
end
|
432
|
+
|
433
|
+
# Same as parse, but removes parsed args from argv.
|
434
|
+
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
|
+
argv = Shellwords.shellwords(argv) if argv.kind_of?(String)
|
416
443
|
args = []
|
417
444
|
|
418
445
|
while !argv.empty?
|
@@ -438,6 +465,11 @@ class ConfigParser
|
|
438
465
|
|
439
466
|
# lookup the option
|
440
467
|
unless option = @switches[$1]
|
468
|
+
if options[:ignore_unknown_options]
|
469
|
+
args << arg
|
470
|
+
next
|
471
|
+
end
|
472
|
+
|
441
473
|
raise "unknown option: #{$1}"
|
442
474
|
end
|
443
475
|
|
@@ -446,15 +478,10 @@ class ConfigParser
|
|
446
478
|
|
447
479
|
default_config.each_pair do |key, default|
|
448
480
|
config[key] = default unless config.has_key?(key)
|
449
|
-
end if
|
481
|
+
end if options[:add_defaults]
|
450
482
|
|
451
|
-
args
|
452
|
-
|
453
|
-
|
454
|
-
# Same as parse, but removes parsed args from argv.
|
455
|
-
def parse!(argv=ARGV, config={}, merge_default=true)
|
456
|
-
result = parse(argv, config, merge_default)
|
457
|
-
argv.kind_of?(Array) ? argv.replace(result) : result
|
483
|
+
argv.replace(args)
|
484
|
+
argv
|
458
485
|
end
|
459
486
|
|
460
487
|
# Converts the options and separators in self into a help string suitable for
|
data/lib/configurable.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'configurable/
|
1
|
+
require 'configurable/module_methods'
|
2
2
|
|
3
3
|
# Configurable enables the specification of configurations within a class
|
4
4
|
# definition.
|
@@ -138,11 +138,6 @@ require 'configurable/class_methods'
|
|
138
138
|
#
|
139
139
|
module Configurable
|
140
140
|
autoload(:Utils, 'configurable/utils')
|
141
|
-
|
142
|
-
# Extends including classes with Configurable::ClassMethods
|
143
|
-
def self.included(mod) # :nodoc:
|
144
|
-
mod.extend ClassMethods if mod.kind_of?(Class)
|
145
|
-
end
|
146
141
|
|
147
142
|
# A DelegateHash bound to self
|
148
143
|
attr_reader :config
|
@@ -176,7 +171,7 @@ module Configurable
|
|
176
171
|
|
177
172
|
# Opens the file specified by io and yield it to the block. If io is an
|
178
173
|
# IO, it will be yielded immediately, and the mode is ignored. Nil io are
|
179
|
-
# simply ignored.
|
174
|
+
# simply ignored.
|
180
175
|
#
|
181
176
|
# === Usage
|
182
177
|
#
|
@@ -202,10 +197,14 @@ module Configurable
|
|
202
197
|
dir = File.dirname(io)
|
203
198
|
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
204
199
|
File.open(io, mode) {|file| yield(file) }
|
205
|
-
when
|
200
|
+
when Integer
|
201
|
+
# note this does not close the io because, as far as I understand,
|
202
|
+
# valid integer file descriptors point to files that are already
|
203
|
+
# open and presumably managed elsewhere
|
204
|
+
yield IO.open(io, mode)
|
205
|
+
when nil then nil
|
206
206
|
else yield(io)
|
207
207
|
end
|
208
|
-
io
|
209
208
|
end
|
210
209
|
|
211
210
|
# Initializes config. Default config values
|
@@ -15,15 +15,6 @@ module Configurable
|
|
15
15
|
# A hash of (key, Delegate) pairs defining the class configurations.
|
16
16
|
attr_reader :configurations
|
17
17
|
|
18
|
-
def self.extended(base) # :nodoc:
|
19
|
-
unless base.instance_variable_defined?(:@source_file)
|
20
|
-
caller[2] =~ Lazydoc::CALLER_REGEXP
|
21
|
-
base.instance_variable_set(:@source_file, File.expand_path($1))
|
22
|
-
end
|
23
|
-
|
24
|
-
base.send(:initialize_configurations).extend(IndifferentAccess)
|
25
|
-
end
|
26
|
-
|
27
18
|
def inherited(child) # :nodoc:
|
28
19
|
unless child.instance_variable_defined?(:@source_file)
|
29
20
|
caller[0] =~ Lazydoc::CALLER_REGEXP
|
@@ -40,23 +31,23 @@ module Configurable
|
|
40
31
|
end
|
41
32
|
|
42
33
|
# Parses configurations from argv in a non-destructive manner by generating
|
43
|
-
# a ConfigParser using the configurations for self.
|
44
|
-
#
|
45
|
-
# config
|
46
|
-
# block, if given, to register additonal options.
|
47
|
-
# arguments that remain after parsing.
|
34
|
+
# a ConfigParser using the configurations for self. Returns an array like
|
35
|
+
# [args, config] where the args are the arguments that remain after parsing,
|
36
|
+
# and config is a hash of the parsed configs. The parser will is yielded to
|
37
|
+
# the block, if given, to register additonal options.
|
48
38
|
#
|
49
39
|
# See ConfigParser#parse for more information.
|
50
|
-
def parse(argv=ARGV,
|
51
|
-
|
52
|
-
parser.add(configurations)
|
53
|
-
yield(parser) if block_given?
|
54
|
-
end.parse(argv, config)
|
40
|
+
def parse(argv=ARGV, options={}) # :yields: parser
|
41
|
+
parse!(argv.dup, options)
|
55
42
|
end
|
56
43
|
|
57
44
|
# Same as parse, but removes parsed args from argv.
|
58
|
-
def parse!(argv=ARGV,
|
59
|
-
|
45
|
+
def parse!(argv=ARGV, options={})
|
46
|
+
parser = ConfigParser.new
|
47
|
+
parser.add(configurations)
|
48
|
+
|
49
|
+
args = parser.parse!(argv, options)
|
50
|
+
[args, parser.config]
|
60
51
|
end
|
61
52
|
|
62
53
|
protected
|
@@ -302,7 +293,14 @@ module Configurable
|
|
302
293
|
else
|
303
294
|
key.to_s.capitalize
|
304
295
|
end
|
305
|
-
|
296
|
+
|
297
|
+
if const_name
|
298
|
+
# this prevents a warning in cases where the nesting
|
299
|
+
# class defines the configurable_class
|
300
|
+
unless const_defined?(const_name) && const_get(const_name) == configurable_class
|
301
|
+
const_set(const_name, configurable_class)
|
302
|
+
end
|
303
|
+
end
|
306
304
|
|
307
305
|
# define instance reader
|
308
306
|
instance_reader = define_attribute_method(:instance_reader, attributes, key) do |attribute|
|
@@ -60,10 +60,10 @@ module Configurable
|
|
60
60
|
# Binds self to the specified receiver. Delegate values are removed from
|
61
61
|
# store and sent to their writer on receiver. If the store has no value
|
62
62
|
# for a delegate key, the delegate default value will be used.
|
63
|
-
def bind(receiver)
|
63
|
+
def bind(receiver, rebind=false)
|
64
64
|
raise ArgumentError, "receiver cannot be nil" if receiver == nil
|
65
65
|
|
66
|
-
if bound?
|
66
|
+
if bound? && !rebind
|
67
67
|
if @receiver == receiver
|
68
68
|
return(self)
|
69
69
|
else
|
@@ -152,10 +152,18 @@ module Configurable
|
|
152
152
|
|
153
153
|
# Returns self as a hash. Any DelegateHash values are recursively
|
154
154
|
# hashified, to account for nesting.
|
155
|
-
def to_hash
|
155
|
+
def to_hash(&block)
|
156
156
|
hash = {}
|
157
157
|
each_pair do |key, value|
|
158
|
-
|
158
|
+
if value.kind_of?(DelegateHash)
|
159
|
+
value = value.to_hash(&block)
|
160
|
+
end
|
161
|
+
|
162
|
+
if block_given?
|
163
|
+
yield(hash, key, value)
|
164
|
+
else
|
165
|
+
hash[key] = value
|
166
|
+
end
|
159
167
|
end
|
160
168
|
hash
|
161
169
|
end
|
@@ -178,15 +186,30 @@ module Configurable
|
|
178
186
|
|
179
187
|
# helper to map delegate values from source to the receiver
|
180
188
|
def map(source) # :nodoc:
|
189
|
+
source_values = {}
|
190
|
+
source.each_key do |key|
|
191
|
+
if delegate = delegates[key]
|
192
|
+
if source_values.has_key?(delegate)
|
193
|
+
key = delegates.keys.find {|k| delegates[k] == delegate }
|
194
|
+
raise "multiple values mapped to #{key.inspect}"
|
195
|
+
end
|
196
|
+
|
197
|
+
source_values[delegate] = source.delete(key)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
181
201
|
delegates.each_pair do |key, delegate|
|
182
202
|
# map the value; if no value is set in the source then use the
|
183
203
|
# delegate default. if map_default is false, then simply skip...
|
184
204
|
# this ensures each config is initialized to a value when bound
|
185
205
|
# UNLESS map_default is set (indicating manual initialization)
|
186
206
|
value = case
|
187
|
-
when
|
188
|
-
|
189
|
-
|
207
|
+
when source_values.has_key?(delegate)
|
208
|
+
source_values[delegate]
|
209
|
+
when delegate[:set_default, true]
|
210
|
+
delegate.default
|
211
|
+
else
|
212
|
+
next
|
190
213
|
end
|
191
214
|
|
192
215
|
receiver.send(delegate.writer, value)
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'configurable/class_methods'
|
2
|
+
|
3
|
+
module Configurable
|
4
|
+
module ModuleMethods
|
5
|
+
|
6
|
+
# Extends including classes with Configurable::ClassMethods
|
7
|
+
def included(mod)
|
8
|
+
mod.extend ClassMethods
|
9
|
+
mod.extend ModuleMethods unless mod.kind_of?(Class)
|
10
|
+
|
11
|
+
unless mod.instance_variable_defined?(:@source_file)
|
12
|
+
caller[1] =~ Lazydoc::CALLER_REGEXP
|
13
|
+
mod.instance_variable_set(:@source_file, File.expand_path($1))
|
14
|
+
end
|
15
|
+
|
16
|
+
unless mod.instance_variable_defined?(:@configurations)
|
17
|
+
mod.send(:initialize_configurations).extend(IndifferentAccess)
|
18
|
+
end
|
19
|
+
|
20
|
+
# add module configurations
|
21
|
+
configurations.each_pair do |key, config|
|
22
|
+
mod.configurations[key] = config.dup
|
23
|
+
end unless self == Configurable
|
24
|
+
|
25
|
+
super
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
extend ModuleMethods
|
30
|
+
end
|
@@ -39,6 +39,18 @@ module Configurable
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
42
|
+
|
43
|
+
# Raised when validate_api fails.
|
44
|
+
class ApiError < ArgumentError
|
45
|
+
def initialize(input, methods)
|
46
|
+
super case
|
47
|
+
when methods.empty?
|
48
|
+
"no api specified"
|
49
|
+
else
|
50
|
+
"expected api #{methods.inspect} for: #{input.inspect}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
42
54
|
|
43
55
|
module_function
|
44
56
|
|
@@ -50,12 +62,18 @@ module Configurable
|
|
50
62
|
end
|
51
63
|
|
52
64
|
# Registers the default attributes of the source as the attributes
|
53
|
-
# of the target.
|
54
|
-
|
55
|
-
|
65
|
+
# of the target. Overridding or additional attributes are merged
|
66
|
+
# to the defaults.
|
67
|
+
def register_as(source, target, attributes={})
|
68
|
+
DEFAULT_ATTRIBUTES[target] = DEFAULT_ATTRIBUTES[source].dup.merge!(attributes)
|
56
69
|
target
|
57
70
|
end
|
58
71
|
|
72
|
+
# Returns the attributes registered to the block.
|
73
|
+
def attributes(block)
|
74
|
+
DEFAULT_ATTRIBUTES[block] || {}
|
75
|
+
end
|
76
|
+
|
59
77
|
# Returns input if it matches any of the validations as in would in a case
|
60
78
|
# statement. Raises a ValidationError otherwise. For example:
|
61
79
|
#
|
@@ -69,9 +87,10 @@ module Configurable
|
|
69
87
|
# end
|
70
88
|
#
|
71
89
|
# A block may be provided to handle invalid inputs; if provided it will be
|
72
|
-
# called and a ValidationError will not be raised
|
73
|
-
# input must be an Array or nil;
|
74
|
-
# otherwise. All inputs are
|
90
|
+
# called with the input and a ValidationError will not be raised unless the
|
91
|
+
# block returns false. Note the validations input must be an Array or nil;
|
92
|
+
# validate will raise an ArgumentError otherwise. All inputs are
|
93
|
+
# considered VALID if validations == nil.
|
75
94
|
def validate(input, validations)
|
76
95
|
case validations
|
77
96
|
when Array
|
@@ -87,10 +106,38 @@ module Configurable
|
|
87
106
|
end
|
88
107
|
|
89
108
|
when nil then input
|
90
|
-
else raise ArgumentError, "validations must be
|
109
|
+
else raise ArgumentError, "validations must be an array of valid inputs or nil"
|
91
110
|
end
|
92
111
|
end
|
93
112
|
|
113
|
+
# Returns input if it responds to all of the specified methods. Raises an
|
114
|
+
# ApiError otherwise. For example:
|
115
|
+
#
|
116
|
+
# validate_api(10, [:to_s, :to_f]) # => 10
|
117
|
+
# validate_api(Object.new, [:to_s, :to_f]) # !> ApiError
|
118
|
+
#
|
119
|
+
# A block may be provided to handle invalid inputs; if provided it will be
|
120
|
+
# called with the input and an ApiError will not be raised unless the
|
121
|
+
# block returns false. Note the methods input must be an Array or nil;
|
122
|
+
# validate_api will raise an ArgumentError otherwise. All inputs are
|
123
|
+
# considered VALID if methods == nil.
|
124
|
+
def validate_api(input, methods)
|
125
|
+
case methods
|
126
|
+
when Array
|
127
|
+
unless methods.all? {|m| input.respond_to?(m) }
|
128
|
+
if block_given? && yield(input)
|
129
|
+
input
|
130
|
+
else
|
131
|
+
raise ApiError.new(input, methods)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
when nil
|
135
|
+
else raise ArgumentError, "methods must be an array or nil"
|
136
|
+
end
|
137
|
+
|
138
|
+
input
|
139
|
+
end
|
140
|
+
|
94
141
|
# Helper to load the input into a valid object. If a valid object is not
|
95
142
|
# loaded as YAML, or if an error occurs, the original input is returned.
|
96
143
|
def load_if_yaml(input, *validations)
|
@@ -110,7 +157,15 @@ module Configurable
|
|
110
157
|
def check(*validations)
|
111
158
|
lambda {|input| validate(input, validations) }
|
112
159
|
end
|
113
|
-
|
160
|
+
|
161
|
+
# Returns a block that calls validate_api using the block input
|
162
|
+
# and methods.
|
163
|
+
def api(*methods)
|
164
|
+
lambda do |input|
|
165
|
+
validate_api(input, methods)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
114
169
|
# Returns a block that loads input strings as YAML, then
|
115
170
|
# calls validate with the result and validations. Non-string
|
116
171
|
# inputs are validated directly.
|
@@ -266,7 +321,20 @@ module Configurable
|
|
266
321
|
# list.call('str') # => ValidationError
|
267
322
|
# list.call(nil) # => ValidationError
|
268
323
|
#
|
269
|
-
def list()
|
324
|
+
def list(&validation)
|
325
|
+
return LIST unless validation
|
326
|
+
|
327
|
+
block = lambda do |input|
|
328
|
+
args = validate(input, [Array]).collect do |arg|
|
329
|
+
arg.kind_of?(String) ? YAML.load(arg) : arg
|
330
|
+
end
|
331
|
+
args.collect! {|arg| validation.call(arg) }
|
332
|
+
args
|
333
|
+
end
|
334
|
+
|
335
|
+
register_as(LIST, block, :validation => attributes(validation))
|
336
|
+
end
|
337
|
+
|
270
338
|
list_block = lambda do |input|
|
271
339
|
validate(input, [Array]).collect do |arg|
|
272
340
|
arg.kind_of?(String) ? YAML.load(arg) : arg
|
@@ -355,26 +423,26 @@ module Configurable
|
|
355
423
|
# Returns a block that checks the input is a number.
|
356
424
|
# String inputs are loaded as yaml first.
|
357
425
|
#
|
358
|
-
#
|
359
|
-
#
|
360
|
-
#
|
361
|
-
#
|
362
|
-
#
|
363
|
-
#
|
364
|
-
#
|
365
|
-
#
|
426
|
+
# numeric.class # => Proc
|
427
|
+
# numeric.call(1.1) # => 1.1
|
428
|
+
# numeric.call(1) # => 1
|
429
|
+
# numeric.call(1e6) # => 1e6
|
430
|
+
# numeric.call('1.1') # => 1.1
|
431
|
+
# numeric.call('1.0e+6') # => 1e6
|
432
|
+
# numeric.call(nil) # => ValidationError
|
433
|
+
# numeric.call('str') # => ValidationError
|
366
434
|
#
|
367
|
-
def
|
435
|
+
def numeric(); NUMERIC; end
|
368
436
|
|
369
|
-
# default attributes {:type => :
|
437
|
+
# default attributes {:type => :numeric, :example => "2, 2.2, 2.0e+2"}
|
370
438
|
NUMERIC = yaml(Numeric)
|
371
|
-
register NUMERIC, :type => :
|
439
|
+
register NUMERIC, :type => :numeric, :example => "2, 2.2, 2.0e+2"
|
372
440
|
|
373
|
-
# Same as
|
441
|
+
# Same as numeric but allows nil:
|
374
442
|
#
|
375
|
-
#
|
376
|
-
#
|
377
|
-
def
|
443
|
+
# numeric_or_nil.call('~') # => nil
|
444
|
+
# numeric_or_nil.call(nil) # => nil
|
445
|
+
def numeric_or_nil(); NUMERIC_OR_NIL; end
|
378
446
|
|
379
447
|
NUMERIC_OR_NIL = yaml(Numeric, nil)
|
380
448
|
register_as NUMERIC, NUMERIC_OR_NIL
|
@@ -429,9 +497,8 @@ module Configurable
|
|
429
497
|
register_as REGEXP, REGEXP_OR_NIL
|
430
498
|
|
431
499
|
# Returns a block that checks the input is a range. String inputs are
|
432
|
-
# loaded as yaml
|
433
|
-
#
|
434
|
-
# before being used to construct a Range.
|
500
|
+
# loaded as yaml (a '!ruby/range' prefix is added before loading if
|
501
|
+
# if it is not specified).
|
435
502
|
#
|
436
503
|
# range.class # => Proc
|
437
504
|
# range.call(1..10) # => 1..10
|
@@ -448,13 +515,10 @@ module Configurable
|
|
448
515
|
def range(); RANGE; end
|
449
516
|
range_block = lambda do |input|
|
450
517
|
if input.kind_of?(String)
|
518
|
+
input = "!ruby/range #{input}" unless input =~ /\A\s*!ruby\/range\s/
|
451
519
|
input = load_if_yaml(input, Range)
|
452
520
|
end
|
453
521
|
|
454
|
-
if input.kind_of?(String) && input =~ /^([^.]+)(\.{2,3})([^.]+)$/
|
455
|
-
input = Range.new(YAML.load($1), YAML.load($3), $2.length == 3)
|
456
|
-
end
|
457
|
-
|
458
522
|
validate(input, [Range])
|
459
523
|
end
|
460
524
|
|
@@ -533,8 +597,8 @@ module Configurable
|
|
533
597
|
TIME_OR_NIL = time_or_nil_block
|
534
598
|
register_as TIME, TIME_OR_NIL
|
535
599
|
|
536
|
-
# Returns a block that only allows the specified
|
537
|
-
# a block that will validate
|
600
|
+
# Returns a block that only allows the specified options. Select can take
|
601
|
+
# a block that will validate the input individual value.
|
538
602
|
#
|
539
603
|
# s = select(1,2,3, &integer)
|
540
604
|
# s.class # => Proc
|
@@ -547,20 +611,23 @@ module Configurable
|
|
547
611
|
#
|
548
612
|
# The select block is registered with these default attributes:
|
549
613
|
#
|
550
|
-
# {:type => :select, :
|
614
|
+
# {:type => :select, :options => options}
|
551
615
|
#
|
552
|
-
def select(*
|
616
|
+
def select(*options, &validation)
|
553
617
|
block = lambda do |input|
|
554
618
|
input = validation.call(input) if validation
|
555
|
-
validate(input,
|
619
|
+
validate(input, options)
|
556
620
|
end
|
557
621
|
|
558
|
-
register(block,
|
622
|
+
register(block,
|
623
|
+
:type => :select,
|
624
|
+
:options => options,
|
625
|
+
:validation => attributes(validation))
|
559
626
|
end
|
560
627
|
|
561
628
|
# Returns a block that checks the input is an array, and that each member
|
562
|
-
# of the array is
|
563
|
-
#
|
629
|
+
# of the array is included in options. A block may be provided to validate
|
630
|
+
# the individual values.
|
564
631
|
#
|
565
632
|
# s = list_select(1,2,3, &integer)
|
566
633
|
# s.class # => Proc
|
@@ -575,27 +642,31 @@ module Configurable
|
|
575
642
|
#
|
576
643
|
# The list_select block is registered with these default attributes:
|
577
644
|
#
|
578
|
-
# {:type => :list_select, :
|
645
|
+
# {:type => :list_select, :options => options, :split => ','}
|
579
646
|
#
|
580
|
-
def list_select(*
|
647
|
+
def list_select(*options, &validation)
|
581
648
|
block = lambda do |input|
|
582
649
|
args = validate(input, [Array])
|
583
650
|
args.collect! {|arg| validation.call(arg) } if validation
|
584
|
-
args.each {|arg| validate(arg,
|
651
|
+
args.each {|arg| validate(arg, options) }
|
585
652
|
end
|
586
653
|
|
587
|
-
register(block,
|
654
|
+
register(block,
|
655
|
+
:type => :list_select,
|
656
|
+
:options => options,
|
657
|
+
:split => ',',
|
658
|
+
:validation => attributes(validation))
|
588
659
|
end
|
589
660
|
|
590
|
-
# Returns a block validating the input is an IO or
|
591
|
-
# are expected to be filepaths
|
661
|
+
# Returns a block validating the input is an IO, a string, or an integer.
|
662
|
+
# String inputs are expected to be filepaths and integer inputs are expected
|
663
|
+
# to be valid file descriptors, but io does not open an IO immediately.
|
592
664
|
#
|
593
665
|
# io.class # => Proc
|
594
666
|
# io.call($stdout) # => $stdout
|
595
667
|
# io.call('/path/to/file') # => '/path/to/file'
|
596
|
-
#
|
668
|
+
# io.call(1) # => 1
|
597
669
|
# io.call(nil) # => ValidationError
|
598
|
-
# io.call(10) # => ValidationError
|
599
670
|
#
|
600
671
|
# An IO api can be specified to allow other objects to be validated. This
|
601
672
|
# is useful for duck-typing an IO when a known subset of methods are needed.
|
@@ -603,15 +674,18 @@ module Configurable
|
|
603
674
|
# array_io = io(:<<)
|
604
675
|
# array_io.call($stdout) # => $stdout
|
605
676
|
# array_io.call([]) # => []
|
606
|
-
# array_io.call(nil) # =>
|
677
|
+
# array_io.call(nil) # => ApiError
|
607
678
|
#
|
679
|
+
# Note that by default io configs will not be duplicated (duplicate IOs
|
680
|
+
# flush separately, and this can result in disorder. see
|
681
|
+
# http://gist.github.com/88808).
|
608
682
|
def io(*api)
|
609
683
|
if api.empty?
|
610
684
|
IO_OR_STRING
|
611
685
|
else
|
612
686
|
block = lambda do |input|
|
613
|
-
validate(input, [IO, String]) do
|
614
|
-
api
|
687
|
+
validate(input, [IO, String, Integer]) do
|
688
|
+
validate_api(input, api)
|
615
689
|
end
|
616
690
|
end
|
617
691
|
|
@@ -619,9 +693,9 @@ module Configurable
|
|
619
693
|
end
|
620
694
|
end
|
621
695
|
|
622
|
-
# default attributes {:type => :io, :example => "/path/to/file"}
|
623
|
-
IO_OR_STRING = check(IO, String)
|
624
|
-
register IO_OR_STRING, :type => :io, :example => "/path/to/file"
|
696
|
+
# default attributes {:type => :io, :duplicate_default => false, :example => "/path/to/file"}
|
697
|
+
IO_OR_STRING = check(IO, String, Integer)
|
698
|
+
register IO_OR_STRING, :type => :io, :duplicate_default => false, :example => "/path/to/file"
|
625
699
|
|
626
700
|
# Same as io but allows nil:
|
627
701
|
#
|
@@ -632,8 +706,8 @@ module Configurable
|
|
632
706
|
IO_STRING_OR_NIL
|
633
707
|
else
|
634
708
|
block = lambda do |input|
|
635
|
-
validate(input, [IO, String, nil]) do
|
636
|
-
api
|
709
|
+
validate(input, [IO, String, Integer, nil]) do
|
710
|
+
validate_api(input, api)
|
637
711
|
end
|
638
712
|
end
|
639
713
|
|
@@ -641,7 +715,7 @@ module Configurable
|
|
641
715
|
end
|
642
716
|
end
|
643
717
|
|
644
|
-
IO_STRING_OR_NIL = check(IO, String, nil)
|
718
|
+
IO_STRING_OR_NIL = check(IO, String, Integer, nil)
|
645
719
|
register_as IO_OR_STRING, IO_STRING_OR_NIL
|
646
720
|
end
|
647
721
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: configurable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simon Chiang
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-05-25 00:00:00 -06:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -20,7 +20,7 @@ dependencies:
|
|
20
20
|
requirements:
|
21
21
|
- - ">="
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: 0.
|
23
|
+
version: 0.9.0
|
24
24
|
version:
|
25
25
|
description:
|
26
26
|
email: simon.a.chiang@gmail.com
|
@@ -45,6 +45,7 @@ files:
|
|
45
45
|
- lib/configurable/delegate.rb
|
46
46
|
- lib/configurable/delegate_hash.rb
|
47
47
|
- lib/configurable/indifferent_access.rb
|
48
|
+
- lib/configurable/module_methods.rb
|
48
49
|
- lib/configurable/validation.rb
|
49
50
|
- lib/configurable/utils.rb
|
50
51
|
- MIT-LICENSE
|