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 valdiate 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)
- 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, :duplicate_default => 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:.
-
.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(block, attributes) ⇒ 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.
-
.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.
163 164 165 166 167 |
# File 'lib/configurable/validation.rb', line 163 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
300 |
# File 'lib/configurable/validation.rb', line 300 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
310 |
# File 'lib/configurable/validation.rb', line 310 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
271 |
# File 'lib/configurable/validation.rb', line 271 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.
285 |
# File 'lib/configurable/validation.rb', line 285 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
408 |
# File 'lib/configurable/validation.rb', line 408 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
418 |
# File 'lib/configurable/validation.rb', line 418 def float_or_nil(); FLOAT_OR_NIL; 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
357 |
# File 'lib/configurable/validation.rb', line 357 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
367 |
# File 'lib/configurable/validation.rb', line 367 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
382 |
# File 'lib/configurable/validation.rb', line 382 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
392 |
# File 'lib/configurable/validation.rb', line 392 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).
682 683 684 685 686 687 688 689 690 691 692 693 694 |
# File 'lib/configurable/validation.rb', line 682 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
704 705 706 707 708 709 710 711 712 713 714 715 716 |
# File 'lib/configurable/validation.rb', line 704 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
324 325 326 327 328 329 330 331 332 333 334 335 336 |
# File 'lib/configurable/validation.rb', line 324 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 => ','}
647 648 649 650 651 652 653 654 655 656 657 658 659 |
# File 'lib/configurable/validation.rb', line 647 def list_select(*, &validation) block = lambda do |input| args = validate(input, [Array]) args.collect! {|arg| validation.call(arg) } if validation args.each {|arg| validate(arg, ) } end register(block, :type => :list_select, :options => , :split => ',', :validation => attributes(validation)) 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
435 |
# File 'lib/configurable/validation.rb', line 435 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
445 |
# File 'lib/configurable/validation.rb', line 445 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
515 |
# File 'lib/configurable/validation.rb', line 515 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
533 |
# File 'lib/configurable/validation.rb', line 533 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/
465 |
# File 'lib/configurable/validation.rb', line 465 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
488 |
# File 'lib/configurable/validation.rb', line 488 def regexp_or_nil(); REGEXP_OR_NIL; end |
.register(block, attributes) ⇒ 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(block, attributes) 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 => }
616 617 618 619 620 621 622 623 624 625 626 |
# File 'lib/configurable/validation.rb', line 616 def select(*, &validation) block = lambda do |input| input = validation.call(input) if validation validate(input, ) end register(block, :type => :select, :options => , :validation => attributes(validation)) end |
.string ⇒ Object
Returns a block that checks the input is a string. Moreover, strings are re-evaluated as string literals using %Q.
string.class # => Proc
string.call('str') # => 'str'
string.call('\n') # => "\n"
string.call("\n") # => "\n"
string.call("%s") # => "%s"
string.call(nil) # => ValidationError
string.call(:sym) # => ValidationError
202 |
# File 'lib/configurable/validation.rb', line 202 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
220 |
# File 'lib/configurable/validation.rb', line 220 def string_or_nil(); STRING_OR_NIL; end |
.switch ⇒ Object
Same as boolean.
278 |
# File 'lib/configurable/validation.rb', line 278 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
241 |
# File 'lib/configurable/validation.rb', line 241 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
251 |
# File 'lib/configurable/validation.rb', line 251 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')
568 569 570 571 572 573 |
# File 'lib/configurable/validation.rb', line 568 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
588 |
# File 'lib/configurable/validation.rb', line 588 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.
182 183 184 185 186 187 188 |
# File 'lib/configurable/validation.rb', line 182 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 |