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



我有一个Rails项目,与大多数应用程序一样,我们有许多严格的验证规则,所有对象在持久化之前必须遵守这些规则。当然,ActiveModel的验证是完美的——我们使用Rails默认值和我们自己的手工验证的组合。

然而,我们越来越多地遇到这样的用例,我们想要提醒用户注意这样的情况,虽然他们的数据在严格意义上不是无效的,但他们应该检查一些元素,但这些元素本身不应该阻止记录持久化的发生。下面是我想到的几个例子:

  • 已提交的文章标题全部大写,这可能是有效的,但可能不是
  • 一段正文大于或少于建议字数x个字

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

我已经看过宝石,如activmodel -warnings,但它们的工作方式是在验证记录时改变检查匹配器,相应地扩展或缩小errors集合。类似地,我查看了用于验证的内置:on参数,以查看是否可以手动滚出某些内容,但是所有违规都将在错误集合中结束,而不是分离出来。

有人尝试过类似的东西吗?我不能想象我是唯一一个想实现这个目标的人,但是我现在一片空白…

这是我为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?之前,这个块不会被执行。

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

多年后,但在较新的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

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

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

最新更新