删除或覆盖由超类或mixin添加的ActiveRecord验证

The*_*heo 21 ruby validation ruby-on-rails clearance

我在我的Rails应用程序中使用Clearance进行身份验证.该Clearance::User混入增加了一些验证的到我的User模型,但有这些,我想删除或覆盖一个.这样做的最佳方式是什么?

有问题的验证是

validates_uniqueness_of :email, :case_sensitive => false
Run Code Online (Sandbox Code Playgroud)

这本身并不坏,但我需要补充一下:scope => :account_id.问题是如果我将它添加到我的User模型中

validates_uniqueness_of :email, :scope => :account_id
Run Code Online (Sandbox Code Playgroud)

我得到了两个验证,而且Clearance添加的一个比我的更严格,所以我没有效果.我需要确保只有我的运行.我该怎么做呢?

And*_*nyy 9

我需要删除狂欢产品性能:value验证,它似乎有一个simplier溶液Klass.class_evalclear_validators!AciveRecord::Base

module Spree
  class ProductProperty < Spree::Base

    #spree logic

    validates :property, presence: true
    validates :value, length: { maximum: 255 }

    #spree logic


  end
end
Run Code Online (Sandbox Code Playgroud)

并在这里覆盖它

Spree::ProductProperty.class_eval do    
  clear_validators!
  validates :property, presence: true
end
Run Code Online (Sandbox Code Playgroud)


kim*_*3er 9

我会分配GEM并添加一个简单的检查,然后可以覆盖它.我的例子使用了一个关注点.

关心:

module Slugify

  extend ActiveSupport::Concern

  included do

    validates :slug, uniqueness: true, unless: :skip_uniqueness?
  end

  protected

  def skip_uniqueness?
    false
  end

end
Run Code Online (Sandbox Code Playgroud)

模型:

class Category < ActiveRecord::Base
  include Slugify

  belongs_to :section

  validates :slug, uniqueness: { scope: :section_id }

  protected

  def skip_uniqueness?
    true
  end
end
Run Code Online (Sandbox Code Playgroud)

  • 只是优雅。正好符合一个问题的运作,这是我目前关心的问题,哈哈 (2认同)

The*_*heo 6

我最后用以下黑客"解决"了这个问题:

  1. :email类型的属性上查找错误:taken
  2. 检查该帐户的电子邮件是否唯一(这是我想要的验证)
  3. 如果此帐户的电子邮件是唯一的,请删除该错误.

在您阅读代码并发现我如何删除错误之前,这听起来很合理.ActiveRecord::Errors没有方法可以删除一旦添加的错误,所以我必须抓住它的内部并自己做.超级超级巨型丑陋.

这是代码:

def validate
  super
  remove_spurious_email_taken_error!(errors)
end

def remove_spurious_email_taken_error!(errors)
  errors.each_error do |attribute, error|
    if error.attribute == :email && error.type == :taken && email_unique_for_account?
      errors_hash = errors.instance_variable_get(:@errors)
      if Array == errors_hash[attribute] && errors_hash[attribute].size > 1
        errors_hash[attribute].delete_at(errors_hash[attribute].index(error))
      else
        errors_hash.delete(attribute)
      end
    end
  end
end

def email_unique_for_account?
  match = account.users.find_by_email(email)
  match.nil? or match == self
end
Run Code Online (Sandbox Code Playgroud)

如果有人知道更好的方式,我将非常感激.


小智 5

我最近遇到了这个问题,在谷歌没有足够快地给我答案之后,我找到了一个更简洁但仍然不理想的解决方案来解决这个问题。现在,这不一定适用于您的情况,因为您似乎使用了预先存在的超类,但对我来说,这是我自己的代码,所以我只是在超类中使用了 :if 参数和类型检查。

def SuperClass
  validates_such_and_such_of :attr, :options => :whatever, :if => Proc.new{|obj| !(obj.is_a? SubClass)}
end

def SubClass < SuperClass
  validates_such_and_such_of :attr
end
Run Code Online (Sandbox Code Playgroud)

对于多个子类的情况

def SuperClass
  validates_such_and_such_of :attr, :options => :whatever, :if => Proc.new{|obj| [SubClass1, SubClass2].select{|sub| obj.is_a? sub}.empty?}
end

def SubClass1 < SuperClass
  validates_such_and_such_of :attr
end

def SubClass2 < SuperClass
end
Run Code Online (Sandbox Code Playgroud)