require "active_model/validator"
require "activemodel_email_address_validator/email_address"

class EmailAddressValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    rules = Array(options[:with] || [])
    if rules.any?
      # We are using the new syntax with support for multiple validations
      invalidate(record, attribute) unless all_rules_pass?(rules, value)
    else
      # We are using the old syntax, assume regular expressions
      address = ActiveModelEmailAddressValidator::EmailAddress.new(value)
      regex = options[:format]
      invalidate(record, attribute) unless address.valid?(regex)
    end
  end

  private

  def all_rules_pass?(rules, address)
    evaluators = rules.map { |rule| build_evaluator(rule) }
    results = evaluators.map { |evaluator| evaluator.call(address) }
    results.all?
  end

  # Returns an evaluator for the given rule. Evaluators are objects that respond
  # to `#call` with an arity of 1; the raw attribute value in question.
  #
  # The return-value of `#call` should be `true` if the address passes the given
  # rule, false otherwise.
  def build_evaluator(rule)
    case rule
    when Regexp
      proc { |a| a =~ rule }
    else
      rule
    end
  end

  def invalidate(record, attribute)
    record.errors.add(attribute, :invalid)
  end
end

require "active_model/validator"
require "activemodel_email_address_validator/email_address"

class EmailAddressValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    rules = Array(options[:with] || [])
    if rules.empty?
      # We are using the old syntax, assume regular expressions
      rules = [build_rule_from_format(options[:format])]
    end

    invalidate(record, attribute) unless all_rules_pass?(rules, value)
  end

  private

  def all_rules_pass?(rules, address)
    evaluators = rules.map { |rule| build_evaluator(rule) }
    results = evaluators.map { |evaluator| evaluator.call(address) }
    results.all?
  end

  # Returns an evaluator for the given rule. Evaluators are objects that respond
  # to `#call` with an arity of 1; the raw attribute value in question.
  #
  # The return-value of `#call` should be `true` if the address passes the given
  # rule, false otherwise.
  def build_evaluator(rule)
    case rule
    when Regexp
      proc { |a| a =~ rule }
    else
      rule
    end
  end

  def build_rule_from_format(format)
    proc { |attribute_value|
      address =
        ActiveModelEmailAddressValidator::EmailAddress.new(attribute_value)
      address.valid?(format)
    }
  end

  def invalidate(record, attribute)
    record.errors.add(attribute, :invalid)
  end
end

Implement the old behavior with new syntax
Implement the old behavior with new syntax

In other words, when specifying a regex using `:format` we'll convert it
internally to a rule using `:with`.

In other words, the following:

    validates :email, :email_address => {:format => /.+@.+/}

is equivalent to this:

    validates :email, :email_address => {:with => /.+@.+/}

and is processed the same way behind the scenes.

We cannot yet remove `EmailAddress#valid_with_regex?`, unfortunately, as that is
in effect a public method and could be used externally. We should probably
deprecate it eventually.
