の除去、またはオーバーライドするActiveRecordの検証を追加"スーパークラスはmixin

StackOverflow https://stackoverflow.com/questions/2309757

質問

私が使っている通関のための認証を私のレールに願います。の Clearance::User mixinを追加しカップルの検証によっ User モデルが表示されていただきたい削除、またはオーバーライド.には、どうするのがベストなの?

の検証であるという

validates_uniqueness_of :email, :case_sensitive => false

これ自体は悪くないがっていなければいけないと考えて追加 :scope => :account_id.問題は場合を追加することを私 User モデル

validates_uniqueness_of :email, :scope => :account_id

を取得しま 検証により、隙間追加しより厳しい方のように鉱山にしても、何の効果もありません。いることを、確認する必要がありみ鉱山運行しています。いるのか?

役に立ちましたか?

解決 2

当社が"解決"の問題点は以下のhack:

  1. みエラーの :email 属性の型 :taken
  2. 確認のメールはこのアカウントの検証したい)
  3. 削除し、エラーの場合はメールには、よりいます。

音声合理的なまでの読み込みコードの見かに削除します。 ActiveRecord::Errors のない方法で除去の誤差回数を増加さを鷲掴みにしたので炉内構造物です。スーパー duperメガ醜い.

このコード:

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

誰もが知って、より良いるのですが、非常に感謝しています。

他のヒント

私は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

私は最近、この問題を持っていたし、Googleは私に答えを与えなかった後に迅速に十分な私は、この問題に対するすっきり依然として非理想的なソリューションを見つけました。今では、あなたの使用して、既存のスーパークラスだと、これはあなたのケースでは必ずしも動作しますが、私はちょうど使用して私のためにそれは私自身のコードだった:。もしスーパークラスの型チェックとのparam

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
multpileサブクラスの場合には、

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

私はシュプレー製品財産:valueの検証を削除するために必要な、Klass.class_evalclear_validators!AciveRecord::Baseとsimplierソリューションがありますようです。

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

私はゲームに遅刻知っているが、方法について:

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
イニシャライザで

Errors.delete(キー)属性のすべてのエラーを削除し、私は唯一の属性に属するエラーの特定のタイプを削除したいです。この次のメソッドは、すべてのモデルに追加することができます。

削除、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"})

でのRails 4、あなたはのHAVE の便利な名前の検証方法ならば... skip_callback(:validate, :name_of_validation_method)を使用することができるはずです。 の(免責事項:私はそれをテストしていません)のない場合、あなたはスキップしたいものを見つけるためにコールバックのリストにハッキングし、そのfilterオブジェクトを使用する必要があります。

例:

私は2.1.4からアップグレードシュプレーを持つ、Railsの4.1.11とシュプレー2.4.11.betaを使用してサイトに取り組んでいます。我々のコードを格納歴史的目的のための1つのテーブル内Spree::Variantsの複数のコピー、

アップグレードので、今validates_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のチェーンからのコールバックを削除します。

NB。それはそれへのアクセサを持っていないので、私は、instance_variable_getため@attributesを使用しなければなりませんでした。あなたは同様にc.filter.optionsブロックにfindを確認することができます。上記の例では、このようになります:

c.filter.options
#=> {:case_sensitive=>true, :allow_blank=>true, :conditions=>#<Proc:... (lambda)>}

ここでは私のために働いたの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
コードの下の私のモデルの私にとっては、

は十分でした。私が検証するために郵便番号を望んでいない。

after_validation :remove_nonrequired

def remove_nonrequired
  errors.messages.delete(:zipcode)
end
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top