Question

J'utilise Clearance pour l'authentification dans mon application Rails. Le mixin Clearance::User ajoute quelques à mon modèle de validation de User, mais il est l'un de ceux-ci que je voudrais supprimer ou remplacer. Quelle est la meilleure façon de le faire?

La validation en question est

validates_uniqueness_of :email, :case_sensitive => false

ce qui en soi est pas mal, mais il me faudrait ajouter :scope => :account_id. Le problème est que si j'ajoute à mon modèle User

validates_uniqueness_of :email, :scope => :account_id

Je reçois les validation et une Clearance est plus restrictive ajoute que le mien, le mien n'a aucun effet. Je dois vous assurer que seulement le mien court. Comment puis-je faire?

Était-ce utile?

La solution 2

J'ai fini par « résoudre » le problème avec le hack suivant:

  1. Rechercher une erreur sur l'attribut :email de type :taken
  2. vérifier si l'e-mail est unique pour ce compte (qui est la validation que je voulais faire)
  3. supprimer l'erreur si l'e-mail est unique pour ce compte.

Sons raisonnable jusqu'à ce que vous lisez le code et découvrez comment supprimer une erreur. ActiveRecord::Errors n'a pas des méthodes pour supprimer les erreurs une fois ajoutées, donc je dois attraper internals et de le faire de moi-même. Super Duper méga laid.

Voici le 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

Si quelqu'un connaît une meilleure façon, je serais très reconnaissant.

Autres conseils

Je FOURCHE le GEM et ajouter une simple vérification, qui peut ensuite être surchargée. Mon exemple utilise une préoccupation.

Inquiétude:

module Slugify

  extend ActiveSupport::Concern

  included do

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

  protected

  def skip_uniqueness?
    false
  end

end

Modèle:

class Category < ActiveRecord::Base
  include Slugify

  belongs_to :section

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

  protected

  def skip_uniqueness?
    true
  end
end

J'ai eu récemment ce problème et après Google ne m'a pas donné les réponses rapides assez j'ai trouvé une solution encore plus net encore non idéale à ce problème. Maintenant, cela ne fonctionnera pas nécessairement dans votre cas car il semble que votre utilisation de super-classes pré-existantes, mais pour moi, il était mon propre code si je viens d'utiliser un. Si un chèque param de type dans la classe super

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

Dans le cas des classes multpile sous

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

Je avais besoin de retirer la validation de :value de propriété du produit de la Spree et il semble qu'il y ait une solution avec Klass.class_eval et simplier clear_validators! de AciveRecord::Base

module Spree
  class ProductProperty < Spree::Base

    #spree logic

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

    #spree logic


  end
end

Et la remplacer ici

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

Je sais que je suis en retard dans le jeu, mais que diriez-vous:

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

dans un initialiseur?

Errors.delete (clé) supprime toutes les erreurs pour un attribut et je veux seulement supprimer un type d'erreur appartenant à un attribut. Cette méthode suivante peut être ajouté à tout modèle.

Retourne le message si supprimé, sinon nul. structures de données internes sont modifiées de sorte que tous les autres méthodes devraient fonctionner comme prévu après le retrait d'erreur.

MIT License

Méthode pour supprimer l'erreur de modèle après validation ont été réalisées.

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

Utilisation:

user.remove_error!(:email, :taken)

Méthode pour vérifier la validité des attributs et à l'exception des messages spécifiés.

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

Utilisation:

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

Dans Rails 4, vous devriez pouvoir utiliser skip_callback(:validate, :name_of_validation_method) ... si vous Vous une méthode de validation commodément nommé. . (Avertissement:. Je n'ai pas testé que) Sinon, vous aurez besoin de pirater la liste des callbacks pour trouver celui que vous voulez sauter et utiliser son objet filter

Exemple:

Je travaille sur un site en utilisant Rails 4.1.11 et Spree 2.4.11.beta, après avoir mis à jour Spree de 2.1.4. Notre code stocke plusieurs copies de Spree::Variants dans une table, à des fins historiques.

Depuis la mise à niveau, le joyau maintenant validates_uniqueness_of :sku, allow_blank: true, conditions: -> { where(deleted_at: nil) }, qui rompt notre code. Comme vous le remarquerez, cependant, il n'utilise pas une méthode appelée à le faire. Voilà ce que je l'ai fait dans un bloc de 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)

Il semble supprimer le rappel de la chaîne de Variant entièrement.

NB. J'ai dû utiliser instance_variable_get pour @attributes, car il ne dispose pas d'un accesseur à elle. Vous pouvez vérifier c.filter.options dans le bloc find ainsi; dans l'exemple ci-dessus, cela ressemble à:

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

Voici un Rails 3 « solution » qui a fonctionné pour moi (encore une fois si quelqu'un a une meilleure façon s'il vous plaît offrir!)

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

Pour moi, sur mon modèle ci-dessous le code était suffisant. Je ne veux pas valider code postal.

after_validation :remove_nonrequired

def remove_nonrequired
  errors.messages.delete(:zipcode)
end
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top