Search code examples
ruby-on-railsactiverecordrecursionacts-as-tree

My recursive function does not end. What am I doing wrong?


I have a model called folder that acts as a tree. Inside the model I have an instance method copy that copies folders from one place to another. When copying a folder, its sub-folders also have to be copied.

This is my code:

class Folder < ActiveRecord::Base
  acts_as_tree :order => 'name'
  before_save :check_for_parent

  def copy(target_folder)
    new_folder = self.clone
    new_folder.parent = target_folder
    new_folder.save!

    # Copy sub-folders recursively
    self.children.each do |folder|
      folder.copy(new_folder) unless folder == new_folder
    end
  end

  def check_for_parent
    raise 'Folders must have a parent.' if parent.nil? && name != 'Root folder'
  end
end

Now consider the following situation:

Root folder-+
            |
         Folder 1-+
                  |
               Folder 2-+
                        |
                      Folder 3

When I copy Folder 1 in the root folder it works fine. It also works when I copy Folder 1 into Folder 2, but when I copy Folder 1 into Folder 3 I end up with endless recursion. In code:

f1 = Folder.find_by_name('Folder 1')
f3 = Folder.find_by_name('Folder 3')
f1.copy(f3) # Never stops

This code leads to:

Root folder-+
            |
         Folder 1-+
                  |
               Folder 2-+
                        |
                      Folder 3-+
                               |
                            Folder 1-+
                                     |
                                  Folder 2-+
                                           |
                                         Folder 3-+
                                                  |
                                               Folder 1-+
                                                        |
                                                     Folder 2-+
                                                              |
                                                            Folder 3-+
                                                                     |
                                                                  Folder 1-+
                                                                           |
                                                                          Etc.

I am overlooking something trivial but I just can't figure it out. What am I doing wrong??


Solution

  • I had to keep track of which folder I originally copied. The code below works. Of course, if anyone sees room for improvement, please let me know.

    def copy(target_folder, originally_copied_folder = nil)
        new_folder = self.clone
        new_folder.parent = target_folder
        new_folder.save!
    
        originally_copied_folder = new_folder if originally_copied_folder.nil?
    
        # Copy sub-folders recursively
        self.children.each do |folder|
          folder.copy(new_folder, originally_copied_folder) unless folder == originally_copied_folder
        end
      end