Efficient way to report record validation warnings as well as errors?

吃可爱长大的小学妹 提交于 2019-12-02 20:39:14

Here is some code I wrote for a Rails 3 project that does exactly what you're talking about here.

# 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)

The comments at the top show how to use it. You can put this code into an initializer and then warnings should be available to all of your ActiveRecord objects. And then basically just add a warnings do block to the top of each model that can have warnings and just manually add as many warnings as you want. This block won't be executed until you call .sensible? on the model.

Also, note that since warnings are not validation errors, a model will still be technically valid even if it isn't "sensible" (as I called it).

Years later, but in newer Rails versions there's a bit easier way:

  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

Then you can do: record.warnings.full_messages

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!