validator validations validates_presence_of validates rails method greater_than custom create ruby-on-rails validation model

ruby on rails - validations - Rails before_validation strip mejores prácticas de espacio en blanco



rails validates if (13)

Me gustaría que mi modelo de usuario desinfecte alguna entrada antes de guardar. Por ahora, algunos simples espacios en blanco pelarán. Entonces, para evitar que la gente se registre con "Harry" y pretenda ser "Harry", por ejemplo.

Supongo que es una buena idea hacer esto stripping antes de la validación, para que validates_uniqueness_of pueda evitar duplicados accidentales.

class User < ActiveRecord::Base has_many :open_ids validates_presence_of :name validates_presence_of :email validates_uniqueness_of :name validates_uniqueness_of :email validates_format_of :email, :with => //A([^@/s]+)@((?:[-a-z0-9]+/.)+[a-z]{2,})/Z/i before_validation :strip_whitespace, :only => [:name, :email, :nick] private def strip_whitespace(value) value.responds_to?(''strip'') ? value.strip : value end end

Sin embargo, este código viene con un error ArgumentError: número incorrecto de argumentos (0 para 1). Supuse que la devolución de llamada pasaría los valores.

Además: ¿esta extracción es realmente una buena idea? O debería validar el espacio y decirle al usuario que "Harry" contiene un spacess no válido (quiero permitir "Harry Potter" pero no "Harry / s / sPotter").

Editar: Como se señaló en un comentario, mi código es incorrecto (razón por la cual hice la pregunta ao). Asegúrese de leer la respuesta aceptada además de mi pregunta sobre el código correcto y evitar los mismos errores que cometí.


StripAttributes Gem

Usé strip_attributes . Es realmente increíble y fácil de implementar.

Comportamiento por defecto

class DrunkPokerPlayer < ActiveRecord::Base strip_attributes end

Por defecto, esto solo quitará los espacios en blanco iniciales y finales y actuará en todos los atributos del modelo. Esto es ideal porque no es destructivo y no requiere que especifique qué atributos necesitan ser rayados.

Usando except

# all attributes will be stripped except :boxers class SoberPokerPlayer < ActiveRecord::Base strip_attributes :except => :boxers end

Usando only

# only :shoe, :sock, and :glove attributes will be stripped class ConservativePokerPlayer < ActiveRecord::Base strip_attributes :only => [:shoe, :sock, :glove] end

Usando allow_empty

# Empty attributes will not be converted to nil class BrokePokerPlayer < ActiveRecord::Base strip_attributes :allow_empty => true end

Usando collapse_spaces

# Sequential spaces in attributes will be collapsed to one space class EloquentPokerPlayer < ActiveRecord::Base strip_attributes :collapse_spaces => true end

Usando regex

class User < ActiveRecord::Base # Strip off characters defined by RegEx strip_attributes :only => [:first_name, :last_name], :regex => /[^[:alpha:]/s]/ # Strip off non-integers strip_attributes :only => [:phone], :regex => /[^0-9]/ end


Anular los métodos de escritura de atributos es otra buena manera. Por ejemplo:

class MyModel def email=(value) super(value.try(:strip)) end end

Entonces, cualquier parte de la aplicación que establezca el valor lo tendrá desmantelado, incluidos assign_attributes y así sucesivamente.


Aquí hay un enfoque alternativo, si lo que más le preocupa es que los usuarios ingresen datos erróneamente en sus formularios de front-end ...

# app/assets/javascripts/trim.inputs.js.coffee $(document).on "change", "input", -> $(this).val $(this).val().trim()

Luego incluya el archivo en su application.js si aún no está incluyendo el árbol completo.

Esto asegurará que cada entrada elimine el espacio en blanco inicial y final antes de enviarlo para que Rails lo guarde. Está vinculado al document y se delega a las entradas, por lo que también se procesarán las entradas añadidas a la página más tarde.

Pros:

  • No requiere el listado de atributos individuales por nombre
  • No requiere ninguna metaprogramación
  • No requiere dependencias externas de biblioteca

Contras:

  • Los datos enviados de otra forma que no sean los formularios (p. Ej., A través de API) no se recortarán
  • No tiene funciones avanzadas como squish (pero puedes agregarlo tú mismo)
  • Como se menciona en los comentarios, no funciona si JS está desactivado (¿pero quién codifica eso?)

Como aún no puedo comentar, tendré que preguntar aquí: ¿qué método está dando ArgumentError? strip , o responds_to?

Además, .strip elimina solo el espacio en blanco .strip y final. Si desea que "Harry Potter" con dos espacios no se acepte, tendrá que usar una expresión regular o, más simple, puede llamar .split, que elimina espacios, y volver a concatenar la cadena con un solo espacio.

