configurable 0.4.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|