= Closure Tree Closure Tree is a mostly-API-compatible replacement for the acts_as_tree and awesome_nested_set gems, but with much better mutation performance thanks to the Closure Tree storage algorithm. See {Bill Karwin}[http://karwin.blogspot.com/]'s excellent {Models for hierarchical data presentation}[http://www.slideshare.net/billkarwin/models-for-hierarchical-data] for a description of different tree storage algorithms. == Setup Note that closure_tree is being developed for Rails 3.1.0.rc1 1. Add this to your Gemfile: gem 'closure_tree' 2. Run bundle install 3. Add acts_as_tree to your hierarchical model(s). 4. Add a migration to add a parent_id column to the model you want to act_as_tree. Note that if the column is null, the tag will be considered a root node. class AddParentIdToTag < ActiveRecord::Migration def change add_column :tag, :parent_id, :integer end end 5. Add a database migration to store the hierarchy for your model. By convention the table name will be the model's table name, followed by "_hierarchy". Note that by calling acts_as_tree, a "virtual model" (in this case, TagsHierarchy) will be added automatically, so you don't need to create it. class CreateTagHierarchy < ActiveRecord::Migration def change create_table :tags_hierarchy, :id => false do |t| t.integer :ancestor_id, :null => false # ID of the parent/grandparent/great-grandparent/... tag t.integer :descendant_id, :null => false # ID of the target tag t.integer :generations, :null => false # Number of generations between the ancestor and the descendant. Parent/child = 1, for example. end # For "all progeny of..." selects: add_index :tags_hierarchy, [:ancestor_id, :descendant_id], :unique => true # For "all ancestors of..." selects add_index :tags_hierarchy, [:descendant_id] end end 6. Run rake db:migrate 7. If you're migrating away from another system where your model already has a parent_id column, run Tag.rebuild! and the ..._hierarchy table will be truncated and rebuilt. If you're starting from scratch you don't need to call rebuild!. == Usage === Creation Create a root node: grandparent = Tag.create!(:name => 'Grandparent') There are two equivalent ways to add children. Either use the add_child method: parent = Tag.create!(:name => 'Parent') grandparent.add_child parent Or append to the children collection: child = Tag.create!(:name => 'Child') parent.children << child Then: puts grandparent.self_and_descendants.collect{ |t| t.name }.join(" > ") "grandparent > parent > child" child.ancestry_path ["grandparent", "parent", "child"] === find_or_create_by_path We can do all the node creation and add_child calls from the prior section with one method call: child = Tag.find_or_create_by_path "grandparent", "parent", "child" You can find as well as find_or_create by "ancestry paths". Ancestry paths may be built using any column in your model. The default column is name, which can be changed with the :name_column option provided to acts_as_tree. Note that the other columns will be null if nodes are created, other than auto-generated columns like ID and created_at timestamp. Only the specified column will receive the path element value. == Accessing Data === Class methods [Tag.root] returns an arbitrary root node [Tag.roots] returns all root nodes [Tag.leaves] returns all leaf nodes === Instance methods [tag.root] returns the root for this node [tag.root?] returns true if this is a root node [tag.child?] returns true if this is a child node. It has a parent. [tag.leaf?] returns true if this is a leaf node. It has no children. [tag.level] returns the level, or "generation", for this node in the tree. A root node = 0 [tag.parent] returns the node's immediate parent [tag.children] returns an array of immediate children (just those in the next level). [tag.ancestors] returns an array of all parents, parents' parents, etc, excluding self. [tag.self_and_ancestors] returns an array of all parents, parents' parents, etc, including self. [tag.siblings] returns an array of brothers and sisters (all at that level), excluding self. [tag.self_and_siblings] returns an array of brothers and sisters (all at that level), including self. [tag.descendants] returns an array of all children, childrens' children, etc., excluding self. [tag.self_and_descendants] returns an array of all children, childrens' children, etc., including self. == Thanks to * https://github.com/collectiveidea/awesome_nested_set * https://github.com/patshaughnessy/class_factory