Das Entfernen oder eine Active Validierung zwingende durch eine übergeordnete Klasse oder mixin hinzugefügt

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

Frage

Ich bin mit Abstand für die Authentifizierung in meiner Rails-Anwendung. Die Clearance::User mixin fügt ein paar Validierungen zu meinem User Modell, aber es ist eine davon, dass ich entfernen oder überschreiben möchte. Was ist der beste Weg, dies zu tun?

Die Validierung in Frage

validates_uniqueness_of :email, :case_sensitive => false

was an sich ist nicht schlecht, aber ich würde :scope => :account_id hinzufügen müssen. Das Problem ist, dass, wenn ich hinzufügen, das mein User Modell

validates_uniqueness_of :email, :scope => :account_id

bekomme ich beide Validierungen und die eine Räumungs fügt restriktiver als meine ist, so meins hat keine Wirkung. Ich muss sicherstellen, dass nur Mine läuft. Wie kann ich das tun?

War es hilfreich?

Lösung 2

I ended up "Lösung" das Problem mit dem folgenden Hack:

  1. Suchen Sie nach einem Fehler auf dem :email Attribute vom Typ :taken
  2. Überprüfen Sie, ob die E-Mail ist einzigartig für dieses Konto (das ist die Validierung ich tun wollte)
  3. entfernen Sie den Fehler, wenn die E-Mail für dieses Konto eindeutig ist.

Klingt vernünftig, bis Sie den Code lesen und entdecken, wie ich einen Fehler entfernen. ActiveRecord::Errors hat keine Methoden Fehler zu entfernen, sobald hinzugefügt, so dass ich zu fasse seine Interna haben und es selbst tun. Super duper mega hässlich.

Dies ist der Code:

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

Wenn jemand einen besseren Weg kennt, wäre ich sehr dankbar.

Andere Tipps

Ich würde die GEM Gabel und eine einfache Prüfung hinzufügen, die dann außer Kraft gesetzt werden können. Mein Beispiel verwendet eine Sorge.

Die Sorge:

module Slugify

  extend ActiveSupport::Concern

  included do

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

  protected

  def skip_uniqueness?
    false
  end

end

Modell:

class Category < ActiveRecord::Base
  include Slugify

  belongs_to :section

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

  protected

  def skip_uniqueness?
    true
  end
end

Vor kurzem hatte ich dieses Problem und nach Google hat mir nicht die Antworten gab schnell genug fand ich ein sauberere dennoch un-ideale Lösung für dieses Problem. Nun wird dies nicht unbedingt Arbeit in Ihrem Fall, wie es Ihre Verwendung von bereits bestehenden Superklassen scheint aber für mich ist es meinen eigenen Code war so ich ein gerade verwendet. Wenn param mit einer Typprüfung in der Superklasse

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

Im Fall von multpile Unterklassen

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

Ich brauchte Spree Produkteigenschaft :value Validierung zu entfernen, und es scheint, gibt es eine einfachere Lösung mit Klass.class_eval und clear_validators! von AciveRecord::Base

module Spree
  class ProductProperty < Spree::Base

    #spree logic

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

    #spree logic


  end
end

Und es außer Kraft setzt hier

Spree::ProductProperty.class_eval do    
  clear_validators!
  validates :property, presence: true
end

Ich weiß, ich bin zu spät, um das Spiel, aber wie etwa:

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

in einer initializer?

Errors.delete (key) entfernt alle Fehler für ein Attribut, und ich möchte nur eine bestimmte Art von Fehler zu einem Attribute gehört, entfernen. Das folgende Verfahren kann zu jedem Modell hinzugefügt werden.

Returns Nachricht, wenn entfernt, Null sonst. Interne Datenstrukturen werden so modifiziert, dass alle anderen Methoden sollten nach Fehlerbeseitigung erwartet.

Veröffentlicht unter MIT-Lizenz

Methode Fehler von Modell zu entfernen, nachdem Validierungen ausgeführt wurden.

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

Verbrauch:

user.remove_error!(:email, :taken)

Methode zur Überprüfung der Gültigkeit der Ausnahme angegebenen Attribute und Nachrichten.

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

Verbrauch:

user.valid_except?({email: :blank})
user.valid_except?({email: "can't be blank"})

In Rails 4, sollten Sie in der Lage seines skip_callback(:validate, :name_of_validation_method) zu verwenden ..., wenn Sie Haben eine bequem genannte Validierungsmethode. . (Disclaimer:. Ich habe nicht getestet, dass) Wenn nicht, müssen Sie in die Liste der Rückrufe hacken die, die Sie überspringen wollen zu finden, und verwenden Sie seine filter Objekt

Beispiel:

Ich bin auf einer Baustelle arbeiten mit Rails 4.1.11 und Spree 2.4.11.beta, Spree von 2.1.4 aktualisiert haben. Unser Code speichert mehrere Kopien von Spree::Variants in einer Tabelle, aus historischen Gründen.

Seit dem Upgrade des Edelstein jetzt validates_uniqueness_of :sku, allow_blank: true, conditions: -> { where(deleted_at: nil) }, die bricht unseren Code. Wie Sie bemerken, obwohl, ist es nicht eine benannte Methode, dies zu tun zu verwenden. Dies ist, was ich in einem Spree::Variant.class_eval Block getan haben:

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)

Dies scheint völlig den Rückruf von Variant der Kette zu entfernen.

NB. Ich habe instance_variable_get für @attributes zu verwenden, da es keine Accessor es hat. Sie können c.filter.options im find Block als auch überprüfen; im obigen Beispiel, das sieht aus wie:

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

Hier ist eine Rails 3 „Lösung“, die für mich gearbeitet (wieder, wenn jemand eine bessere Art und Weise hat Sie bietet es!)

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

Für mich auf meinem Modell Code unten war genug. Ich will nicht zipcode zu überprüfen.

after_validation :remove_nonrequired

def remove_nonrequired
  errors.messages.delete(:zipcode)
end
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top