ruby-on-rails ruby validation clearance

ruby on rails - Eliminar o anular una validación de ActiveRecord agregada por una superclase o mixin



ruby-on-rails validation (9)

Terminé "resolviendo" el problema con el siguiente truco:

  1. busque un error en el atributo de :email :taken tipo
  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 lea el código y descubra cómo elimino un error. ActiveRecord::Errors no tiene métodos para eliminar errores una vez agregados, así que tengo que agarrarme de sus elementos internos y hacerlo 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.

Estoy usando Clearance para autenticación en mi aplicación Rails. The Clearance::User mixin agrega un par de validaciones a mi modelo de User , pero hay una que me gustaría eliminar o anular. Cual 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 agregar :scope => :account_id . El problema es que si agrego esto a mi modelo de User

validates_uniqueness_of :email, :scope => :account_id

Recibo ambas validaciones, y la que Clearance añade es más restrictiva que la mía, por lo que la mía no tiene ningún efecto. Necesito asegurarme de que solo funciona la mía. ¿Cómo hago esto?


Hace poco tuve este problema y, después de que Google no me diera las respuestas lo suficientemente rápido, encontré una solución más ordenada pero aún no ideal para este problema. Ahora, esto no funcionará necesariamente en su caso, ya que parece que usa superclases preexistentes, pero para mí era mi propio código, así que acabo de usar un: si param con una verificación de tipo 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 sub clases de multiples

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


Aquí hay una "solución" de Rails 3 que funcionó para mí (de nuevo, si alguien tiene una mejor manera, por favor, ofrézcala).

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


Sé que llego tarde al juego, pero ¿qué tal?

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 de un atributo y solo quiero eliminar un tipo específico de error que pertenece a un atributo. Este siguiente método se puede agregar a cualquier modelo.

Devuelve el mensaje si se eliminó, de lo contrario no. Las estructuras de datos internas se modifican para que todos los demás métodos funcionen según lo esperado después de la eliminación del error.

Publicado bajo la licencia MIT

Método para eliminar el error del modelo después de que se hayan ejecutado las validaciones.

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 verificar la validez, excepto los 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"})


Para mí, en mi modelo a continuación, el código era suficiente. No quiero que se valide el código postal.

after_validation :remove_nonrequired def remove_nonrequired errors.messages.delete(:zipcode) end


Necesitaba eliminar la propiedad del producto Spree :value validación de :value y parece que hay una solución más Klass.class_eval 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


Bifurcaría el GEM y agregaría un cheque simple, que luego podría anularse. Mi ejemplo usa una preocupación.

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


En Rails 4, debería poder usar skip_callback(:validate, :name_of_validation_method) ... si tiene un método de validación convenientemente nombrado. (Descargo de responsabilidad: no lo he probado). De lo contrario, deberá ingresar a la lista de devoluciones de llamada para encontrar la que desea omitir y usar su objeto de filter .

Ejemplo:

Estoy trabajando en un sitio usando Rails 4.1.11 y Spree 2.4.11.beta, habiendo actualizado Spree desde 2.1.4. Nuestro código almacena copias múltiples de Spree::Variant s en una sola tabla, 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 notará, sin embargo, no usa un método con nombre para hacerlo. Esto es lo que hice en un bloque 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)

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

NÓTESE BIEN. He tenido que usar instance_variable_get para @attributes , porque no tiene un @attributes de @attributes . También puede verificar c.filter.options en el bloque de find ; en el ejemplo anterior, esto se ve así:

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