报告记录验证警告和错误的有效方法?

时间:2014-07-08 09:44:46

标签: ruby-on-rails validation activemodel

我有一个Rails项目,就像在大多数应用程序中一样,我们有许多硬性和快速的验证规则,所有对象在持久化之前必须符合这些规则。当然,ActiveModel的验证是完美的 - 我们使用Rails默认值和我们自己的手动验证的组合。

但是,我们越来越多地反对用例,我们希望提醒用户注意这样的情况:尽管他们的数据在最严格的意义上并非无效,但他们应该检查哪些元素,但它本身不应该阻止记录持久性的发生。几个例子,我的头顶:

  • 已在所有大写字母中提交了帖子标题,该标题可能有效,但可能不是
  • 正文的一个或多个单词的数量少于或多于建议的单词数

验证模块是我们如何处理验证错误的一个很好的比喻 - 并且有很多匹配器已经可用 - 理想情况下我希望能够重用该基本代码,但生成{的集合{1}}项warnings旁边的项目。这将使我们能够以不同的方式突出显示这些案例,而不是暗示可能违反房屋风格的行为等同于更严重,严格执行的规则。

我查看了activemodel-warnings之类的宝石,但是他们通过更改在验证记录时检查哪些匹配器,相应地扩展或缩小errors集合来工作。类似地,我查看了内置的errors验证参数,看看我是否可以手动滚动,但是所有违规行为都会在错误集合中结束,而不是分开。

有没有人尝试过类似的东西?我无法想象我是唯一一个想实现这一目标的人,但我现在正在画一个空白......

3 个答案:

答案 0 :(得分:15)

以下是我为Rails 3项目编写的一些代码,它完全符合您在此处所说的内容。

# Define a "warnings" validation bucket on ActiveRecord objects.
#
# @example
#
#   class MyObject < ActiveRecord::Base
#     warning do |vehicle_asset|
#       unless vehicle_asset.description == 'bob'
#         vehicle_asset.warnings.add(:description, "should be 'bob'")
#       end
#     end
#   end
#
# THEN:
#
#   my_object = MyObject.new
#   my_object.description = 'Fred'
#   my_object.sensible? # => false
#   my_object.warnings.full_messages # => ["Description should be 'bob'"]
module Warnings
  module Validations
    extend ActiveSupport::Concern
    include ActiveSupport::Callbacks

    included do
      define_callbacks :warning
    end

    module ClassMethods
      def warning(*args, &block)
        options = args.extract_options!
        if options.key?(:on)
          options = options.dup
          options[:if] = Array.wrap(options[:if])
          options[:if] << "validation_context == :#{options[:on]}"
        end
        args << options
        set_callback(:warning, *args, &block)
      end
    end

    # Similar to ActiveModel::Validations#valid? but for warnings
    def sensible?
      warnings.clear
      run_callbacks :warning
      warnings.empty?
    end

    # Similar to ActiveModel::Validations#errors but returns a warnings collection
    def warnings
      @warnings ||= ActiveModel::Errors.new(self)
    end

  end
end

ActiveRecord::Base.send(:include, Warnings::Validations)

顶部的评论显示了如何使用它。您可以将此代码放入初始化程序中,然后警告应该可用于所有ActiveRecord对象。然后基本上只需在每个模型的顶部添加一个warnings do块,它可以有警告,只需手动添加任意数量的警告即可。在您在模型上调用.sensible?之前,此块将无法执行。

此外,请注意,由于警告不是验证错误,因此即使模型不明智,模型仍然在技术上有效。 (正如我所说的那样)。

答案 1 :(得分:0)

几年后,但是在较新的Rails版本中,有一种更简单的方法:

  attr_accessor :save_despite_warnings

  def warnings
    @warnings ||= ActiveModel::Errors.new(self)
  end

  before_save :check_for_warnings
  def check_for_warnings
    warnings.add(:notes, :too_long, count: 120) if notes.to_s.length > 120

    !!save_despite_warnings
  end

然后您可以做:record.warnings.full_messages

答案 2 :(得分:0)

另一种选择是为警告设置一个单独的对象,例如:

class MyModelWarnings < SimpleDelegator
  include ActiveModel::Validations

  validates :name, presence: true

  def initialize(model)
    super
    validate
  end  

  def warnings; errors; end
end

class MyModel < ActiveRecord::Base
  def warnings
    @warnings ||= MyModelWarnings.new(self).warnings
  end
end