Domanda

Sto usando Liquidazione per l'autenticazione nella mia applicazione Rails. Il mixin Clearance::User aggiunge un paio di convalide al mio modello User, ma non c'è uno di questi che vorrei rimuovere o sostituire. Qual è il modo migliore di fare questo?

La convalida in questione è

validates_uniqueness_of :email, :case_sensitive => false

che di per sé non è male, ma avrei bisogno di aggiungere :scope => :account_id. Il problema è che se aggiungo questo per il mio modello User

validates_uniqueness_of :email, :scope => :account_id

ho sia validazioni, e quello Clearance aggiunge è più restrittiva del mio, quindi il mio non ha alcun effetto. Ho bisogno di fare in modo che solo mia corre. Come posso fare questo?

È stato utile?

Soluzione 2

Ho finito per "risolvere" il problema con il seguente trucco:

  1. sguardo per un errore l'attributo di tipo :email :taken
  2. controllare se l'email è unico per questo account (che è la convalida che volevo fare)
  3. rimuovere l'errore se l'email è unico per questo account.

Suoni ragionevole fino a leggere il codice e scoprire come rimuovo un errore. ActiveRecord::Errors non ha metodi per rimuovere gli errori, una volta aggiunti, quindi devo afferrare IT di interni e di farlo anche io. Super duper mega brutto.

Questo è il codice:

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

Se qualcuno sa di un modo migliore, sarei molto grato.

Altri suggerimenti

Mi piacerebbe fork del GEM e aggiungi un semplice controllo, che possono poi essere ignorata. Il mio esempio utilizza una preoccupazione.

La preoccupazione:

module Slugify

  extend ActiveSupport::Concern

  included do

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

  protected

  def skip_uniqueness?
    false
  end

end

Modello:

class Category < ActiveRecord::Base
  include Slugify

  belongs_to :section

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

  protected

  def skip_uniqueness?
    true
  end
end

Recentemente ho avuto questo problema e dopo che Google non mi ha dato le risposte abbastanza veloci ho trovato una soluzione più ordinato ma ancora non-ideale a questo problema. Ora, questo non necessariamente funziona nel vostro caso come sembra la vostra utilizzando le classi di super preesistente, ma per me è stato il mio codice così ho usato un:. Se param con un controllo di tipo nella 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

Nel caso di sottoclassi 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

Avevo bisogno di rimuovere il prodotto Spree convalida proprietà :value e sembra c'è una soluzione più semplice con Klass.class_eval e clear_validators! di AciveRecord::Base

module Spree
  class ProductProperty < Spree::Base

    #spree logic

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

    #spree logic


  end
end

E ignorare qui

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

Lo so, sono in ritardo al gioco, ma come circa:

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 un inizializzatore?

Errors.delete (chiave) rimuove tutti gli errori per un attributo e ho solo voglia di rimuovere un tipo specifico di errore appartenente ad un attributo. Questo metodo seguente può essere aggiunto a qualsiasi modello.

Restituisce un messaggio se rimosso, altrimenti nullo. strutture di dati interni vengono modificati in modo che tutti gli altri metodi dovrebbero funzionare come previsto dopo la rimozione di errore.

Metodo eliminare l'errore dal modello convalide sono stati eseguiti.

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

Utilizzo:

user.remove_error!(:email, :taken)

metodo di verifica della validità tranne gli attributi e messaggi specificati.

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

Utilizzo:

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

In Rails 4, si dovrebbe essere in grado di utilizzare skip_callback(:validate, :name_of_validation_method) ... se sono un metodo di validazione comodamente nome. . (Disclaimer:. Non ho provato che) In caso contrario, sarà necessario incidere nella lista di callback per trovare quello che si desidera saltare, e utilizzare il suo oggetto filter

Esempio:

Sto lavorando su un sito utilizzando Rails 4.1.11 e Spree 2.4.11.beta, dopo aver aggiornato Spree da 2.1.4. Il nostro codice memorizza copie multiple di Spree::Variants in una tabella, per scopi storici.

Dato che l'aggiornamento, la gemma ora validates_uniqueness_of :sku, allow_blank: true, conditions: -> { where(deleted_at: nil) }, che rompe il nostro codice. Come si noterà, però, non fa uso di un metodo chiamato a farlo. Questo è quello che ho fatto in un blocco 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)

Questo sembra rimuovere la richiamata da catena di Variant del tutto.

NB. Ho dovuto usare instance_variable_get per @attributes, perché non dispone di una funzione di accesso ad esso. È possibile controllare c.filter.options nel blocco find pure; Nell'esempio di cui sopra, questo appare come:

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

Ecco una "soluzione" Rails 3 che ha funzionato per me (sempre se qualcuno ha un modo migliore si prega di offrirlo!)

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

Per quanto mi riguarda il mio modello sottostante Codice è stato sufficiente. Non voglio codice postale per convalidare.

after_validation :remove_nonrequired

def remove_nonrequired
  errors.messages.delete(:zipcode)
end
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top