23

我在我的 Rails 应用程序中使用 Clearance 进行身份验证。mixin 为我的模型Clearance::User添加了几个验证User,但我想删除或覆盖其中一个。这样做的最佳方法是什么?

有问题的验证是

validates_uniqueness_of :email, :case_sensitive => false

这本身还不错,但我需要添加:scope => :account_id. 问题是如果我将它添加到我的User模型中

validates_uniqueness_of :email, :scope => :account_id

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

4

9 回答 9

12

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

忧虑:

module Slugify

  extend ActiveSupport::Concern

  included do

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

  protected

  def skip_uniqueness?
    false
  end

end

模型:

class Category < ActiveRecord::Base
  include Slugify

  belongs_to :section

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

  protected

  def skip_uniqueness?
    true
  end
end
于 2014-07-24T11:04:44.973 回答
9

我需要删除 Spree 产品属性:value验证,似乎有一个更简单的Klass.class_eval解决clear_validators!方案AciveRecord::Base

module Spree
  class ProductProperty < Spree::Base

    #spree logic

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

    #spree logic


  end
end

并在这里覆盖它

Spree::ProductProperty.class_eval do    
  clear_validators!
  validates :property, presence: true
end
于 2014-07-17T00:28:25.390 回答
6

我最终通过以下技巧“解决”了这个问题:

  1. :email在type 的属性上查找错误: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

如果有人知道更好的方法,我将不胜感激。

于 2010-02-22T12:23:32.487 回答
4

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

在多个子类的情况下

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
于 2010-07-22T18:23:09.273 回答
3

在 Rails 4 中,如果您有一个命名方便的验证方法,您应该可以使用skip_callback(:validate, :name_of_validation_method)...。(免责声明:我还没有测试过。)如果没有,您需要侵入回调列表以找到您想要跳过的回调,并使用它的对象。filter

例子:

我正在使用 Rails 4.1.11 和 Spree 2.4.11.beta 的网站上工作,并从 2.1.4 升级了 Spree。Spree::Variant出于历史目的,我们的代码将 s 的多个副本存储在一个表中。

自升级以来,gem nowvalidates_uniqueness_of :sku, allow_blank: true, conditions: -> { where(deleted_at: nil) }破坏了我们的代码。但是,您会注意到,它不使用命名方法来执行此操作。这是我在一个Spree::Variant.class_eval块中所做的:

unique_sku_filter = _validate_callbacks.find do |c|
  c.filter.is_a?(ActiveRecord::Validations::UniquenessValidator) &&
    c.filter.instance_variable_get(:@attributes) == [:sku]
end.filter

skip_callback(:validate, unique_sku_filter)

这似乎Variant完全从 ' 的链中删除了回调。

注意。我不得不使用instance_variable_getfor @attributes,因为它没有访问器。您也可以签c.filter.optionsfind块;在上面的例子中,这看起来像:

c.filter.options
#=> {:case_sensitive=>true, :allow_blank=>true, :conditions=>#<Proc:... (lambda)>}
于 2017-08-29T02:55:41.067 回答
2

Errors.delete(key) 删除属性的所有错误,我只想删除属于属性的特定类型的错误。可以将以下方法添加到任何模型中。

如果删除则返回消息,否则返回 nil。内部数据结构已修改,因此所有其他方法在错误消除后应按预期工作。

MIT 许可下发布

运行验证后从模型中删除错误的方法。

def remove_error!(attribute, message = :invalid, options = {})
  # -- Same code as private method ActiveModel::Errors.normalize_message(attribute, message, options).
  callbacks_options = [:if, :unless, :on, :allow_nil, :allow_blank, :strict]
  case message
  when Symbol
    message = self.errors.generate_message(attribute, message, options.except(*callbacks_options))
  when Proc
    message = message.call
  else
    message = message
  end
  # -- end block

  # -- Delete message - based on ActiveModel::Errors.added?(attribute, message = :invalid, options = {}).
  message = self.errors[attribute].delete(message) rescue nil
  # -- Delete attribute from errors if message array is empty.
  self.errors.messages.delete(attribute) if !self.errors.messages[attribute].present?
  return message
end

用法:

user.remove_error!(:email, :taken)

检查除指定属性和消息之外的有效性的方法。

def valid_except?(except={})
  self.valid?
  # -- Use this to call valid? for superclass if self.valid? is overridden.
  # self.class.superclass.instance_method(:valid?).bind(self).call
  except.each do |attribute, message|
    if message.present?
      remove_error!(attribute, message)
    else
      self.errors.delete(attribute)
    end
  end
  !self.errors.present?
end

用法:

user.valid_except?({email: :blank})
user.valid_except?({email: "can't be blank"})
于 2013-10-19T03:39:17.153 回答
1

我知道我迟到了,但是如何:

module Clearance
  module User
    module Validations
      extend ActiveSupport::Concern

      included do
        validates :email,
          email: true,
          presence: true,
          uniqueness: { scope: :account, allow_blank: true },
          unless: :email_optional?

        validates :password, presence: true, unless: :password_optional?
      end
    end
  end
end

在初始化程序中?

于 2013-07-22T18:28:08.463 回答
-1

这是一个对我有用的 Rails 3“解决方案”(如果有人有更好的方法,请提供它!)

class NameUniqueForTypeValidator < ActiveModel::Validator

  def validate(record)
    remove_name_taken_error!(record)
  end

  def remove_name_taken_error!(record)
    errors = record.errors
    errors.each do |attribute, error|
      if attribute == :name && error.include?("taken") && record.name_unique_for_type?
        errors.messages[attribute].each do |msg|
          errors.messages[attribute].delete_at(errors.messages[attribute].index(msg)) if msg.include?("taken")
        end
        errors.messages.delete(attribute) if errors.messages[attribute].empty?
      end
    end
  end

end


ActsAsTaggableOn::Tag.class_eval do
  validates_with NameUniqueForTypeValidator

  def name_unique_for_type?
    !ActsAsTaggableOn::Tag.where(:name => name, :type => type).exists?
  end
end
于 2011-12-22T16:03:57.033 回答
-2

对我来说,下面的代码就足够了。我不想验证邮政编码。

after_validation :remove_nonrequired

def remove_nonrequired
  errors.messages.delete(:zipcode)
end
于 2014-03-14T16:28:48.123 回答