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 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