5

我有这个任务模型:

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

我有这个测试

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

测试成功:当我销毁d1时,它会销毁d1的所有后代。因此,在销毁之后只剩下根。

但是,在我向任务添加了 before_save 回调之后,这个测试现在失败了。这是我添加到任务的代码:

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

当我添加此代码时,assert_equal 1, Task.count失败,带有Task.count == 4. 我认为self.childrenunderhandled_parent_id_change是罪魁祸首,因为当我注释掉该self.children.each do |sub_task|块时,测试再次通过。

有任何想法吗?

4

2 回答 2

4

我发现了这个错误。线

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

创建 d1。这会调用before_save回调,而回调又会调用self.children. 正如 Orion 指出的那样,这缓存了 d1 的孩子。

但是,此时 d1 还没有任何子节点。所以 d1 的孩子缓存是空的。

因此,当我尝试销毁 d1 时,程序会尝试销毁 d1 的孩子。它遇到缓存,发现它是空的,结果没有破坏 d2、d3 和 d4。

我通过改变这样的任务创建解决了这个问题:

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

这行得通,所以我可以接受:) 我认为也可以通过重新加载 d1 ( d1.reload) 或 self.children ( self.children(true)) 来解决这个问题,尽管我没有尝试任何这些解决方案。

于 2008-10-06T04:02:30.317 回答
1

children 是一个简单的 has_many 关联

这意味着,当您调用 时.children,它将从数据库中加载它们(如果尚未存在)。然后它将缓存它们。

我要说的是,您的第二个“测试”实际上将查看缓存值而不是真正的数据库,但这不应该发生,因为您只是使用Task.count而不是d1.children.count. 人力资源管理

你看过日志吗?他们将向您展示正在执行的 SQL。你可能会在那里看到一个 mysql 错误,它会告诉你发生了什么

于 2008-10-05T20:07:08.123 回答