5

随着从 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 中是否有更好的方法来确定验证错误的类型?

4

2 回答 2

4

检查添加?在 ActiveModel::错误上:

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

这使您可以这样做:

record.errors.added?(:field, :error)
于 2012-09-28T20:48:19.957 回答
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)
于 2016-01-06T17:25:20.253 回答