This validates a data object against a Ruby based schema to see if they match.
An object matches the schema if +==+ or +===+ returns true
Hashes match if all the key's values match the classes given in the schema as well. This can be configured in the options
Arrays match when every element in the data matches the case given in the schema.
The schema and validation are very simple and probably not suitable for some cases.
The following classes can be used to check for special behaviour
Fog::Boolean - value may be true
or false
Fog::Nullable::Boolean - value may be true
, false
or nil
Fog::Nullable::Integer - value may be an Integer or nil
Fog::Nullable::String
Fog::Nullable::Time
Fog::Nullable::Float
Fog::Nullable::Hash
Fog::Nullable::Array
All the “nullable” objects will pass if the value is of the class or if it
is nil
. This allows you to match APIs that may include keys
when the value is not available in some cases but will always be a String. Such as an password that is only
displayed on the reset action.
The keys for “nullable” resources should always be present but original matcher had a bug that allowed these to also appear to work as optional keys/values.
If you need the original behaviour, data with a missing key is still valid,
then you may pass the :allow_optional_rules
option to the validate method.
That is not recommended because you are describing a schema with optional keys in a format that does not support it.
Setting :allow_extra_keys
as true
allows the data
to contain keys not declared by the schema and still pass. This is useful
if new attributes appear in the API in a backwards compatible manner and
can be ignored.
This is the behaviour you would have seen with strict
being
false
in the original test helper.
@example Schema example
{ "id" => String, "ram" => Integer, "disks" => [ "size" => Float ], "dns_name" => Fog::Nullable::String, "active" => Fog::Boolean, "created" => DateTime }
# File lib/fog/schema/data_validator.rb, line 65 def initialize @message = nil end
This returns the last message set by the validator
@return [String]
# File lib/fog/schema/data_validator.rb, line 95 def message @message end
Checks if the data structure matches the schema passed in and returns
true
if it fits.
@param [Object] data Hash or Array to check @param [Object] schema Schema pattern to check against @param [Boolean] options @option options [Boolean] :allow_extra_keys
If +true+ does not fail if extra keys are in the data that are not in the schema.
@option options [Boolean] :allow_optional_rules
If +true+ does not fail if extra keys are in the schema that do not match the data. Not recommended!
@return [Boolean] Did the data fit the schema?
# File lib/fog/schema/data_validator.rb, line 83 def validate(data, schema, options = {}) valid = validate_value(schema, data, options) unless valid @message = "#{data.inspect} does not match #{schema.inspect}" end valid end
This contains a slightly modified version of the Hashidator gem but unfortunately the gem does not cope with Array schemas.
@see github.com/vangberg/hashidator/blob/master/lib/hashidator.rb
# File lib/fog/schema/data_validator.rb, line 106 def validate_value(validator, value, options) Fog::Logger.write :debug, "[yellow][DEBUG] #{value.inspect} against #{validator.inspect}[/]\n" case validator when Array return false if value.is_a?(Hash) value.respond_to?(:all?) && value.all? {|x| validate_value(validator[0], x, options)} when Symbol value.respond_to? validator when Hash return false if value.is_a?(Array) # When being strict values not specified in the schema are fails unless options[:allow_extra_keys] if value.respond_to?(:empty?) # Validator is empty but values are not return false if !value.empty? && validator.empty? end end unless options[:allow_optional_rules] if value.respond_to?(:empty?) # Validator has rules left but no more values return false if value.empty? && !validator.empty? end end validator.all? do |key, sub_validator| Fog::Logger.write :debug, "[blue][DEBUG] #{key.inspect} against #{sub_validator.inspect}[/]\n" validate_value(sub_validator, value[key], options) end else result = validator == value result = validator === value unless result # Repeat unless we have a Boolean already unless (result.is_a?(TrueClass) || result.is_a?(FalseClass)) result = validate_value(result, value, options) end if result Fog::Logger.write :debug, "[green][DEBUG] Validation passed: #{value.inspect} against #{validator.inspect}[/]\n" else Fog::Logger.write :debug, "[red][DEBUG] Validation failed: #{value.inspect} against #{validator.inspect}[/]\n" end result end end