En cuanto a si pelar es una buena idea, no veo un problema cuando solo es el espacio en blanco inicial / final. Sin embargo, si hay varios espacios entre palabras, se lo notificaría al usuario en lugar de eliminar automáticamente los espacios adicionales y darle al usuario un inicio de sesión que no es el que presentaron.


En cambio, podemos escribir un método mejor, más genérico, independientemente de cuál sea el tipo de atributos con el objeto (podría tener 3 campos de tipo cadena, pocos elementos booleanos, pocos numéricos)

before_validation :strip_input_fields def strip_input_fields self.attributes.each do |key, value| self[key] = value.strip if value.respond_to?("strip") end end

Espero que eso ayude a alguien!


Hay varias gemas para hacer esto automáticamente. Esas gemas funcionan de forma similar a la creación de devolución de llamada en before_validation. Una buena joya está en https://github.com/holli/auto_strip_attributes

gem "auto_strip_attributes", "~> 2.2" class User < ActiveRecord::Base auto_strip_attributes :name, :nick, nullify: false, squish: true auto_strip_attributes :email end

La extracción a menudo es una buena idea. Especialmente para espacios en blanco iniciales y finales. El usuario a menudo crea espacios finales al copiar / pegar valor a un formulario. Con los nombres y otras cadenas de identificación también es posible que desee aplastar la cuerda. Para que "Harry Potter" se convierta en "Harry Potter" (opción aplastar en la gema).


La respuesta de Charlie es buena, pero hay un poco de verbosidad. Aquí hay una versión más ajustada:

def clean_data # trim whitespace from beginning and end of string attributes attribute_names.each do |name| if send(name).respond_to?(:strip) send("#{name}=", send(name).strip) end end end

La razón por la que usamos

self.foo = "bar"

en lugar de

foo = "bar"

en el contexto de los objetos ActiveRecord, Ruby interpreta esto último como una asignación de variable local. Simplemente establecerá la variable foo en su ámbito de método, en lugar de llamar al método "foo =" de su objeto.

Pero si llamas a un método, no hay ambigüedad. El intérprete sabe que no se está refiriendo a una variable local llamada foo porque no la hay. Entonces, por ejemplo, con:

self.foo = foo + 1

necesita usar "self" para la tarea, pero no para leer el valor actual.


Me gusta la respuesta de Karl, pero ¿hay alguna forma de hacerlo sin hacer referencia a cada uno de los atributos por su nombre? Es decir, ¿hay alguna forma de ejecutar los atributos del modelo y la franja de llamadas en cada uno (si responde a ese método)?

Esto sería deseable, así que no tengo que actualizar el método remove_whitespace cada vez que cambio el modelo.

ACTUALIZAR

Veo que Karl insinuó que es posible que desee hacer este tipo de cosas. No supe de inmediato cómo se podría hacer, pero aquí hay algo que funciona para mí como se describió anteriormente. Probablemente haya una mejor manera de hacerlo, pero esto funciona:

def clean_data # trim whitespace from beginning and end of string attributes attribute_names().each do |name| if self.send(name.to_sym).respond_to?(:strip) self.send("#{name}=".to_sym, self.send(name).strip) end end

fin


Me gustaría agregar una trampa que pueda experimentar con las soluciones "before_validations" anteriores. Toma este ejemplo:

u = User.new(name: " lala") u.name # => " lala" u.save u.name # => "lala"

Esto significa que tiene un comportamiento incoherente en función de si su objeto se guardó o no. Si desea abordar esto, le sugiero otra solución a su problema: sobrescribir los métodos del colocador correspondiente.

class User < ActiveRecord::Base def name=(name) write_attribute(:name, name.try(:strip)) end end

También me gusta este enfoque porque no te obliga a habilitar la eliminación de todos los atributos que lo soportan, a diferencia de los attribute_names.each mencionados anteriormente. Además, no se requieren devoluciones de llamada.


No creo que la before_validation funcione así. Probablemente quieras escribir tu método así:

def strip_whitespace self.name = self.name.strip unless self.name.nil? self.email = self.email.strip unless self.email.nil? self.nick = self.nick.strip unless self.nick.nil? end

Podrías hacerlo más dinámico si quieres usar algo como self.columns , pero eso es lo esencial.


Otra opción de gema es attribute_normalizer :

# By default it will strip leading and trailing whitespace # and set to nil if blank. normalize_attributes :author, :publisher

: strip Will strip whitespace principal y posterior.

normalize_attribute :author, :with => :strip


Si bien podría adoptar un enfoque similar a la respuesta de Karl, prefiero una sintaxis más escueta con menos tareas:

def strip_whitespace self.name.try(:strip!) self.email.try(:strip!) self.nick.try(:strip!) end