Rails antes de la validación strip whitespace mejores prácticas
Me gustaría que mi modelo de usuario desinfectara alguna entrada antes de guardar. Por ahora un simple stripping de espacios en blanco servirá. Así que para evitar que la gente se registre con " Harry "y pretender ser" Harry", por ejemplo.
Asumo que es una buena idea hacer este 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 a 1). Asumí la devolución de llamada se pasarían los valores.
También: ¿es este stripping realmente una buena idea? O debería validar en espacio y decirle al usuario que "Harry" contiene spacess inválido (quiero permitir "Harry Potter" pero no "Harry\s\sPotter").
Editar: Como se señaló en un comentario, mi código es incorrecto (que es por lo que estaba haciendo la pregunta a.o.). Por favor, asegúrese de leer la respuesta aceptada además de mi pregunta para el código correcto y para evitar los mismos errores que cometí.
13 answers
No creo que 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ía hacerlo más dinámico si desea usar algo como self.columns
, pero esa es la esencia de la misma.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-18 01:27:21
Hay varias gemas para hacer esto automáticamente. Esas gemas funcionan de la misma manera de crear callback en before_validation. Una buena gema 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
El stripping es a menudo 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 cadena. Para que "Harry Potter" se convierta "Harry Potter" (opción de aplastar en la gema).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-06 13:50:55
La respuesta de Charlie es buena, pero hay un poco de detalle. 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 es que Ruby interpreta estos últimos como una asignación de variable local. Simplemente establecerá la variable foo en el ámbito de su método, en lugar de llamar al método "foo=" de su objeto.
Pero si está llamando a un método, no hay ambigüedad. El intérprete sabe que no eres se refiere a una variable local llamada foo porque no hay ninguna. Así por ejemplo con:
self.foo = foo + 1
Necesita usar "self" para la tarea, pero no para leer el valor actual.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-27 10:24:08
Me gustaría agregar una trampa que podría experimentar con las soluciones "before_validations" anteriores. Tomemos este ejemplo:
u = User.new(name: " lala")
u.name # => " lala"
u.save
u.name # => "lala"
Esto significa que tiene un comportamiento inconsistente basado en si su objeto fue guardado o no. Si desea abordar esto, le sugiero otra solución a su problema: sobrescribir los métodos de configuración correspondientes.
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 el stripping para todos los atributos que lo soportan, a diferencia del attribute_names.each
mencionado anteriormente. Además, no se requieren devoluciones de llamada.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-17 13:20:15
Me gusta la respuesta de Karl, pero ¿hay una manera de hacerlo sin hacer referencia a cada uno de los atributos por nombre? Es decir, ¿hay una manera de simplemente ejecutar a través de los atributos del modelo y la tira de llamadas en cada uno (si responde a ese método)?
Esto sería deseable para no tener que actualizar el método remove_whitespace cada vez que cambie el modelo.
UPDATE
Veo que Karl insinuó que podrías querer hacer este tipo de cosas. No supe inmediatamente cómo podría hacerse, pero aquí hay algo que funciona para mí como se describió anteriormente. Probablemente hay 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-06 01:03:07
Si tiene acceso a ActiveSupport, utilice squish en lugar de strip.
Http://api.rubyonrails.org/classes/String.html#method-i-squish
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-20 04:00:30
En su lugar podemos escribir un método mejor más genérico independientemente de lo que pueda ser el tipo de atributos con el objeto (podría tener 3 campos de tipo de cadena, pocos 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 will ayude a alguien!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-25 19:54:12
StripAttributes Gem
Usé strip_attributes. Es realmente impresionante y fácil de implementar.
Comportamiento predeterminado
class DrunkPokerPlayer < ActiveRecord::Base
strip_attributes
end
De forma predeterminada, esto solo eliminará los espacios en blanco iniciales y finales y actuará sobre todos los atributos del modelo. Esto es ideal porque no es destructivo y no requiere que especifique qué atributos deben 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
Utilizando 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 expresiones regulares
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-20 17:49:36
Aquí hay un enfoque alternativo, si lo que más le preocupa es que los usuarios introduzcan datos incorrectamente 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 solicitud.js si aún no estás incluyendo todo el árbol.
Esto asegurará que cada entrada obtenga espacios en blanco iniciales y finales eliminados antes de ser enviados para ser guardados por Rails. Está vinculado a document
, y delegado a las entradas, por lo que cualquier entrada añadida a la página más tarde se procesará como bien.
Ventajas:
- No requiere enumerar atributos individuales por nombre
- No requiere ninguna metaprogramación
- No requiere dependencias de bibliotecas externas
Contras:
- Los datos enviados de cualquier otra manera que los formularios (por ejemplo, a través de API) no se recortarán
- No tiene características avanzadas como squish (pero podría agregarlo usted mismo)
- Como se mencionó en los comentarios, no funciona si JS es deshabilitado (¿pero quién codifica para eso?)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-30 08:14:21
Reemplazar el atributo write methods 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á despojado, incluyendo assign_attributes y así sucesivamente.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-05 04:13:31
Aunque podría adoptar un enfoque similar a la respuesta de Karl, prefiero una sintaxis más concisa con menos asignaciones:
def strip_whitespace
self.name.try(:strip!)
self.email.try(:strip!)
self.nick.try(:strip!)
end
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-22 19:37:21
Como aún no puedo comentar, tendré que preguntar aquí: ¿qué método está dando el Argumententerror? strip
, o responds_to?
Además, .strip
elimina solo los espacios en blanco iniciales y finales. Si quieres que" Harry Potter " con dos espacios no sea aceptado, tendrías que usar una expresión regular o, más simplemente, podrías llamar .dividir, que elimina espacios, y volver a concatenar la cadena con un solo espacio.
En cuanto a si el stripping es una buena idea, no veo un problema cuando es solo el espacio en blanco inicial/final. Sin embargo, si hay varios espacios entre palabras, 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 lo que envió.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-14 07:48:22
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 Eliminará los espacios en blanco iniciales y finales.
normalize_attribute :author, :with => :strip
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-08 16:10:14