class Fog::Schema::DataValidator

This validates a data object against a Ruby based schema to see if they match.

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

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
}

Attributes

message[R]

This returns the last message set by the validator

@return [String]

Public Class Methods

new() click to toggle source
# File lib/fog/schema/data_validator.rb, line 69
def initialize
  @message = nil
end

Public Instance Methods

validate(data, schema, options = {}) click to toggle source

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 87
def validate(data, schema, options = {})
  valid = validate_value(schema, data, options)

  unless valid
    @message = "#{data.inspect} does not match #{schema.inspect}"
  end
  valid
end

Private Instance Methods

validate_value(validator, value, options) click to toggle source

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 103
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
    # Validator is empty but values are not
    return false if !options[:allow_extra_keys] &&
                    value.respond_to?(:empty?) &&
                    !value.empty? &&
                    validator.empty?

    # Validator has rules left but no more values
    return false if !options[:allow_optional_rules] &&
                    value.respond_to?(:empty?) &&
                    value.empty? &&
                    !validator.empty?

    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