2008-10-05 12 views
5

que tengo este modelo de tareas:acts_as_tree no destruir a los niños del modelo

class Task < ActiveRecord::Base 
    acts_as_tree :order => 'sort_order' 
end 

Y puedo tener este examen

class TaskTest < Test::Unit::TestCase 
    def setup 
    @root = create_root 
    end 

    def test_destroying_a_task_should_destroy_all_of_its_descendants 
    d1 = create_task(:parent_id => @root.id, :sort_order => 2) 
    d2 = create_task(:parent_id => d1.id, :sort_order => 3) 
    d3 = create_task(:parent_id => d2.id, :sort_order => 4) 
    d4 = create_task(:parent_id => d1.id, :sort_order => 5) 
    assert_equal 5, Task.count 

    d1.destroy 

    assert_equal @root, Task.find(:first) 
    assert_equal 1, Task.count 
    end 
end 

La prueba es satisfactoria: cuando destruyo d1, destruye todos los descendientes de d1. Por lo tanto, después de la destrucción solo queda la raíz.

Sin embargo, esta prueba ahora está fallando después de haber agregado una devolución de llamada before_save a la tarea. Este es el código añadí a la Tarea:

before_save :update_descendants_if_necessary 

def update_descendants_if_necessary 
    handle_parent_id_change if self.parent_id_changed? 
    return true 
end 

def handle_parent_id_change 
    self.children.each do |sub_task| 
    #the code within the loop is deliberately commented out 
    end 
end 

Cuando añadí este código, assert_equal 1, Task.count falla, con Task.count == 4. Creo que self.children bajo handled_parent_id_change es el culpable, porque cuando comento el bloque self.children.each do |sub_task|, la prueba pasa nuevamente.

¿Alguna idea?

Respuesta

4

Encontré el error. La línea

d1 = create_task(:parent_id => @root.id, :sort_order => 2) 

crea d1. Esto llama a la devolución de llamada before_save, que a su vez llama al self.children. Como señaló Orion, esto almacena en caché a los hijos de d1.

Sin embargo, en este punto, d1 aún no tiene hijos. Entonces el caché de hijos de d1 está vacío.

Por lo tanto, cuando intento destruir d1, el programa intenta destruir los elementos secundarios de d1. Encuentra la caché, descubre que está vacía y un resultado no destruye d2, d3 y d4.

He resuelto esto cambiando las creaciones de trabajo así:

@root.children << (d1 = new_task(:sort_order => 2)) 
@root.save! 

Esto funcionó, así que estoy bien con él :) Creo que también es posible solucionar este problema, ya sea d1 recarga (d1.reload) o self.children (self.children(true)) aunque no probé ninguna de estas soluciones.

1

childrenis a simple has_many association

Esto significa que, cuando se llama .children, se cargarlos desde la base de datos (si no está ya presente). Luego los almacenará en caché.

Iba a decir que su segunda "prueba" en realidad mirará los valores almacenados en caché no la base de datos real, pero eso no debería ocurrir ya que solo está usando Task.count en lugar de d1.children.count. Hrm

¿Has mirado los registros? Le mostrarán el SQL que se está ejecutando. Puede ver un error de mysql allí que le dirá lo que está pasando

Cuestiones relacionadas