2

In my pursuit to add some dynamic policy logic to my ActiveRecord models I've attempted to create a way to add instance-level validations. Has anyone had experience with this? The stuff I've searched for has been less than helpful. Here is the solution I came up with. Please critique.

# This extension can be used to create instance-level validations. For example
# if you have an instance of 'user' you can do something like the following:
# @example:
#   user.custom_validation ->(scope) {
#     if scope.bad_logins >= scope.account.max_bad_logins
#       scope.errors.add :bad_logins, "too many bad logins for your account policy"
#     end
#   }
#   user.account.max_bad_logins = 5
#   user.bad_logins = 5
#   user.valid?   => false
#
module ActiveRecordExtension
  module CustomValidation

    def self.included(base)
      base.class_eval do
        attr_accessor :custom_validation
        validate :run_custom_validation
        send :include, InstanceMethods
      end
    end

    module InstanceMethods
      def run_custom_validation
        if custom_validation
          custom_validation.call(self)
        else
          true
        end
      end
    end

  end
end

ActiveRecord::Base.send :include, ActiveRecordExtension::CustomValidation
4

1 回答 1

2

这违反了关注点分离。您正在将模型验证逻辑移动到控制器代码中。您真的不希望您的控制器知道是什么使模型有效 - 他们应该简单地将数据传递给模型并返回有效或无效响应。如果您有一个仅应在模型的某些实例上运行的验证器,那么您可以将这些验证的范围限定为在设置某些条件时运行。

class User
  attr_accessor :enforce_login_limits
  validate :if => :enforce_login_limits do |user|
    if user.bad_logins >= user.account.max_bad_logins
      user.errors.add :bad_logins, "too many bad logins for your account policy"
    end
  end
end

# Controller
user.enforce_login_limits = true
user.bad_logins = 10
user.valid? # => false

或者,只需使用 ActiveModel 的现有#validates_with机制附加自定义验证器:

# Controller/service/whatever
@user.validates_with Validators::BadLoginValidator

# lib/validators/bad_login_validator.rb
class Validators::BadLoginValidator < ActiveModel::Validator
  def validate(user)
    if user.bad_logins && user.bad_logins >= user.account.max_bad_logins
      user.errors.add :bad_logins, "too many bad logins for your account policy"
    end
  end
end
于 2013-09-04T16:47:54.473 回答