Pregunta

Estoy usando Liquidación para la autenticación en mi aplicación Rails. El mixin Clearance::User agrega un par de validaciones a mi modelo User, pero hay uno de ellos que me gustaría eliminar o anulación. ¿Cuál es la mejor manera de hacer esto?

La validación en cuestión es

validates_uniqueness_of :email, :case_sensitive => false

que en sí mismo no es malo, pero necesitaría añadir :scope => :account_id. El problema es que si añado esto a mi modelo User

validates_uniqueness_of :email, :scope => :account_id

I get ambos validaciones, y la Liquidación añade es más restrictiva que la mía, así que la mía no tiene ningún efecto. Necesito para asegurarse de que sólo la mía corre. ¿Cómo puedo hacer esto?

¿Fue útil?

Solución 2

Me terminó "resolver" el problema con este truco:

  1. mirada de un error en el atributo de tipo :email :taken
  2. Compruebe si el correo electrónico es único para esta cuenta (que es la validación que quería hacer)
  3. eliminar el error si el correo electrónico es único para esta cuenta.

suena razonable hasta que haya leído el código y descubrir cómo elimino un error. ActiveRecord::Errors no tiene métodos para eliminar los errores una vez añadido, así que tengo que agarrar de TI de partes internas y yo mismo. Super Duper Mega feo.

Este es el código:

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 alguien sabe de una mejor manera, estaría muy agradecido.

Otros consejos

Me había tenedor el GEM y añadir una simple comprobación, que luego pueden ser anulados. Mi ejemplo se utiliza una preocupación.

La preocupación:

module Slugify

  extend ActiveSupport::Concern

  included do

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

  protected

  def skip_uniqueness?
    false
  end

end

Modelo:

class Category < ActiveRecord::Base
  include Slugify

  belongs_to :section

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

  protected

  def skip_uniqueness?
    true
  end
end

recientemente he tenido este problema y después de que Google no me dio las respuestas lo suficientemente rápida he encontrado una solución más limpia y aún así no-ideal a este problema. Ahora bien, esto no necesariamente funcionará en su caso, ya que parece que su uso de pre-existentes súper clases pero para mí fue mi propio código de manera que acabo de utilizar un:. Si parámetro con una verificación de tipos en la superclase

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

En el caso de clases de sub 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

que necesitaba para eliminar el producto Spree validación :value propiedad y parece que hay una solución más simple con Klass.class_eval y 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

Y anularlo aquí

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

Yo sé que estoy tarde para el juego, pero ¿qué hay de:

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

en un inicializador?

Errors.delete (clave) elimina todos los errores para un atributo y sólo desea eliminar un tipo específico de error que pertenece a un atributo. Este método siguiente se puede añadir a cualquier modelo.

Devuelve si el mensaje eliminado, nil de lo contrario. estructuras de datos internos se modifican para todos los demás métodos deben funcionar como se espera después de la eliminación de errores.

Método de eliminar el error de modelo después de validaciones se han ejecutado.

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

Uso:

user.remove_error!(:email, :taken)

Método para comprobar la validez excepto atributos y mensajes especificados.

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

Uso:

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

En los carriles 4, que debe ser capaz de utilizar skip_callback(:validate, :name_of_validation_method) ... si Tienes un método de validación convenientemente llamado. . (Negación:. No he probado eso) Si no es así, tendrá que introducirse en la lista de las devoluciones de llamada a encontrar la que desea saltar, y el uso de su objeto filter

Ejemplo:

Estoy trabajando en un sitio usando Rails 4.1.11 y 2.4.11.beta Spree, después de haber actualizado desde Spree 2.1.4. Nuestras tiendas de códigos múltiples copias de Spree::Variants en una mesa, con fines históricos.

Desde la actualización, la gema ahora validates_uniqueness_of :sku, allow_blank: true, conditions: -> { where(deleted_at: nil) }, que rompe nuestro código. Como se dará cuenta, sin embargo, que no utiliza un método denominado de hacerlo. Esto es lo que he hecho en un bloque 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)

Esto parece eliminar la devolución de llamada de la cadena de Variant por completo.

NB. He tenido que utilizar para instance_variable_get @attributes, ya que no tiene un descriptor de acceso a la misma. Puede comprobar c.filter.options en el bloque find así; en el ejemplo anterior, esto se parece a:

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

Aquí hay una Rieles "solución" 3 que trabajó para mí (de nuevo, si alguien tiene una mejor manera por favor ofrecerlo!)

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

Para mí en mi siguiente modelo de código era suficiente. No quiero código postal para validar.

after_validation :remove_nonrequired

def remove_nonrequired
  errors.messages.delete(:zipcode)
end
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top