23

Railsアプリケーションの認証にクリアランスを使用しています。ミックスインは私のモデルにいくつかのClearance::User検証を追加しますがUser、削除またはオーバーライドしたい検証が1つあります。これを行うための最良の方法は何ですか?

問題の検証は

validates_uniqueness_of :email, :case_sensitive => false

それ自体は悪くありませんが、追加する必要があり:scope => :account_idます。問題は、これをUserモデルに追加すると

validates_uniqueness_of :email, :scope => :account_id

私は両方の検証を取得し、クリアランスが追加するものは私のものよりも制限が厳しいので、私のものは効果がありません。私だけが実行されることを確認する必要があります。どうすればよいですか?

4

9 に答える 9

9

Spree 製品プロパティの検証を削除する必要がありましたが、:valueより簡単なソリューションがあるようです。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

そして、ここでそれをオーバーライドします

Spree::ProductProperty.class_eval do    
  clear_validators!
  validates :property, presence: true
end
于 2014-07-17T00:28:25.390 に答える
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

誰かがより良い方法を知っていれば、私はとても感謝しています。

于 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 に答える
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

これが私のために働いたRails3の「ソリューション」です(誰かがもっと良い方法を持っているなら、それを提供してください!)

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 に答える