closure_tree icon indicating copy to clipboard operation
closure_tree copied to clipboard

Copy node of tree with descendants

Open BlackKOT opened this issue 12 years ago • 1 comments

It will be also good to add besides moving nodes around to other parents the ability to copy node of tree with descendants. I wrote a temporary solution, but to my mind it's a bit hardcoded.

Migration for creation the field old_node:

class AddOldNodeToTags < ActiveRecord::Migration
  def change
    add_column :tags, :old_node, :integer
  end
end

The model method:

def clone_descendants_to(target_node)
    model = self.class
    model.transaction do
      self.descendants.each do |node|
        parent_id = (node.parent_id == self.id) ? target_node.id : model.where(:old_node => node.parent_id).first.id
        node.dup.update_attributes(:old_node => node.id, :parent_id => parent_id)
      end
      model.update_all({:old_node => nil}, model.arel_table[:old_node].not_eq(nil))
    end
  end

Usage:

Tag.first.clone_descendants_to(Tag.last)

Probably there is a more simple solution for copying subtrees to another node, and I didn't see it?

BlackKOT avatar Aug 28 '13 15:08 BlackKOT

Unless I'm overlooking something simple, I believe utilizing the amoeba gem as the "guts" of your clone_descendants_to method will solve your problem without making any unneeded work for the developers of this gem.

After adding the amoeba gem to your gemfile, add the code snippet below in your Tag model (or whichever model you are using closure tree in):

  amoeba do
    enable
  end

second, make your clone_descendants_to method look like this:

def clone_descendants_to(target_node)
  # First, make sure someone didn't pass in a model
  # that is not the current closure_tree model, in this case, Tag
  if target_node and target_node.class.model_name.to_s == "Tag"
    self_copy = self.amoeba_dup
    self_copy.save
    target_node.children << self_copy
  else
    # handle programmer error passing wrong model how ever you see fit
  end

end

As you can see, those three lines of code within the if statement literally take care of everything. Not only that, but the amoeba gem lets you pass additional options during the creation of each child model, or 'node' in this case, which makes it super flexible. You can read more about the amoeba options on the amoeba github page.

Again, I have not fully tested this yet but am implementing it in my code as I type it :) ...just tested it, it looks like it worked fine. I hope my code helps you!

-Mike - mkralla11

mkralla11 avatar Dec 18 '13 16:12 mkralla11