class Bodhi::TypeValidator

Attributes

reference[R]
type[R]

Public Class Methods

new(type, reference=nil) click to toggle source
# File lib/bodhi-slam/validators/type.rb, line 5
def initialize(type, reference=nil)
  if type.nil?
    raise ArgumentError.new("Expected :type to not be nil")
  end

  @type = type
  @reference = reference if reference
end

Public Instance Methods

to_options() click to toggle source
# File lib/bodhi-slam/validators/type.rb, line 104
def to_options
  if @reference.nil?
    {self.to_sym => @type}
  else
    {self.to_sym => @type, ref: @reference}
  end
end
validate(record, attribute, value) click to toggle source
# File lib/bodhi-slam/validators/type.rb, line 14
def validate(record, attribute, value)
  unless value.nil?

    # Default values for comparators and messages
    klass = nil
    single_comparator = ->(item){ item.is_a? klass }
    array_comparator = ->(items){ items.select{ |item| !item.is_a?(klass) }.empty? }
    single_message = "must be a #{@type}"
    array_message = "must contain only #{@type}s"

    # Check what the given type is, and assign the correct comparator and messages
    case @type
    when "GeoJSON"
      klass = Hash
    when "Link"
      klass = Hash
    when "DateTime"
      klass = Time
    when "Object"
      klass = Hash
      single_message = "must be a JSON object"
      array_message = "must contain only JSON objects"
    when "Real"
      klass = Float
    when "Boolean"
      single_comparator = ->(item){ item.is_a?(TrueClass) || item.is_a?(FalseClass) }
      array_comparator = ->(items){ items.delete_if{ |item| item.is_a?(TrueClass) || item.is_a?(FalseClass) }.empty? }
    when "Enumerated"
      if @reference.nil?
        raise RuntimeError.new("Enumerated reference is missing!  Cannot validate #{record.class}.#{attribute}=#{value}")
      end

      single_message = "must be a #{@reference}"
      array_message = "must contain only #{@reference} objects"

      name = @reference.split(".")[0]
      field = @reference.split(".")[1]

      enumeration = Bodhi::Enumeration.cache[name.to_sym]
      if field.nil?
        single_comparator = ->(item){ enumeration.values.include?(item) }
        array_comparator = ->(items){ items.select{ |item| !enumeration.values.include?(item) }.empty? }
      else
        flattened_values = enumeration.values.map{|val| val[field.to_sym] }
        single_comparator = ->(item){ flattened_values.include?(item) }
        array_comparator = ->(items){ items.select{ |item| !flattened_values.include?(item) }.empty? }
      end
    else # type is an embedded type
      klass = Object.const_get(@type)
    end

    # Do the validations and add any error messages
    if value.is_a?(Array)
      if !value.empty?
        if !array_comparator.call(value)
          record.errors.add(attribute, array_message)
        else # validate each value in the array if it responds to :valid?
          value.each_with_index do |item, index|
            if item.respond_to?(:valid?)
              item.valid?
              if item.errors.any?
                item.errors.each do |error_attribute, message|
                  full_name = "#{attribute}[#{index}].#{error_attribute}".to_sym
                  record.errors.add(full_name, message)
                end
              end
            end
          end
        end
      end
    else
      if !single_comparator.call(value)
        record.errors.add(attribute, single_message)
      else # validate the value if it responds to :valid?
        if value.respond_to?(:valid?)
          value.valid?
          if value.errors.any?
            value.errors.each do |error_attribute, error|
              full_path = "#{attribute}.#{error_attribute}".to_sym
              record.errors.add(full_path, error)
            end
          end
        end
      end
    end

    # Party time, excellent!
  end
end