Module: Configurable::Validation
- Defined in:
- lib/configurable/validation.rb
Overview
Validation generates blocks for common validations and transformations of configurations set through Configurable. In general these blocks load string inputs as YAML and validate the results; non-string inputs are simply validated.
integer = Validation.integer
integer.class # => Proc
integer.call(1) # => 1
integer.call('1') # => 1
integer.call(nil) # => ValidationError
– Developers: note the unusual syntax for declaring constants that are blocks defined by lambda… ex:
block = lambda {}
CONST = block
This syntax plays well with RDoc, which otherwise gets jacked when you do it all in one step.
Defined Under Namespace
Classes: ApiError, ValidationError
Constant Summary collapse
- STRING =
default attributes => :string, :example => “string”
string_validation_block- STRING_OR_NIL =
string_or_nil_validation_block- SYMBOL =
default attributes => :symbol, :example => “:sym”
yaml(Symbol)
- SYMBOL_OR_NIL =
yaml(Symbol, nil)
- STRBOL =
default attributes => :symbol, :example => “:sym”
lambda do |input| if input.kind_of?(String) input = input.to_sym end validate(input, [Symbol]) end
- STRBOL_OR_NIL =
lambda do |input| input = case input when "~" then nil when String then input.to_sym else input end validate(input, [Symbol, nil]) end
- BOOLEAN =
default attributes => :boolean, :example => “true, yes”
yaml(true, false, nil)
- SWITCH =
default attributes => :switch
yaml(true, false, nil)
- FLAG =
default attributes => :flag
yaml(true, false, nil)
- ARRAY =
default attributes => :array, :example => “[a, b, c]”
yaml(Array)
- ARRAY_OR_NIL =
yaml(Array, nil)
- LIST =
default attributes => :list, :split => ‘,’
list_block- HASH =
default attributes => :hash, :example => “{one: 1, two: 2”}
yaml(Hash)
- HASH_OR_NIL =
yaml(Hash, nil)
- INTEGER =
default attributes => :integer, :example => “2”
yaml(Integer)
- INTEGER_OR_NIL =
yaml(Integer, nil)
- FLOAT =
default attributes => :float, :example => “2.2, 2.0e+2”
yaml(Float)
- FLOAT_OR_NIL =
yaml(Float, nil)
- NUMERIC =
default attributes => :numeric, :example => “2, 2.2, 2.0e+2”
yaml(Numeric)
- NUMERIC_OR_NIL =
yaml(Numeric, nil)
- REGEXP =
default attributes => :regexp, :example => “/regexp/i”
regexp_block- REGEXP_OR_NIL =
regexp_or_nil_block- RANGE =
default attributes => :range, :example => “min..max”
range_block- RANGE_OR_NIL =
range_or_nil_block- TIME =
default attributes => :time, :example => “2008-08-08 08:00:00”
time_block- TIME_OR_NIL =
time_or_nil_block- IO_OR_STRING =
default attributes => :io, :dup => false, :example => “/path/to/file”
check(IO, String, Integer)
- IO_STRING_OR_NIL =
check(IO, String, Integer, nil)
Class Method Summary collapse
-
.api(*methods) ⇒ Object
Returns a block that calls validate_api using the block input and methods.
-
.array ⇒ Object
Returns a block that checks the input is an array.
-
.array_or_nil ⇒ Object
Same as array but allows nil:.
-
.attributes(block) ⇒ Object
Returns the attributes registered to the block.
-
.boolean ⇒ Object
Returns a block that checks the input is true, false or nil.
-
.check(*validations) ⇒ Object
Returns a block that calls validate using the block input and validations.
-
.flag ⇒ Object
Same as boolean.
-
.float ⇒ Object
Returns a block that checks the input is a float.
-
.float_or_nil ⇒ Object
Same as float but allows nil:.
-
.guess(value) ⇒ Object
Guesses and returns a block for the example value.
-
.hash ⇒ Object
Returns a block that checks the input is a hash.
-
.hash_or_nil ⇒ Object
Same as hash but allows nil:.
-
.integer ⇒ Object
Returns a block that checks the input is an integer.
-
.integer_or_nil ⇒ Object
Same as integer but allows nil:.
-
.io(*api) ⇒ Object
Returns a block validating the input is an IO, a string, or an integer.
-
.io_or_nil(*api) ⇒ Object
Same as io but allows nil:.
-
.list(&validation) ⇒ Object
Returns a block that checks the input is an array, then yamlizes each string value of the array.
-
.list_select(*options, &validation) ⇒ Object
Returns a block that checks the input is an array, and that each member of the array is included in options.
-
.load_if_yaml(input, *validations) ⇒ Object
Helper to load the input into a valid object.
-
.numeric ⇒ Object
Returns a block that checks the input is a number.
-
.numeric_or_nil ⇒ Object
Same as numeric but allows nil:.
-
.range ⇒ Object
Returns a block that checks the input is a range.
-
.range_or_nil ⇒ Object
Same as range but allows nil:.
-
.regexp ⇒ Object
Returns a block that checks the input is a regexp.
-
.regexp_or_nil ⇒ Object
Same as regexp but allows nil.
-
.register(attributes = {}, &block) ⇒ Object
Registers the default attributes with the specified block in Configurable::DEFAULT_ATTRIBUTES.
-
.register_as(source, target, attributes = {}) ⇒ Object
Registers the default attributes of the source as the attributes of the target.
-
.select(*options, &validation) ⇒ Object
Returns a block that only allows the specified options.
-
.strbol ⇒ Object
Returns a block that checks the input is a symbol.
-
.strbol_or_nil ⇒ Object
Same as strbol but allows nil.
-
.string ⇒ Object
Returns a block that checks the input is a string.
-
.string_or_nil ⇒ Object
Same as string but allows nil.
-
.switch ⇒ Object
Same as boolean.
-
.symbol ⇒ Object
Returns a block that checks the input is a symbol.
-
.symbol_or_nil ⇒ Object
Same as symbol but allows nil:.
-
.time ⇒ Object
Returns a block that checks the input is a Time.
-
.time_or_nil ⇒ Object
Same as time but allows nil:.
-
.validate(input, validations) ⇒ Object
Returns input if it matches any of the validations as in would in a case statement.
-
.validate_api(input, methods) ⇒ Object
Returns input if it responds to all of the specified methods.
-
.yaml(*validations) ⇒ Object
Returns a block that loads input strings as YAML, then calls validate with the result and validations.
Class Method Details
.api(*methods) ⇒ Object
Returns a block that calls validate_api using the block input and methods.
178 179 180 181 182 |
# File 'lib/configurable/validation.rb', line 178 def api(*methods) lambda do |input| validate_api(input, methods) end end |
.array ⇒ Object
Returns a block that checks the input is an array. String inputs are loaded as yaml first.
array.class # => Proc
array.call([1,2,3]) # => [1,2,3]
array.call('[1, 2, 3]') # => [1,2,3]
array.call(nil) # => ValidationError
array.call('str') # => ValidationError
349 |
# File 'lib/configurable/validation.rb', line 349 def array(); ARRAY; end |
.array_or_nil ⇒ Object
Same as array but allows nil:
array_or_nil.call('~') # => nil
array_or_nil.call(nil) # => nil
359 |
# File 'lib/configurable/validation.rb', line 359 def array_or_nil(); ARRAY_OR_NIL; end |
.attributes(block) ⇒ Object
Returns the attributes registered to the block.
73 74 75 |
# File 'lib/configurable/validation.rb', line 73 def attributes(block) DEFAULT_ATTRIBUTES[block] || {} end |
.boolean ⇒ Object
Returns a block that checks the input is true, false or nil. String inputs are loaded as yaml first.
boolean.class # => Proc
boolean.call(true) # => true
boolean.call(false) # => false
boolean.call(nil) # => nil
boolean.call('true') # => true
boolean.call('yes') # => true
boolean.call('FALSE') # => false
boolean.call(1) # => ValidationError
boolean.call("str") # => ValidationError
320 |
# File 'lib/configurable/validation.rb', line 320 def boolean(); BOOLEAN; end |
.check(*validations) ⇒ Object
Returns a block that calls validate using the block input and validations.
157 158 159 |
# File 'lib/configurable/validation.rb', line 157 def check(*validations) lambda {|input| validate(input, validations) } end |
.flag ⇒ Object
Same as boolean.
334 |
# File 'lib/configurable/validation.rb', line 334 def flag(); FLAG; end |
.float ⇒ Object
Returns a block that checks the input is a float. String inputs are loaded as yaml first.
float.class # => Proc
float.call(1.1) # => 1.1
float.call('1.1') # => 1.1
float.call('1.0e+6') # => 1e6
float.call(1) # => ValidationError
float.call(nil) # => ValidationError
float.call('str') # => ValidationError
457 |
# File 'lib/configurable/validation.rb', line 457 def float(); FLOAT; end |
.float_or_nil ⇒ Object
Same as float but allows nil:
float_or_nil.call('~') # => nil
float_or_nil.call(nil) # => nil
467 |
# File 'lib/configurable/validation.rb', line 467 def float_or_nil(); FLOAT_OR_NIL; end |
.guess(value) ⇒ Object
Guesses and returns a block for the example value.
162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/configurable/validation.rb', line 162 def guess(value) case value when true then switch when false then flag when Numeric then numeric when Array then list when String then string when Time then time when Range then range when Regexp then regexp else yaml end end |
.hash ⇒ Object
Returns a block that checks the input is a hash. String inputs are loaded as yaml first.
hash.class # => Proc
hash.call({'key' => 'value'}) # => {'key' => 'value'}
hash.call('key: value') # => {'key' => 'value'}
hash.call(nil) # => ValidationError
hash.call('str') # => ValidationError
406 |
# File 'lib/configurable/validation.rb', line 406 def hash(); HASH; end |
.hash_or_nil ⇒ Object
Same as hash but allows nil:
hash_or_nil.call('~') # => nil
hash_or_nil.call(nil) # => nil
416 |
# File 'lib/configurable/validation.rb', line 416 def hash_or_nil(); HASH_OR_NIL; end |
.integer ⇒ Object
Returns a block that checks the input is an integer. String inputs are loaded as yaml first.
integer.class # => Proc
integer.call(1) # => 1
integer.call('1') # => 1
integer.call(1.1) # => ValidationError
integer.call(nil) # => ValidationError
integer.call('str') # => ValidationError
431 |
# File 'lib/configurable/validation.rb', line 431 def integer(); INTEGER; end |
.integer_or_nil ⇒ Object
Same as integer but allows nil:
integer_or_nil.call('~') # => nil
integer_or_nil.call(nil) # => nil
441 |
# File 'lib/configurable/validation.rb', line 441 def integer_or_nil(); INTEGER_OR_NIL; end |
.io(*api) ⇒ Object
Returns a block validating the input is an IO, a string, or an integer. String inputs are expected to be filepaths and integer inputs are expected to be valid file descriptors, but io does not open an IO immediately.
io.class # => Proc
io.call($stdout) # => $stdout
io.call('/path/to/file') # => '/path/to/file'
io.call(1) # => 1
io.call(nil) # => ValidationError
An IO api can be specified to allow other objects to be validated. This is useful for duck-typing an IO when a known subset of methods are needed.
array_io = io(:<<)
array_io.call($stdout) # => $stdout
array_io.call([]) # => []
array_io.call(nil) # => ApiError
Note that by default io configs will not be duplicated (duplicate IOs flush separately, and this can result in disorder. see gist.github.com/88808).
729 730 731 732 733 734 735 736 737 738 739 740 741 |
# File 'lib/configurable/validation.rb', line 729 def io(*api) if api.empty? IO_OR_STRING else block = lambda do |input| validate(input, [IO, String, Integer]) do validate_api(input, api) end end register_as IO_OR_STRING, block end end |
.io_or_nil(*api) ⇒ Object
Same as io but allows nil:
io_or_nil.call(nil) # => nil
751 752 753 754 755 756 757 758 759 760 761 762 763 |
# File 'lib/configurable/validation.rb', line 751 def io_or_nil(*api) if api.empty? IO_STRING_OR_NIL else block = lambda do |input| validate(input, [IO, String, Integer, nil]) do validate_api(input, api) end end register_as IO_STRING_OR_NIL, block end end |
.list(&validation) ⇒ Object
Returns a block that checks the input is an array, then yamlizes each string value of the array.
list.class # => Proc
list.call([1,2,3]) # => [1,2,3]
list.call(['1', 'str']) # => [1,'str']
list.call('str') # => ValidationError
list.call(nil) # => ValidationError
373 374 375 376 377 378 379 380 381 382 383 384 385 |
# File 'lib/configurable/validation.rb', line 373 def list(&validation) return LIST unless validation block = lambda do |input| args = validate(input, [Array]).collect do |arg| arg.kind_of?(String) ? YAML.load(arg) : arg end args.collect! {|arg| validation.call(arg) } args end register_as(LIST, block, :validation => attributes(validation)) end |
.list_select(*options, &validation) ⇒ Object
Returns a block that checks the input is an array, and that each member of the array is included in options. A block may be provided to validate the individual values.
s = list_select(1,2,3, &integer)
s.class # => Proc
s.call([1]) # => [1]
s.call([1, '3']) # => [1, 3]
s.call([]) # => []
s.call(1) # => ValidationError
s.call([nil]) # => ValidationError
s.call([0]) # => ValidationError
s.call(['4']) # => ValidationError
The list_select block is registered with these default attributes:
{:type => :list_select, :options => , :split => ','}
695 696 697 698 699 700 701 702 703 704 705 706 |
# File 'lib/configurable/validation.rb', line 695 def list_select(*, &validation) register( :type => :list_select, :options => , :split => ',', :validation => attributes(validation) ) do |input| args = validate(input, [Array]) args.collect! {|arg| validation.call(arg) } if validation args.each {|arg| validate(arg, ) } end end |
.load_if_yaml(input, *validations) ⇒ Object
Helper to load the input into a valid object. If a valid object is not loaded as YAML, or if an error occurs, the original input is returned.
143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/configurable/validation.rb', line 143 def load_if_yaml(input, *validations) begin yaml = YAML.load(input) case yaml when *validations then yaml else input end rescue(ArgumentError) input end end |
.numeric ⇒ Object
Returns a block that checks the input is a number. String inputs are loaded as yaml first.
numeric.class # => Proc
numeric.call(1.1) # => 1.1
numeric.call(1) # => 1
numeric.call(1e6) # => 1e6
numeric.call('1.1') # => 1.1
numeric.call('1.0e+6') # => 1e6
numeric.call(nil) # => ValidationError
numeric.call('str') # => ValidationError
484 |
# File 'lib/configurable/validation.rb', line 484 def numeric(); NUMERIC; end |
.numeric_or_nil ⇒ Object
Same as numeric but allows nil:
numeric_or_nil.call('~') # => nil
numeric_or_nil.call(nil) # => nil
494 |
# File 'lib/configurable/validation.rb', line 494 def numeric_or_nil(); NUMERIC_OR_NIL; end |
.range ⇒ Object
Returns a block that checks the input is a range. String inputs are loaded as yaml (a ‘!ruby/range’ prefix is added before loading if if it is not specified).
range.class # => Proc
range.call(1..10) # => 1..10
range.call('1..10') # => 1..10
range.call('a..z') # => 'a'..'z'
range.call('-10...10') # => -10...10
range.call(nil) # => ValidationError
range.call('1.10') # => ValidationError
range.call('a....z') # => ValidationError
yaml_str = "!ruby/range \nbegin: 1\nend: 10\nexcl: false\n"
range.call(yaml_str) # => 1..10
564 |
# File 'lib/configurable/validation.rb', line 564 def range(); RANGE; end |
.range_or_nil ⇒ Object
Same as range but allows nil:
range_or_nil.call('~') # => nil
range_or_nil.call(nil) # => nil
582 |
# File 'lib/configurable/validation.rb', line 582 def range_or_nil(); RANGE_OR_NIL; end |
.regexp ⇒ Object
Returns a block that checks the input is a regexp. String inputs are loaded as yaml; if the result is not a regexp, it is converted to a regexp using Regexp#new.
regexp.class # => Proc
regexp.call(/regexp/) # => /regexp/
regexp.call('regexp') # => /regexp/
yaml_str = '!ruby/regexp /regexp/'
regexp.call(yaml_str) # => /regexp/
# use of ruby-specific flags can turn on/off
# features like case insensitive matching
regexp.call('(?i)regexp') # => /(?i)regexp/
514 |
# File 'lib/configurable/validation.rb', line 514 def regexp(); REGEXP; end |
.regexp_or_nil ⇒ Object
Same as regexp but allows nil. Note the special behavior of the nil string ‘~’ – rather than being converted to a regexp, it is processed as nil to be consistent with the other [class]_or_nil methods.
regexp_or_nil.call('~') # => nil
regexp_or_nil.call(nil) # => nil
537 |
# File 'lib/configurable/validation.rb', line 537 def regexp_or_nil(); REGEXP_OR_NIL; end |
.register(attributes = {}, &block) ⇒ Object
Registers the default attributes with the specified block in Configurable::DEFAULT_ATTRIBUTES.
59 60 61 62 |
# File 'lib/configurable/validation.rb', line 59 def register(attributes={}, &block) DEFAULT_ATTRIBUTES[block] = attributes block end |
.register_as(source, target, attributes = {}) ⇒ Object
Registers the default attributes of the source as the attributes of the target. Overridding or additional attributes are merged to the defaults.
67 68 69 70 |
# File 'lib/configurable/validation.rb', line 67 def register_as(source, target, attributes={}) DEFAULT_ATTRIBUTES[target] = DEFAULT_ATTRIBUTES[source].dup.merge!(attributes) target end |
.select(*options, &validation) ⇒ Object
Returns a block that only allows the specified options. Select can take a block that will validate the input individual value.
s = select(1,2,3, &integer)
s.class # => Proc
s.call(1) # => 1
s.call('3') # => 3
s.call(nil) # => ValidationError
s.call(0) # => ValidationError
s.call('4') # => ValidationError
The select block is registered with these default attributes:
{:type => :select, :options => }
665 666 667 668 669 670 671 672 673 674 |
# File 'lib/configurable/validation.rb', line 665 def select(*, &validation) register( :type => :select, :options => , :validation => attributes(validation) ) do |input| input = validation.call(input) if validation validate(input, ) end end |
.strbol ⇒ Object
Returns a block that checks the input is a symbol. String inputs are directly converted to a symbol.
strbol.class # => Proc
strbol.call(:sym) # => :sym
strbol.call(':sym') # => :":sym"
strbol.call('str') # => :sym
strbol.call(nil) # => ValidationError
274 |
# File 'lib/configurable/validation.rb', line 274 def strbol(); STRBOL; end |
.strbol_or_nil ⇒ Object
Same as strbol but allows nil. Tilde is considered a string equivalent of nil (this behavior is consistent with the YAML methods but obviously inconsistent with the strbol behavior).
strbol_or_nil.call('~') # => nil
strbol_or_nil.call(nil) # => nil
292 |
# File 'lib/configurable/validation.rb', line 292 def strbol_or_nil(); STRBOL_OR_NIL; end |
.string ⇒ Object
Returns a block that checks the input is a string.
string.class # => Proc
string.call('str') # => 'str'
string.call(nil) # => ValidationError
string.call(:sym) # => ValidationError
212 |
# File 'lib/configurable/validation.rb', line 212 def string(); STRING; end |
.string_or_nil ⇒ Object
Same as string but allows nil. Note the special behavior of the nil string ‘~’ – rather than being treated as a string, it is processed as nil to be consistent with the other [class]_or_nil methods.
string_or_nil.call('~') # => nil
string_or_nil.call(nil) # => nil
229 |
# File 'lib/configurable/validation.rb', line 229 def string_or_nil(); STRING_OR_NIL; end |
.switch ⇒ Object
Same as boolean.
327 |
# File 'lib/configurable/validation.rb', line 327 def switch(); SWITCH; end |
.symbol ⇒ Object
Returns a block that checks the input is a symbol. String inputs are loaded as yaml first.
symbol.class # => Proc
symbol.call(:sym) # => :sym
symbol.call(':sym') # => :sym
symbol.call(nil) # => ValidationError
symbol.call('str') # => ValidationError
250 |
# File 'lib/configurable/validation.rb', line 250 def symbol(); SYMBOL; end |
.symbol_or_nil ⇒ Object
Same as symbol but allows nil:
symbol_or_nil.call('~') # => nil
symbol_or_nil.call(nil) # => nil
260 |
# File 'lib/configurable/validation.rb', line 260 def symbol_or_nil(); SYMBOL_OR_NIL; end |
.time ⇒ Object
Returns a block that checks the input is a Time. String inputs are loaded using Time.parse and then converted into times. Parsed times are local unless specified otherwise.
time.class # => Proc
now = Time.now
time.call(now) # => now
time.call('2008-08-08 20:00:00.00 +08:00').getutc.strftime('%Y/%m/%d %H:%M:%S')
# => '2008/08/08 12:00:00'
time.call('2008-08-08').strftime('%Y/%m/%d %H:%M:%S')
# => '2008/08/08 00:00:00'
time.call(1) # => ValidationError
time.call(nil) # => ValidationError
Warning: Time.parse will parse a valid time (Time.now) even when no time is specified:
time.call('str').strftime('%Y/%m/%d %H:%M:%S')
# => Time.now.strftime('%Y/%m/%d %H:%M:%S')
617 618 619 620 621 622 |
# File 'lib/configurable/validation.rb', line 617 def time() # adding this here is a compromise to lazy-load the parse # method (autoload doesn't work since Time already exists) require 'time' unless Time.respond_to?(:parse) TIME end |
.time_or_nil ⇒ Object
Same as time but allows nil:
time_or_nil.call('~') # => nil
time_or_nil.call(nil) # => nil
637 |
# File 'lib/configurable/validation.rb', line 637 def time_or_nil(); TIME_OR_NIL; end |
.validate(input, validations) ⇒ Object
Returns input if it matches any of the validations as in would in a case statement. Raises a ValidationError otherwise. For example:
validate(10, [Integer, nil])
Does the same as:
case 10
when Integer, nil then input
else raise ValidationError.new(...)
end
A block may be provided to handle invalid inputs; if provided it will be called with the input and a ValidationError will not be raised unless the block returns false. Note the validations input must be an Array or nil; validate will raise an ArgumentError otherwise. All inputs are considered VALID if validations == nil.
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/configurable/validation.rb', line 94 def validate(input, validations) case validations when Array case input when *validations then input else if block_given? && yield(input) input else raise ValidationError.new(input, validations) end end when nil then input else raise ArgumentError, "validations must be an array of valid inputs or nil" end end |
.validate_api(input, methods) ⇒ Object
Returns input if it responds to all of the specified methods. Raises an ApiError otherwise. For example:
validate_api(10, [:to_s, :to_f]) # => 10
validate_api(Object.new, [:to_s, :to_f]) # !> ApiError
A block may be provided to handle invalid inputs; if provided it will be called with the input and an ApiError will not be raised unless the block returns false. Note the methods input must be an Array or nil; validate_api will raise an ArgumentError otherwise. All inputs are considered VALID if methods == nil.
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/configurable/validation.rb', line 124 def validate_api(input, methods) case methods when Array unless methods.all? {|m| input.respond_to?(m) } if block_given? && yield(input) input else raise ApiError.new(input, methods) end end when nil else raise ArgumentError, "methods must be an array or nil" end input end |
.yaml(*validations) ⇒ Object
Returns a block that loads input strings as YAML, then calls validate with the result and validations. Non-string inputs are validated directly.
b = yaml(Integer, nil)
b.class # => Proc
b.call(1) # => 1
b.call("1") # => 1
b.call(nil) # => nil
b.call("str") # => ValidationError
If no validations are specified, the result will be returned without validation.
197 198 199 200 201 202 203 |
# File 'lib/configurable/validation.rb', line 197 def yaml(*validations) validations = nil if validations.empty? lambda do |input| input = YAML.load(input) if input.kind_of?(String) validate(input, validations) end end |