diff --git a/README.rdoc b/README.rdoc index ac3cf8f3..ec65884d 100644 --- a/README.rdoc +++ b/README.rdoc @@ -221,6 +221,26 @@ For example, from IRB: Additionally, if you think something is wrong with your depth cache: >> TreeNode.rebuild_depth_cache! + += Copying + +Ancestry provides a method to clone a tree, optionally replacing attributes and/or preserving a reference to the parent. This allows the creation of structures that use a 'default' tree and make copies of it. Example + + class Category < ActiveRecord::Base + belongs_to :project + belongs_to :source_category, :class_name => "Category" + scope :defaults, where(:project_id => nil) + + def self.import_from_defaults_for(project) + defaults.roots.all.each do |root| + root.clone_with_modifications!({:project_id => project.id}, nil, :source_category_id) + end + end + + class Project < ActiveRecord::Base + has_many :categories, :dependent => :destroy + after_create Proc.new { |p| Category.import_from_defaults_for(self) } + end = Tests diff --git a/lib/ancestry/instance_methods.rb b/lib/ancestry/instance_methods.rb index 99ba623e..b6d7229a 100644 --- a/lib/ancestry/instance_methods.rb +++ b/lib/ancestry/instance_methods.rb @@ -55,6 +55,19 @@ def apply_orphan_strategy end end end + + # Clone an object and all children + # => replacing values with those from attributes if present + # => setting parent to new parent if present + # => setting the "original_id_field_name" if present to the id of the original object + def clone_with_modifications!(attributes = nil, parent = nil, original_id_field_name = nil) + clone = self.class.create!(self.attributes.merge(:ancestry => nil).merge(attributes)) + clone.send("#{original_id_field_name}=", self.id) if original_id_field_name + clone.parent = parent + self.children.each { |child| child.clone_with_modifications!(attributes, clone, original_id_field_name) } + clone.save! + clone + end # The ancestry value for this record's children def child_ancestry diff --git a/test/has_ancestry_test.rb b/test/has_ancestry_test.rb index dca1d90c..d143bb1d 100644 --- a/test/has_ancestry_test.rb +++ b/test/has_ancestry_test.rb @@ -701,4 +701,22 @@ def test_sort_by_ancestry assert_equal [n1, n2, n4, n3, n5].map(&:id), arranged.map(&:id) end end + + def test_clone_with_modifications! + AncestryTestDatabase.with_model :extra_columns => {:source_id => :integer, :project_id => :integer} do |model| + n1 = model.create!(:project_id => nil) + n2 = model.create!(:parent => n1, :project_id => nil) + n3 = model.create!(:parent => n2, :project_id => nil) + n4 = model.create!(:parent => n2, :project_id => nil) + n5 = model.create!(:parent => n1, :project_id => nil) + + n1c = n1.clone_with_modifications!({:project_id => 1}, nil, :source_id) + + assert_equal n1.id, n1c.source_id + assert_equal 1, n1c.project_id + assert_equal n1.descendants.count, n1c.descendants.count + assert_equal 1, n1c.descendants.last.project_id + assert_equal n1.descendants.last.id, n1c.descendants.last.source_id + end + end end