2011-03-02 20 views
15

Estoy tratando de asegurar que un campo de mi modelo sea booleano, pero mis pruebas siguen fallando.Rieles: la validación de la inclusión de un booleano falla las pruebas

Después de leer esto: Validating boolean value in Rspec and Rails y esto Rails: how do I validate that something is a boolean? terminé haciéndolo de este modo:

class Model < ActiveRecord::Base 

    validates :my_field, :inclusion => { :in => [true, false] } 

end 

he intentado probar esto unas cuantas maneras diferentes (usando rspec y shoulda comparadores) y desde mis pruebas en mantener fracasando, estoy ahora mismo al modo más tonto posible (?). Aún así, las pruebas no pasan y supongo que hay algún mecanismo que convierte el valor en alguna parte.

Aquí es lo que estoy usando para averiguar lo que está pasando:

# create instance without setting value ... 

# these work as expected 
model_instance.valid?.should be_false  # passes 
model_instance.my_field = true 
model_instance.valid?.should be_true  # passes 
model_instance.my_field = false  
model_instance.valid?.should be_true  # passes 

# works as expected 
model_instance.my_field = "" 
model_instance.valid?.should be_false  # passes 

# these should pass but fail 
model_instance.my_field = "foo" 
model_instance.my_field.should == "foo" # fails as well, my_field == false 
model_instance.valid?.should be_false  # fails 

model_instance.my_field = "false" 
model_instance.my_field.should == "false" # fails as well, my_field == false 
model_instance.valid?.should be_false  # fails 

model_instance.my_field = "123" 
model_instance.valid?.should be_false  # fails 

model_instance.my_field = "true" 
model_instance.my_field.should == "true" # fails as well, my_field == true 
model_instance.valid?.should be_false  # fails 

¿Qué me falta? Parece que el valor se convierte de una manera un tanto lógica, pero ¿dónde y cómo prevenirlo? ¿Cómo hacer este tipo de validación correctamente?

Respuesta

34

No sé de dónde vino esta idea de que necesita validar un campo booleano como verdadero/falso, pero lo he visto en algunos proyectos no relacionados recientemente, así que estoy empezando a preguntarme si esto no es cierto. No es un meme que comenzó en alguna parte.

Es un campo booleano, TIENE que ser verdadero o falso. AR se ocupa de los detalles de implementación de los valores booleanos por usted. Si desea un campo booleano, cree un campo booleano en la base de datos. Fin de la historia.

En cuanto a la fuente, aquí está el código que convierte un valor a un valor lógico:

# File activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb, line 147 
147:   def value_to_boolean(value) 
148:   if value.is_a?(String) && value.blank? 
149:    nil 
150:   else 
151:    TRUE_VALUES.include?(value) 
152:   end 
153:   end 

Y aquí son los valores que equivalen a sus homólogos booleanos:

TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].to_set 
FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE'].to_set 

Tan tomar otra mirada en su código anterior, todo está como debe ser, excepto que lo que está esperando es incorrecto. Al asignar "foo" a un valor booleano, vemos que "foo" no está en la lista de valores verdaderos aceptables, por lo que el campo booleano predeterminado es falso, que es lo que devolverá desde su acceso posterior. El valor booleano para el campo sigue siendo :in => [true, false] por lo que el modelo es válido. "falso" y "123" fallan por la misma razón. "verdadero" es un valor aceptable del valor booleano verdadero, por lo que el campo se establece en verdadero y sigue siendo válido por el motivo descrito anteriormente.

+1

Gracias, no sabía acerca de las constantes 'FALSE_VALUES' /' TRUE_VALUES'. ¡Y su respuesta completa lo explica muy bien! Aprendí algo hoy. – polarblau

+8

Creo que el meme es que cuando un campo booleano tiene validates_presence_of, falla la validación siempre que sea falso. La solución alternativa es usar validates_inclusion_in. Estoy viendo este comportamiento yo mismo en Rails 3.0.9. Vea aquí: http://www.ruby-forum.com/topic/99285 –

+0

Estaba leyendo de nuevo las guías de Rails sobre Validaciones, y me explicó cómo validar un campo booleano. Mi primer instinto fue "¿Por qué validar un booleano, ya que siempre será verdadero o falso y nunca nulo?". Tratando de encontrar razones, me encontré con esta publicación. Creo que el meme comenzó allí con los recién llegados, y se estancó un poco. "Si explican cómo, debería hacerse". Creo que fue explicado por completo ... solo mis 2 centavos valen ... –

26

No soy un gurú de Rails pero voy a decir que definitivamente quieres validar valores booleanos para :inclusion => { :in => [true, false] }, es decir, suponiendo que no quieres permitir valores nulos (NULL).

Viniendo de una base de datos de programación de bases de datos, aprendí a tener en cuenta que un campo booleano puede tener TRES valores: verdadero, falso y NULO. Al programar en SQL nativo, los NULL requieren un manejo especial (is null en lugar de = null) que causa mucho trabajo adicional.

En las pruebas con Rails 3.2 de hoy, no fue un problema crear un campo booleano no validado con un valor nulo y guardarlo en mi base de datos PostgreSQL, donde se almacenó diligentemente como NULL. Para evitar todos los problemas que causarían, estoy planeando sobre el uso de este enfoque para booleanos en Rails:

  • definir campos booleanos con :null => false en migraciones.
  • Utilice :inclusion => { :in => [true, false] } para validar el campo en el modelo. Esto da un buen mensaje si el campo no está inicializado (nulo).

No es intuitivo en un primer momento que no puedo utilizar una validación :presence, pero resulta :presence valida que el valor no está en blanco, y el valor es false en blanco. En otras palabras, si valida :presence => true y establece el valor en false, la validación fallará. Ver validates_presence_of en la API y el comentario de @Paul A Jungwirth sobre la respuesta de @Karmajunkie.

+1

Estoy completamente de acuerdo. Nunca quiero que un INSERT sea rechazado por la base de datos (mensaje muy desagradable, generalmente un error de 500) cuando podría ser capturado por una validación de Rails en su lugar, con un mensaje de error útil y fácil de usar. – sockmonk

+0

Lo siento, pero no puedo obtener la sintaxis para mi archivo model.rb. Probé variaciones de validates: property_name, inclusion (true, false) así como: inclusion => {in => [true, false]}. ¿Su respuesta es para usar en el archivo model.rb? – flobacca

+1

@flobacca, sí, va en un modelo, al igual que en el ejemplo en la parte superior de la pregunta original. Si no funciona para usted, es posible que deba crear su propia pregunta para que pueda mostrar ejemplos de códigos específicos. –

Cuestiones relacionadas