Das Entfernen oder eine Active Validierung zwingende durch eine übergeordnete Klasse oder mixin hinzugefügt
-
22-09-2019 - |
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?
Lösung 2
I ended up "Lösung" das Problem mit dem folgenden Hack:
- Suchen Sie nach einem Fehler auf dem
:email
Attribute vom Typ:taken
- Überprüfen Sie, ob die E-Mail ist einzigartig für dieses Konto (das ist die Validierung ich tun wollte)
- 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::Variant
s 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