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 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
@@ -156,22 +156,32 @@ class ConfigParser
156
156
  # handles them.
157
157
  attr_reader :switches
158
158
 
159
- # The most recent hash of configurations produced by parse.
160
- attr_reader :config
159
+ # The hash receiving configurations produced by parse.
160
+ attr_accessor :config
161
161
 
162
- # A hash of default configurations merged into parsed configs.
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
- add(default.delegates, key)
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
- # A config may be provided to receive configurations; it will be set
411
- # as self.config.
422
+ # ==== Options
412
423
  #
413
- def parse(argv=ARGV, config={}, merge_default=true)
414
- @config = config
415
- argv = argv.kind_of?(String) ? Shellwords.shellwords(argv) : argv.dup
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 merge_default
481
+ end if options[:add_defaults]
450
482
 
451
- args
452
- end
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
@@ -1,4 +1,4 @@
1
- require 'configurable/class_methods'
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. The input io is always returned.
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 nil
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. Parsed configs are
44
- # added to config (note that you must keep a separate reference to
45
- # config as it is not returned by parse). The parser will is yielded to the
46
- # block, if given, to register additonal options. Returns an array of the
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, config={})
51
- ConfigParser.new do |parser|
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, config={})
59
- argv.replace(parse(argv, config))
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
- const_set(const_name, configurable_class) if const_name
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
- hash[key] = value.kind_of?(DelegateHash) ? value.to_hash : value
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 source.has_key?(key) then source.delete(key)
188
- when delegate[:set_default, true] then delegate.default
189
- else next
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. Attributes are duplicated so they may be modifed.
54
- def register_as(source, target)
55
- DEFAULT_ATTRIBUTES[target] = DEFAULT_ATTRIBUTES[source].dup
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. Note the validations
73
- # input must be an Array or nil; validate will raise an ArgumentError
74
- # otherwise. All inputs are considered VALID if validations == nil.
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 nil, or an array of valid inputs"
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(); LIST; end
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
- # num.class # => Proc
359
- # num.call(1.1) # => 1.1
360
- # num.call(1) # => 1
361
- # num.call(1e6) # => 1e6
362
- # num.call('1.1') # => 1.1
363
- # num.call('1.0e+6') # => 1e6
364
- # num.call(nil) # => ValidationError
365
- # num.call('str') # => ValidationError
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 num(); NUMERIC; end
435
+ def numeric(); NUMERIC; end
368
436
 
369
- # default attributes {:type => :num, :example => "2, 2.2, 2.0e+2"}
437
+ # default attributes {:type => :numeric, :example => "2, 2.2, 2.0e+2"}
370
438
  NUMERIC = yaml(Numeric)
371
- register NUMERIC, :type => :num, :example => "2, 2.2, 2.0e+2"
439
+ register NUMERIC, :type => :numeric, :example => "2, 2.2, 2.0e+2"
372
440
 
373
- # Same as num but allows nil:
441
+ # Same as numeric but allows nil:
374
442
  #
375
- # num_or_nil.call('~') # => nil
376
- # num_or_nil.call(nil) # => nil
377
- def num_or_nil(); NUMERIC_OR_NIL; end
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; if the result is still a string, it is split into a
433
- # beginning and end, if possible, and each part is loaded as yaml
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 values. Select can take
537
- # a block that will validate each individual value.
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, :values => values}
614
+ # {:type => :select, :options => options}
551
615
  #
552
- def select(*values, &validation)
616
+ def select(*options, &validation)
553
617
  block = lambda do |input|
554
618
  input = validation.call(input) if validation
555
- validate(input, values)
619
+ validate(input, options)
556
620
  end
557
621
 
558
- register(block, :type => :select, :values => values)
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 one of the specified values. A block may be provided
563
- # to validate each individual value.
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, :values => values, :split => ','}
645
+ # {:type => :list_select, :options => options, :split => ','}
579
646
  #
580
- def list_select(*values, &validation)
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, values) }
651
+ args.each {|arg| validate(arg, options) }
585
652
  end
586
653
 
587
- register(block, :type => :list_select, :values => values, :split => ',')
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 a string. String inputs
591
- # are expected to be filepaths, but io does not open a file immediately.
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) # => ValidationError
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.all? {|m| input.respond_to?(m) }
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.all? {|m| input.respond_to?(m) }
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.2
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-03-30 00:00:00 -06:00
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.3.1
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