class AWS::Record::Validator

@private
Base class for all validators

def add_accessors klass, *accessors

def add_accessors klass, *accessors
  methods = klass.instance_methods.collect{|m| m.to_s }
  accessors.each do |attr|
    setter = "#{attr}="
    getter = attr.to_s
    unless methods.include?(getter)
      klass.send(:attr_reader, attr)
      klass.send(:public, getter)
    end
    unless methods.include?(setter)
      klass.send(:attr_writer, attr) 
      klass.send(:public, setter)
    end
  end
end

def each_value value, &block

def each_value value, &block
  case value
  when Array, Set then value.each{|v| yield(v) }
  else yield(value)
  end
end

def ensure_at_least_one *keys

def ensure_at_least_one *keys
  found = keys.select{|k| options.has_key?(k) }
  unless found.count >= 1
    opts = keys.collect{|k| ":#{k}" }.join(', ')
    msg = "must provide at least one of the following options: #{opts}"
    raise ArgumentError, msg
  end
end

def ensure_exclusive *key_groups

def ensure_exclusive *key_groups
  key_groups.each do |key_group|
    others = key_groups - [key_group]
    Array(key_group).each do |key|
      next unless options.has_key?(key)
      conflicts = others.flatten.select{|other| options.has_key?(other) }
      unless conflicts.empty?
        msg = ":#{key} may not be used with :#{conflicts.first}"
        raise ArgumentError, msg
      end
    end
  end
end

def ensure_is value_or_values, *keys

def ensure_is value_or_values, *keys
  values = Array(value_or_values)
  keys.each do |key|
    next unless options.has_key?(key)
    unless values.include?(options[key])
      valid = values.map{|v| v.is_a?(Symbol) ? ":#{v}" : v.to_s }.join(', ')
      raise ArgumentError, "expected :#{key} to be one of #{valid}"
    end
  end
end

def ensure_present *keys

def ensure_present *keys
  keys.each do |k|
    unless options.has_key?(k)
      raise ArgumentError, "missing required option :#{k}"
    end
  end
end

def ensure_type type_or_types, *keys

def ensure_type type_or_types, *keys
  types = Array(type_or_types)
  keys.each do |key|
    next unless options.has_key?(key)
    next unless types.none?{|type| options[key].is_a?(type) }
    expected = types.map{|type| type.to_s }
    if expected.count == 1
      raise ArgumentError, "expected option :#{key} to be a #{expected}"
    else
      msg = "expected :#{key} to be one of #{expected.join(', ')}"
      raise ArgumentError, msg
    end
  end
end

def initialize record_class, *attribute_names, &block

def initialize record_class, *attribute_names, &block
  @options = attribute_names.last.is_a?(Hash) ? attribute_names.pop : {}
  @attribute_names = attribute_names
  reject_unknown_options
  ensure_type([Symbol, Proc], :if, :unless)
  ensure_is([:save, :create, :update], :on) 
  setup(record_class)
end

def passes_if_condition? record

def passes_if_condition? record
  case options[:if]
  when nil            then true
  when Proc           then options[:if].call(record)
  when String, Symbol then record.send(options[:if])
  else
    raise 'invalid :if option, must be nil, a method name or a Proc'
  end
end

def passes_on_condition? record

def passes_on_condition? record
  case options[:on]
  when nil     then true
  when :save   then true
  when :create then record.new_record?
  when :update then record.persisted?
  else
    raise 'invalid :on option, must be nil, :save, :create or :update'
  end
end

def passes_unless_condition? record

def passes_unless_condition? record
  case options[:unless]
  when nil            then true
  when Proc           then !options[:unless].call(record)
  when String, Symbol then !record.send(options[:unless])
  else
    raise 'invalid :unless option, must be nil, a method name or a Proc'
  end
end

def read_attribute_for_validation(record, attribute_name)

def read_attribute_for_validation(record, attribute_name)
  record.send(attribute_name)
end

def reject_unknown_options

Other tags:
    Private: -
def reject_unknown_options
  invalid_keys = options.keys - self.class::ACCEPTED_OPTIONS
  if invalid_keys.length == 1
    raise ArgumentError, "unknown option :#{invalid_keys.first}"
  elsif invalid_keys.length > 1
    bad = invalid_keys.collect{|k| ":#{k}" }.join(', ')
    raise ArgumentError, "unknown options #{bad}"
  end
end

def set_default key, value

def set_default key, value
  options[key] = value unless options.has_key?(key)
end

def setup klass

def setup klass
end

def validate record

def validate record
  if 
    passes_on_condition?(record) and
    passes_if_condition?(record) and
    passes_unless_condition?(record)
  then
    validate_attributes(record)
  end
end

def validate_attributes record

def validate_attributes record
  attribute_names.each do |attribute_name|
    value = read_attribute_for_validation(record, attribute_name)
    next if value.nil? and options[:allow_nil]
    validate_attribute(record, attribute_name, value)
  end
end