2008-10-05 6 views
5

du modèle que j'ai ce modèle de tâche:acts_as_tree ne pas détruire les enfants

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

Et je ce test

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 

Le test est réussi: quand je détruirai d1, il détruit tous les descendants de d1. Ainsi, après la destruction, seule la racine est laissée.

Toutefois, ce test échoue maintenant après avoir ajouté un rappel before_save à la tâche. Voici le code que j'ajouté à la tâche:

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 

Quand j'ai ajouté ce code, assert_equal 1, Task.count échoue, avec Task.count == 4. Je pense que self.children sous handled_parent_id_change est le coupable, parce que quand je commente le bloc self.children.each do |sub_task|, le test passe à nouveau.

Des idées?

Répondre

4

J'ai trouvé le bogue. La ligne

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

crée d1. Cela appelle le rappel before_save, qui à son tour appelle self.children. Comme Orion l'a souligné, cela met en cache les enfants de d1.

Cependant, à ce stade, d1 n'a encore aucun enfant. Le cache des enfants de d1 est donc vide. Ainsi, lorsque j'essaye de détruire d1, le programme essaie de détruire les enfants de d1. Il rencontre le cache, trouve qu'il est vide et un résultat ne détruit pas d2, d3 et d4.

Je résolu ce problème en changeant les créations de tâches comme ceci:

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

Cela a fonctionné, je suis ok avec elle :) Je pense qu'il est également possible de corriger ce soit par d1 rechargeant (d1.reload) ou self.children (self.children(true)) bien que je n'ai pas essayé aucune de ces solutions.

1

childrenis a simple has_many association

Cela signifie, lorsque vous appelez .children, il les charger à partir de la base de données (si pas déjà). Il les mettra ensuite en cache. J'allais dire que votre deuxième 'test' regarderait réellement les valeurs mises en cache pas la vraie base de données, mais cela ne devrait pas arriver car vous utilisez simplement Task.count plutôt que d1.children.count. Hrm

Avez-vous regardé les journaux? Ils vous montreront le SQL en cours d'exécution. Vous pouvez voir une erreur mysql là-dedans qui vous dira ce qui se passe

Questions connexes