acts_as_tree не уничтожает детей модели - PullRequest
5 голосов
/ 05 октября 2008

У меня есть эта модель задачи:

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.children в handled_parent_id_change является виновником, потому что, когда я закомментирую блок self.children.each do |sub_task|, тест снова проходит.

Есть идеи?

Ответы [ 2 ]

4 голосов
/ 06 октября 2008

Я нашел ошибку. Линия

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

создает d1. Это вызывает before_save обратный вызов, который, в свою очередь, вызывает self.children. Как указал Орион, это кеширует детей 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)), хотя я не пробовал ни одного из них решения.

1 голос
/ 06 октября 2008

children - простая ассоциация has_many

Это означает, что при вызове .children он будет загружать их из базы данных (если она еще не существует). Затем он их кеширует.

Я собирался сказать, что ваш второй «тест» будет на самом деле смотреть на кэшированные значения, а не на реальную базу данных, но этого не должно происходить, поскольку вы просто используете Task.count вместо d1.children.count. Хмм

Вы смотрели журналы? Они покажут вам SQL, который выполняется. Вы можете увидеть ошибку mysql, которая скажет вам, что происходит

...