2012-07-04 17 views
17

Todavía soy bastante nuevo para probar en Rails 3, y uso RSpec y Notable. Leí un montón de publicaciones y algunos libros, pero todavía estoy atascado en la incertidumbre sobre cuándo usar el nombre de la asociación, cuando es ID.La forma perfecta de validar y probar las asociaciones de Rails 3 (utilizando RSpec/Notable)?

class Project < ActiveRecord::Base 
    has_many :tasks 
end 

class Task < ActiveRecord::Base 
    belongs_to :project 
end 

Debido a las buenas prácticas, quiero proteger a mis atributos de asignaciones de masas:

class Task < ActiveRecord::Base 
    attr_accessible :project # Or is it :project_id?? 

    belongs_to :project 
end 

En primer lugar, quiero para asegurarse de que un proyecto Nunca existe sin una tarea válida:

class Task < ActiveRecord::Base 
    validates :project, :presence => true  # Which one is the... 
    validates :project_id, :presence => true # ...right way to go?? 
end 

también quiere asegurarse de que el proyecto asignado o ID del proyecto es siempre válida:

class Task < ActiveRecord::Base 
    validates :project, :associated => true  # Again, which one is... 
    validates :project_id, :associated => true # ...the right way to go? 
end 

... y necesito la validación de: presencia cuando uso: asociado ??

Muchas gracias por la aclaración, parece que después de horas de lectura y tratando de probar cosas usando RSpec/Shoulda/Notable No veo el bosque, porque de todos los árboles nunca más ...

+0

Buena pregunta clara. Para confirmar, ¿no quiere decir que quiere "asegurarse de que una tarea ** nunca ** exista sin un proyecto válido (padre)"? –

Respuesta

12

Esta parece ser la forma correcta de hacerlo:

attr_accessible :project_id 

Usted no tiene que poner :project allí, también! Es de todos modos posible hacer task.project=(Project.first!)

A continuación, comprobar la existencia de la :project_id utilizando la siguiente (:project_id también se establece cuando se utiliza task.project=(...)):

validates :project_id, :presence => true 

Ahora asegúrese de que un proyecto asociado es válido como esto :

validates :project, :associated => true 

Así:

t = Task.new 
t.project_id = 1 # Value is accepted, regardless whether there is a Project with ID 1 
t.project = Project.first # Any existing valid project is accepted 
t.project = Project.new(:name => 'valid value') # A new valid project is accepted 
t.project = Project.new(:name => 'invalid value') # A new invalid (or an existing invalid) project is NOT accepted! 

Es una pena que al asignar una ID a través del t.project_id = no se verifique si realmente existe esta identificación específica. Debe verificar esto usando una validación personalizada o usando el Validates Existence GEM.

Para probar estas asociaciones usando RSpec con igualadores de Notables, hacer algo como:

describe Task do 
    it { should validate_presence_of :project_id } 
    it { should validate_associated :project } 
end 
+2

Wow esto es una revelación. Y estoy obteniendo resultados inesperados con Rails 3.2.3. Prueba esto. En el modelo de tarea, agregue 'validates: project,: presence => true' pero no': associated' validation. En la consola, crea una nueva tarea con project_id = 999 y comprueba si es '.valid?'. Rails busca el registro en la tabla de proyectos, luego (presumiblemente porque no se encuentra y project_id sigue siendo nulo), la validación falla. Ahora cambie la validación a 'validates: project_id,: presence => true'. Rails NO lee proyectos y la validación tiene éxito. Agrega ': associated' y la validación * still * tiene éxito. –

4
validates :project, :associated => true 
validates :project_id, :presence => true 

Si desea estar seguro de que una asociación está presente, necesitará probar si la clave externa utilizada para asignar la asociación está presente, y no el objeto asociado en sí. http://guides.rubyonrails.org/active_record_validations_callbacks.html

attr_accessible :project_id 
+3

Esa es una cita directa de la documentación, pero en mis pruebas con Rails 3.2.3, funciona exactamente al revés. Si valida la presencia en la asociación (': project,: presence => true'), falla la validación si el project_id no existe en la tabla de proyectos. Pero si valida la presencia en el id (': project_id,: presence => true'),' .valid? 'Devuelve' true' sin importar el valor que asigne a project_id. Agregar 'validates: project,: associated => true' no ayuda, aún acepta project_ids inexistentes. ¡Bastante confundido acerca de cómo se supone que uno valida las asociaciones! –

3

EDIT: suponiendo que la asociación no es opcional ...

La única manera de que pueda conseguirlo para validar a fondo es la siguiente:

validates_associated :project 
validates_presence_of :project_id, 
    :unless => Proc.new {|o| o.project.try(:new_record?)} 
validates_presence_of :project, :if => Proc.new {|o| o.project_id} 

La primera línea valida si el proyecto asociado es válido, si es que existe. La segunda línea insiste en que el project_id está presente, a menos que el Proyecto asociado exista y sea nuevo (si es un registro nuevo, aún no tendrá una ID). La tercera línea garantiza que el proyecto esté presente si hay una identificación presente, es decir, si el proyecto asociado ya se ha guardado.

ActiveRecord asignará un project_id a la tarea si asigna un Proyecto guardado a project. Si asigna un proyecto nuevo/no guardado a project, dejará en blanco project_id. Por lo tanto, queremos asegurarnos de que project_id esté presente, pero solo cuando se trata de un Proyecto guardado; esto se logra en la línea dos anterior.

Por el contrario, si asigna un número al project_id que representa un Proyecto real, ActiveRecord completará project con el objeto Proyecto correspondiente. Pero si la identificación que asignó es falsa, dejará project como nulo. Por lo tanto, la línea tres arriba, que asegura que tenemos un project si se completa project_id; si proporciona una identificación falsa, esto fallará.

ejemplos

Ver RSpec que ponen a prueba estas validaciones: https://gist.github.com/kianw/5085085

2

funciona la solución de Joshua Muheim, pero no me gusta ser no ser capaz de enlazar simplemente un proyecto para una tarea con un id de esta manera:

t = Task.new 
t.project_id = 123 # Won't verify if it's valid or not. 

Así que se me ocurrió esto en su lugar:

class Task < ActiveRecord:Base 

    belongs_to :project 

    validates :project_id, :presence => true 
    validate :project_exists 

    private 
    def project_exists 
     # Validation will pass if the project exists 
     valid = Project.exists?(self.project_id) 
     self.errors.add(:project, "doesn't exist.") unless valid 
    end 

end 
Cuestiones relacionadas