如何确定ActiveModel :: Errors验证类型

时间:2012-08-09 10:04:17

标签: ruby-on-rails-3 validation

从Rails 2迁移到Rails 3时,验证错误从ActiveRecord :: Error移到ActiveModel :: Errors。 在rails 2中,验证错误有类型和消息(以及其他内容),您可以通过执行以下操作来检查验证错误的类型:

rescue ActiveRecord::RecordInvalid => e
  e.record.errors.each do |attr, error|
    if error.type == :foo
      do_something
    end
  end
end

但是使用Rails 3似乎除了无效的属性和消息之外什么都丢失了。因此,确定类型的唯一方法是比较错误消息:

rescue ActiveRecord::RecordInvalid => e
  e.record.errors.each do |attr, error|
    if error == "foobar"
      do_something
    end
  end
end

哪个不理想(例如,如果您有多个使用相同消息的验证怎么办?)。

问题:
在rails 3.0中有更好的方法来确定验证错误的类型吗?

2 个答案:

答案 0 :(得分:4)

检查是否已添加?在ActiveModel ::错误:

https://github.com/rails/rails/blob/master/activemodel/lib/active_model/errors.rb#L331

允许你这样做:

record.errors.added?(:field, :error)

答案 1 :(得分:1)

我不仅需要它用于测试目的,还需要API。我最终得到了猴子补丁:

module CoreExt
  module ActiveModel
    module Errors
      # When validation on model fails, ActiveModel sets only human readable
      # messages. This does not allow programmatically identify which
      # validation rule exactly was violated.
      #
      # This module patches {ActiveModel::Errors} to have +details+ property,
      # that keeps name of violated validators.
      #
      # @example
      #   customer.valid? # => false
      #   customer.errors.messages # => { email: ["must be present"] }
      #   customer.errors.details # => { email: { blank: ["must be present"] } }
      module Details
        extend ActiveSupport::Concern

        included do
          if instance_methods.include?(:details)
            fail("Can't monkey patch. ActiveModel::Errors already has method #details")
          end

          def details
            @__details ||= Hash.new do |attr_hash, attr_key|
              attr_hash[attr_key] = Hash.new { |h, k| h[k] = [] }
            end
          end

          def add_with_details(attribute, message = nil, options = {})
            error_type = message.is_a?(Symbol) ? message : :invalid
            normalized_message = normalize_message(attribute, message, options)
            details[attribute][error_type] << normalized_message

            add_without_details(attribute, message, options)
          end
          alias_method_chain :add, :details

          def clear_with_details
            details.clear
            clear_without_details
          end
          alias_method_chain :clear, :details
        end
      end
    end
  end
end

# Apply monkey patches
::ActiveModel::Errors.send(:include, ::CoreExt::ActiveModel::Errors::Details)