configurable 0.4.1 → 0.4.2
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 +9 -0
- data/lib/config_parser.rb +2 -3
- data/lib/config_parser/utils.rb +4 -8
- data/lib/configurable.rb +34 -0
- data/lib/configurable/delegate.rb +23 -10
- data/lib/configurable/validation.rb +197 -30
- metadata +2 -2
data/History
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
== 0.4.2 / 2009-03-30
|
2
|
+
|
3
|
+
* set delegate default no longer freezes value
|
4
|
+
* added :duplicate_default attribute to turn off default
|
5
|
+
value duplication
|
6
|
+
* standardized formatting of argument names
|
7
|
+
* added select and list_select validations
|
8
|
+
* added io validation + open_io method
|
9
|
+
|
1
10
|
== 0.4.1 / 2009-03-23
|
2
11
|
|
3
12
|
* Simplified internal API for nesting (removed initialize_<key>)
|
data/lib/config_parser.rb
CHANGED
@@ -346,14 +346,13 @@ class ConfigParser
|
|
346
346
|
block = case attributes[:type]
|
347
347
|
when :switch then setup_switch(key, default_value, attributes)
|
348
348
|
when :flag then setup_flag(key, default_value, attributes)
|
349
|
-
when :list
|
349
|
+
when :list, :list_select then setup_list(key, attributes)
|
350
350
|
when :hidden then return nil
|
351
|
-
when nil then setup_option(key, attributes)
|
352
351
|
else
|
353
352
|
if respond_to?("setup_#{attributes[:type]}")
|
354
353
|
send("setup_#{attributes[:type]}", key, default_value, attributes)
|
355
354
|
else
|
356
|
-
|
355
|
+
setup_option(key, attributes)
|
357
356
|
end
|
358
357
|
end
|
359
358
|
|
data/lib/config_parser/utils.rb
CHANGED
@@ -120,19 +120,15 @@ class ConfigParser
|
|
120
120
|
# Attributes:
|
121
121
|
#
|
122
122
|
# :long the long key ("--key")
|
123
|
-
# :arg_name the argument name ("KEY"
|
123
|
+
# :arg_name the argument name ("KEY")
|
124
124
|
# :split the split character
|
125
125
|
#
|
126
126
|
def setup_list(key, attributes={})
|
127
127
|
attributes[:long] ||= "--#{key}"
|
128
|
+
attributes[:long].to_s =~ /^(--)?(.*)$/
|
129
|
+
attributes[:arg_name] ||= $2.upcase
|
128
130
|
|
129
|
-
|
130
|
-
attributes[:arg_name] ||= %w{A B C}.join(split)
|
131
|
-
else
|
132
|
-
attributes[:long].to_s =~ /^(--)?(.*)$/
|
133
|
-
attributes[:arg_name] ||= $2.upcase
|
134
|
-
end
|
135
|
-
|
131
|
+
split = attributes[:split]
|
136
132
|
n = attributes[:n]
|
137
133
|
|
138
134
|
lambda do |value|
|
data/lib/configurable.rb
CHANGED
@@ -173,6 +173,40 @@ module Configurable
|
|
173
173
|
end
|
174
174
|
|
175
175
|
protected
|
176
|
+
|
177
|
+
# Opens the file specified by io and yield it to the block. If io is an
|
178
|
+
# IO, it will be yielded immediately, and the mode is ignored. Nil io are
|
179
|
+
# simply ignored. The input io is always returned.
|
180
|
+
#
|
181
|
+
# === Usage
|
182
|
+
#
|
183
|
+
# open_io is used to compliment the io validation, to ensure that if a file
|
184
|
+
# is specified, it will be closed.
|
185
|
+
#
|
186
|
+
# class IoSample
|
187
|
+
# include Configurable
|
188
|
+
# config :output, $stdout, &c.io # can be an io or filepath
|
189
|
+
#
|
190
|
+
# def say_hello
|
191
|
+
# open_io(output, 'w') do |io|
|
192
|
+
# io << 'hello!'
|
193
|
+
# end
|
194
|
+
# end
|
195
|
+
# end
|
196
|
+
#
|
197
|
+
# In short, this method provides a way to responsibly handle IO and file
|
198
|
+
# configurations.
|
199
|
+
def open_io(io, mode='r')
|
200
|
+
case io
|
201
|
+
when String
|
202
|
+
dir = File.dirname(io)
|
203
|
+
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
204
|
+
File.open(io, mode) {|file| yield(file) }
|
205
|
+
when nil
|
206
|
+
else yield(io)
|
207
|
+
end
|
208
|
+
io
|
209
|
+
end
|
176
210
|
|
177
211
|
# Initializes config. Default config values
|
178
212
|
# are overridden as specified by overrides.
|
@@ -22,23 +22,25 @@ module Configurable
|
|
22
22
|
# The writer method, by default key=
|
23
23
|
attr_reader :writer
|
24
24
|
|
25
|
-
# An hash of metadata for self, used to present the
|
26
|
-
#
|
27
|
-
#
|
25
|
+
# An hash of metadata for self, used to present the delegate in different
|
26
|
+
# contexts (ex on the command line, in a web form, or a desktop app).
|
27
|
+
# Note that attributes should be set through []= and not through this
|
28
|
+
# reader.
|
28
29
|
attr_reader :attributes
|
29
30
|
|
30
31
|
# Initializes a new Delegate with the specified key and default value.
|
31
32
|
def initialize(reader, writer="#{reader}=", default=nil, attributes={})
|
33
|
+
@attributes = attributes
|
34
|
+
|
32
35
|
self.default = default
|
33
36
|
self.reader = reader
|
34
37
|
self.writer = writer
|
35
|
-
|
36
|
-
@attributes = attributes
|
37
38
|
end
|
38
39
|
|
39
40
|
# Sets the value of an attribute.
|
40
41
|
def []=(key, value)
|
41
42
|
attributes[key] = value
|
43
|
+
reset_duplicable if key == :duplicate_default
|
42
44
|
end
|
43
45
|
|
44
46
|
# Returns the value for the specified attribute, or
|
@@ -49,13 +51,14 @@ module Configurable
|
|
49
51
|
|
50
52
|
# Sets the default value for self.
|
51
53
|
def default=(value)
|
52
|
-
@
|
53
|
-
|
54
|
+
@default = value
|
55
|
+
reset_duplicable
|
54
56
|
end
|
55
57
|
|
56
|
-
# Returns the default value, or a duplicate of the default
|
57
|
-
# value
|
58
|
-
#
|
58
|
+
# Returns the default value, or a duplicate of the default value if specified.
|
59
|
+
# The default value will not be duplicated unless duplicable (see
|
60
|
+
# Delegate.duplicable_value?). Duplication can also be turned off by
|
61
|
+
# specifying self[:duplicate_default] = false.
|
59
62
|
def default(duplicate=true)
|
60
63
|
duplicate && @duplicable ? @default.dup : @default
|
61
64
|
end
|
@@ -86,5 +89,15 @@ module Configurable
|
|
86
89
|
self.writer == another.writer &&
|
87
90
|
self.default(false) == another.default(false)
|
88
91
|
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
# resets marker indiciating whether or not a default value is duplicable
|
96
|
+
def reset_duplicable # :nodoc:
|
97
|
+
@duplicable = case attributes[:duplicate_default]
|
98
|
+
when true, nil then Delegate.duplicable_value?(@default)
|
99
|
+
else false
|
100
|
+
end
|
101
|
+
end
|
89
102
|
end
|
90
103
|
end
|
@@ -46,6 +46,14 @@ module Configurable
|
|
46
46
|
# in Configurable::DEFAULT_ATTRIBUTES.
|
47
47
|
def register(block, attributes)
|
48
48
|
DEFAULT_ATTRIBUTES[block] = attributes
|
49
|
+
block
|
50
|
+
end
|
51
|
+
|
52
|
+
# Registers the default attributes of the source as the attributes
|
53
|
+
# of the target. Attributes are duplicated so they may be modifed.
|
54
|
+
def register_as(source, target)
|
55
|
+
DEFAULT_ATTRIBUTES[target] = DEFAULT_ATTRIBUTES[source].dup
|
56
|
+
target
|
49
57
|
end
|
50
58
|
|
51
59
|
# Returns input if it matches any of the validations as in would in a case
|
@@ -71,9 +79,9 @@ module Configurable
|
|
71
79
|
case input
|
72
80
|
when *validations then input
|
73
81
|
else
|
74
|
-
if block_given?
|
75
|
-
|
76
|
-
else
|
82
|
+
if block_given? && yield(input)
|
83
|
+
input
|
84
|
+
else
|
77
85
|
raise ValidationError.new(input, validations)
|
78
86
|
end
|
79
87
|
end
|
@@ -82,7 +90,21 @@ module Configurable
|
|
82
90
|
else raise ArgumentError, "validations must be nil, or an array of valid inputs"
|
83
91
|
end
|
84
92
|
end
|
85
|
-
|
93
|
+
|
94
|
+
# Helper to load the input into a valid object. If a valid object is not
|
95
|
+
# loaded as YAML, or if an error occurs, the original input is returned.
|
96
|
+
def load_if_yaml(input, *validations)
|
97
|
+
begin
|
98
|
+
yaml = YAML.load(input)
|
99
|
+
case yaml
|
100
|
+
when *validations then yaml
|
101
|
+
else input
|
102
|
+
end
|
103
|
+
rescue(ArgumentError)
|
104
|
+
input
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
86
108
|
# Returns a block that calls validate using the block input
|
87
109
|
# and validations.
|
88
110
|
def check(*validations)
|
@@ -127,8 +149,11 @@ module Configurable
|
|
127
149
|
input = validate(input, [String])
|
128
150
|
eval %Q{"#{input}"}
|
129
151
|
end
|
152
|
+
|
153
|
+
# default attributes {:type => :string, :example => "string"}
|
130
154
|
STRING = string_validation_block
|
131
|
-
|
155
|
+
register STRING, :type => :string, :example => "string"
|
156
|
+
|
132
157
|
# Same as string but allows nil. Note the special
|
133
158
|
# behavior of the nil string '~' -- rather than
|
134
159
|
# being treated as a string, it is processed as nil
|
@@ -145,8 +170,10 @@ module Configurable
|
|
145
170
|
else eval %Q{"#{input}"}
|
146
171
|
end
|
147
172
|
end
|
173
|
+
|
148
174
|
STRING_OR_NIL = string_or_nil_validation_block
|
149
|
-
|
175
|
+
register_as STRING, STRING_OR_NIL
|
176
|
+
|
150
177
|
# Returns a block that checks the input is a symbol.
|
151
178
|
# String inputs are loaded as yaml first.
|
152
179
|
#
|
@@ -157,15 +184,20 @@ module Configurable
|
|
157
184
|
# symbol.call('str') # => ValidationError
|
158
185
|
#
|
159
186
|
def symbol(); SYMBOL; end
|
187
|
+
|
188
|
+
# default attributes {:type => :symbol, :example => ":sym"}
|
160
189
|
SYMBOL = yaml(Symbol)
|
161
|
-
|
190
|
+
register SYMBOL, :type => :symbol, :example => ":sym"
|
191
|
+
|
162
192
|
# Same as symbol but allows nil:
|
163
193
|
#
|
164
194
|
# symbol_or_nil.call('~') # => nil
|
165
195
|
# symbol_or_nil.call(nil) # => nil
|
166
196
|
def symbol_or_nil(); SYMBOL_OR_NIL; end
|
197
|
+
|
167
198
|
SYMBOL_OR_NIL = yaml(Symbol, nil)
|
168
|
-
|
199
|
+
register_as SYMBOL, SYMBOL_OR_NIL
|
200
|
+
|
169
201
|
# Returns a block that checks the input is true, false or nil.
|
170
202
|
# String inputs are loaded as yaml first.
|
171
203
|
#
|
@@ -182,7 +214,10 @@ module Configurable
|
|
182
214
|
# boolean.call("str") # => ValidationError
|
183
215
|
#
|
184
216
|
def boolean(); BOOLEAN; end
|
217
|
+
|
218
|
+
# default attributes {:type => :boolean, :example => "true, yes"}
|
185
219
|
BOOLEAN = yaml(true, false, nil)
|
220
|
+
register BOOLEAN, :type => :boolean, :example => "true, yes"
|
186
221
|
|
187
222
|
# Same as boolean.
|
188
223
|
def switch(); SWITCH; end
|
@@ -209,9 +244,9 @@ module Configurable
|
|
209
244
|
#
|
210
245
|
def array(); ARRAY; end
|
211
246
|
|
212
|
-
# default attributes {:
|
247
|
+
# default attributes {:type => :array, :example => "[a, b, c]"}
|
213
248
|
ARRAY = yaml(Array)
|
214
|
-
register ARRAY, :
|
249
|
+
register ARRAY, :type => :array, :example => "[a, b, c]"
|
215
250
|
|
216
251
|
# Same as array but allows nil:
|
217
252
|
#
|
@@ -219,9 +254,8 @@ module Configurable
|
|
219
254
|
# array_or_nil.call(nil) # => nil
|
220
255
|
def array_or_nil(); ARRAY_OR_NIL; end
|
221
256
|
|
222
|
-
# default attributes {:arg_name => "'[a, b, c]'"}
|
223
257
|
ARRAY_OR_NIL = yaml(Array, nil)
|
224
|
-
|
258
|
+
register_as ARRAY, ARRAY_OR_NIL
|
225
259
|
|
226
260
|
# Returns a block that checks the input is an array,
|
227
261
|
# then yamlizes each string value of the array.
|
@@ -254,9 +288,9 @@ module Configurable
|
|
254
288
|
#
|
255
289
|
def hash(); HASH; end
|
256
290
|
|
257
|
-
# default attributes {:
|
291
|
+
# default attributes {:type => :hash, :example => "{one: 1, two: 2}"}
|
258
292
|
HASH = yaml(Hash)
|
259
|
-
register HASH, :
|
293
|
+
register HASH, :type => :hash, :example => "{one: 1, two: 2}"
|
260
294
|
|
261
295
|
# Same as hash but allows nil:
|
262
296
|
#
|
@@ -264,9 +298,8 @@ module Configurable
|
|
264
298
|
# hash_or_nil.call(nil) # => nil
|
265
299
|
def hash_or_nil(); HASH_OR_NIL; end
|
266
300
|
|
267
|
-
# default attributes {:arg_name => "'{one: 1, two: 2}'"}
|
268
301
|
HASH_OR_NIL = yaml(Hash, nil)
|
269
|
-
|
302
|
+
register_as HASH, HASH_OR_NIL
|
270
303
|
|
271
304
|
# Returns a block that checks the input is an integer.
|
272
305
|
# String inputs are loaded as yaml first.
|
@@ -279,15 +312,20 @@ module Configurable
|
|
279
312
|
# integer.call('str') # => ValidationError
|
280
313
|
#
|
281
314
|
def integer(); INTEGER; end
|
315
|
+
|
316
|
+
# default attributes {:type => :integer, :example => "2"}
|
282
317
|
INTEGER = yaml(Integer)
|
283
|
-
|
318
|
+
register INTEGER, :type => :integer, :example => "2"
|
319
|
+
|
284
320
|
# Same as integer but allows nil:
|
285
321
|
#
|
286
322
|
# integer_or_nil.call('~') # => nil
|
287
323
|
# integer_or_nil.call(nil) # => nil
|
288
324
|
def integer_or_nil(); INTEGER_OR_NIL; end
|
325
|
+
|
289
326
|
INTEGER_OR_NIL = yaml(Integer, nil)
|
290
|
-
|
327
|
+
register_as INTEGER, INTEGER_OR_NIL
|
328
|
+
|
291
329
|
# Returns a block that checks the input is a float.
|
292
330
|
# String inputs are loaded as yaml first.
|
293
331
|
#
|
@@ -300,14 +338,19 @@ module Configurable
|
|
300
338
|
# float.call('str') # => ValidationError
|
301
339
|
#
|
302
340
|
def float(); FLOAT; end
|
341
|
+
|
342
|
+
# default attributes {:type => :float, :example => "2.2, 2.0e+2"}
|
303
343
|
FLOAT = yaml(Float)
|
304
|
-
|
344
|
+
register FLOAT, :type => :float, :example => "2.2, 2.0e+2"
|
345
|
+
|
305
346
|
# Same as float but allows nil:
|
306
347
|
#
|
307
348
|
# float_or_nil.call('~') # => nil
|
308
349
|
# float_or_nil.call(nil) # => nil
|
309
350
|
def float_or_nil(); FLOAT_OR_NIL; end
|
351
|
+
|
310
352
|
FLOAT_OR_NIL = yaml(Float, nil)
|
353
|
+
register_as FLOAT, FLOAT_OR_NIL
|
311
354
|
|
312
355
|
# Returns a block that checks the input is a number.
|
313
356
|
# String inputs are loaded as yaml first.
|
@@ -322,14 +365,19 @@ module Configurable
|
|
322
365
|
# num.call('str') # => ValidationError
|
323
366
|
#
|
324
367
|
def num(); NUMERIC; end
|
368
|
+
|
369
|
+
# default attributes {:type => :num, :example => "2, 2.2, 2.0e+2"}
|
325
370
|
NUMERIC = yaml(Numeric)
|
326
|
-
|
371
|
+
register NUMERIC, :type => :num, :example => "2, 2.2, 2.0e+2"
|
372
|
+
|
327
373
|
# Same as num but allows nil:
|
328
374
|
#
|
329
375
|
# num_or_nil.call('~') # => nil
|
330
376
|
# num_or_nil.call(nil) # => nil
|
331
377
|
def num_or_nil(); NUMERIC_OR_NIL; end
|
378
|
+
|
332
379
|
NUMERIC_OR_NIL = yaml(Numeric, nil)
|
380
|
+
register_as NUMERIC, NUMERIC_OR_NIL
|
333
381
|
|
334
382
|
# Returns a block that checks the input is a regexp. String inputs are
|
335
383
|
# loaded as yaml; if the result is not a regexp, it is converted to
|
@@ -349,10 +397,7 @@ module Configurable
|
|
349
397
|
def regexp(); REGEXP; end
|
350
398
|
regexp_block = lambda do |input|
|
351
399
|
if input.kind_of?(String)
|
352
|
-
|
353
|
-
input = validate(YAML.load(input), [Regexp]) {|obj| input }
|
354
|
-
rescue(ArgumentError)
|
355
|
-
end
|
400
|
+
input = load_if_yaml(input, Regexp)
|
356
401
|
end
|
357
402
|
|
358
403
|
if input.kind_of?(String)
|
@@ -361,7 +406,10 @@ module Configurable
|
|
361
406
|
|
362
407
|
validate(input, [Regexp])
|
363
408
|
end
|
409
|
+
|
410
|
+
# default attributes {:type => :regexp, :example => "/regexp/i"}
|
364
411
|
REGEXP = regexp_block
|
412
|
+
register REGEXP, :type => :regexp, :example => "/regexp/i"
|
365
413
|
|
366
414
|
# Same as regexp but allows nil. Note the special behavior of the nil
|
367
415
|
# string '~' -- rather than being converted to a regexp, it is processed
|
@@ -376,7 +424,9 @@ module Configurable
|
|
376
424
|
else REGEXP[input]
|
377
425
|
end
|
378
426
|
end
|
427
|
+
|
379
428
|
REGEXP_OR_NIL = regexp_or_nil_block
|
429
|
+
register_as REGEXP, REGEXP_OR_NIL
|
380
430
|
|
381
431
|
# Returns a block that checks the input is a range. String inputs are
|
382
432
|
# loaded as yaml; if the result is still a string, it is split into a
|
@@ -398,10 +448,7 @@ module Configurable
|
|
398
448
|
def range(); RANGE; end
|
399
449
|
range_block = lambda do |input|
|
400
450
|
if input.kind_of?(String)
|
401
|
-
|
402
|
-
input = validate(YAML.load(input), [Range]) {|obj| input }
|
403
|
-
rescue(ArgumentError)
|
404
|
-
end
|
451
|
+
input = load_if_yaml(input, Range)
|
405
452
|
end
|
406
453
|
|
407
454
|
if input.kind_of?(String) && input =~ /^([^.]+)(\.{2,3})([^.]+)$/
|
@@ -410,8 +457,11 @@ module Configurable
|
|
410
457
|
|
411
458
|
validate(input, [Range])
|
412
459
|
end
|
460
|
+
|
461
|
+
# default attributes {:type => :range, :example => "min..max"}
|
413
462
|
RANGE = range_block
|
414
|
-
|
463
|
+
register RANGE, :type => :range, :example => "min..max"
|
464
|
+
|
415
465
|
# Same as range but allows nil:
|
416
466
|
#
|
417
467
|
# range_or_nil.call('~') # => nil
|
@@ -423,7 +473,9 @@ module Configurable
|
|
423
473
|
else RANGE[input]
|
424
474
|
end
|
425
475
|
end
|
476
|
+
|
426
477
|
RANGE_OR_NIL = range_or_nil_block
|
478
|
+
register_as RANGE, RANGE_OR_NIL
|
427
479
|
|
428
480
|
# Returns a block that checks the input is a Time. String inputs are
|
429
481
|
# loaded using Time.parse and then converted into times. Parsed times
|
@@ -460,8 +512,11 @@ module Configurable
|
|
460
512
|
input = Time.parse(input) if input.kind_of?(String)
|
461
513
|
validate(input, [Time])
|
462
514
|
end
|
515
|
+
|
516
|
+
# default attributes {:type => :time, :example => "2008-08-08 08:00:00"}
|
463
517
|
TIME = time_block
|
464
|
-
|
518
|
+
register TIME, :type => :time, :example => "2008-08-08 08:00:00"
|
519
|
+
|
465
520
|
# Same as time but allows nil:
|
466
521
|
#
|
467
522
|
# time_or_nil.call('~') # => nil
|
@@ -474,7 +529,119 @@ module Configurable
|
|
474
529
|
else TIME[input]
|
475
530
|
end
|
476
531
|
end
|
532
|
+
|
477
533
|
TIME_OR_NIL = time_or_nil_block
|
534
|
+
register_as TIME, TIME_OR_NIL
|
535
|
+
|
536
|
+
# Returns a block that only allows the specified values. Select can take
|
537
|
+
# a block that will validate each individual value.
|
538
|
+
#
|
539
|
+
# s = select(1,2,3, &integer)
|
540
|
+
# s.class # => Proc
|
541
|
+
# s.call(1) # => 1
|
542
|
+
# s.call('3') # => 3
|
543
|
+
#
|
544
|
+
# s.call(nil) # => ValidationError
|
545
|
+
# s.call(0) # => ValidationError
|
546
|
+
# s.call('4') # => ValidationError
|
547
|
+
#
|
548
|
+
# The select block is registered with these default attributes:
|
549
|
+
#
|
550
|
+
# {:type => :select, :values => values}
|
551
|
+
#
|
552
|
+
def select(*values, &validation)
|
553
|
+
block = lambda do |input|
|
554
|
+
input = validation.call(input) if validation
|
555
|
+
validate(input, values)
|
556
|
+
end
|
557
|
+
|
558
|
+
register(block, :type => :select, :values => values)
|
559
|
+
end
|
560
|
+
|
561
|
+
# Returns a block that checks the input is an array, and that each member
|
562
|
+
# of the array is one of the specified values. A block may be provided
|
563
|
+
# to validate each individual value.
|
564
|
+
#
|
565
|
+
# s = list_select(1,2,3, &integer)
|
566
|
+
# s.class # => Proc
|
567
|
+
# s.call([1]) # => [1]
|
568
|
+
# s.call([1, '3']) # => [1, 3]
|
569
|
+
# s.call([]) # => []
|
570
|
+
#
|
571
|
+
# s.call(1) # => ValidationError
|
572
|
+
# s.call([nil]) # => ValidationError
|
573
|
+
# s.call([0]) # => ValidationError
|
574
|
+
# s.call(['4']) # => ValidationError
|
575
|
+
#
|
576
|
+
# The list_select block is registered with these default attributes:
|
577
|
+
#
|
578
|
+
# {:type => :list_select, :values => values, :split => ','}
|
579
|
+
#
|
580
|
+
def list_select(*values, &validation)
|
581
|
+
block = lambda do |input|
|
582
|
+
args = validate(input, [Array])
|
583
|
+
args.collect! {|arg| validation.call(arg) } if validation
|
584
|
+
args.each {|arg| validate(arg, values) }
|
585
|
+
end
|
586
|
+
|
587
|
+
register(block, :type => :list_select, :values => values, :split => ',')
|
588
|
+
end
|
589
|
+
|
590
|
+
# Returns a block validating the input is an IO or a string. String inputs
|
591
|
+
# are expected to be filepaths, but io does not open a file immediately.
|
592
|
+
#
|
593
|
+
# io.class # => Proc
|
594
|
+
# io.call($stdout) # => $stdout
|
595
|
+
# io.call('/path/to/file') # => '/path/to/file'
|
596
|
+
#
|
597
|
+
# io.call(nil) # => ValidationError
|
598
|
+
# io.call(10) # => ValidationError
|
599
|
+
#
|
600
|
+
# An IO api can be specified to allow other objects to be validated. This
|
601
|
+
# is useful for duck-typing an IO when a known subset of methods are needed.
|
602
|
+
#
|
603
|
+
# array_io = io(:<<)
|
604
|
+
# array_io.call($stdout) # => $stdout
|
605
|
+
# array_io.call([]) # => []
|
606
|
+
# array_io.call(nil) # => ValidationError
|
607
|
+
#
|
608
|
+
def io(*api)
|
609
|
+
if api.empty?
|
610
|
+
IO_OR_STRING
|
611
|
+
else
|
612
|
+
block = lambda do |input|
|
613
|
+
validate(input, [IO, String]) do
|
614
|
+
api.all? {|m| input.respond_to?(m) }
|
615
|
+
end
|
616
|
+
end
|
617
|
+
|
618
|
+
register_as IO_OR_STRING, block
|
619
|
+
end
|
620
|
+
end
|
621
|
+
|
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"
|
625
|
+
|
626
|
+
# Same as io but allows nil:
|
627
|
+
#
|
628
|
+
# io_or_nil.call(nil) # => nil
|
629
|
+
#
|
630
|
+
def io_or_nil(*api)
|
631
|
+
if api.empty?
|
632
|
+
IO_STRING_OR_NIL
|
633
|
+
else
|
634
|
+
block = lambda do |input|
|
635
|
+
validate(input, [IO, String, nil]) do
|
636
|
+
api.all? {|m| input.respond_to?(m) }
|
637
|
+
end
|
638
|
+
end
|
639
|
+
|
640
|
+
register_as IO_STRING_OR_NIL, block
|
641
|
+
end
|
642
|
+
end
|
478
643
|
|
644
|
+
IO_STRING_OR_NIL = check(IO, String, nil)
|
645
|
+
register_as IO_OR_STRING, IO_STRING_OR_NIL
|
479
646
|
end
|
480
647
|
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.
|
4
|
+
version: 0.4.2
|
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-03-
|
12
|
+
date: 2009-03-30 00:00:00 -06:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|