README.md in closure_tree-3.1.0 vs README.md in closure_tree-3.2.0
- old
+ new
@@ -1,22 +1,24 @@
# Closure Tree [![Build Status](https://secure.travis-ci.org/mceachen/closure_tree.png?branch=master)](http://travis-ci.org/mceachen/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,
-as well as support for polymorphism within the hierarchy.
+[ancestry](https://github.com/stefankroes/ancestry),
+[acts_as_tree](https://github.com/amerine/acts_as_tree) and
+[awesome_nested_set](https://github.com/collectiveidea/awesome_nested_set/) gems, giving you:
+* Much better mutation performance thanks to the Closure Tree storage algorithm
+* Very efficient select performance (again, thanks to Closure Tree)
+* Efficient subtree selects
+* Support for polymorphism [STI](#sti) within the hierarchy
+* ```find_or_create_by_path``` for [building out hierarchies quickly and conveniently](#find_or_create_by_path)
+* Support for [deterministic ordering](#deterministic-ordering) of children
+* Excellent [test coverage](#testing) in a variety of environments
+
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.
-Closure tree is [tested under every combination](https://secure.travis-ci.org/mceachen/closure_tree.png?branch=master) of
-
-* Ruby 1.8.7 and Ruby 1.9.3
-* The latest Rails 3.0, 3.1, and 3.2 branches, and
-* Using MySQL, Postgresql, and SQLite.
-
## Installation
Note that closure_tree only supports Rails 3.0 and later, and has test coverage for MySQL, PostgreSQL, and SQLite.
1. Add this to your Gemfile: ```gem 'closure_tree'```
@@ -24,10 +26,11 @@
2. Run ```bundle install```
3. Add ```acts_as_tree``` to your hierarchical model(s) (see the <em>Available options</em> section below for details).
4. Add a migration to add a ```parent_id``` column to the model you want to act_as_tree.
+ You may want to also [add a column for deterministic ordering of children](#sort_order), but that's optional.
```ruby
class AddParentIdToTag < ActiveRecord::Migration
def change
add_column :tag, :parent_id, :integer
@@ -100,19 +103,19 @@
Then:
```ruby
puts grandparent.self_and_descendants.collect{ |t| t.name }.join(" > ")
-"grandparent > parent > child"
+=> "grandparent > parent > child"
child.ancestry_path
-["grandparent", "parent", "child"]
+=> ["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:
+We can do all the node creation and add_child calls with one method call:
```ruby
child = Tag.find_or_create_by_path(["grandparent", "parent", "child"])
```
@@ -128,22 +131,35 @@
```
This will pass the attribute hash of ```{:name => "home", :tag_type => "File"}``` to
```Tag.find_or_create_by_name``` if the root directory doesn't exist (and
```{:name => "chuck", :tag_type => "File"}``` if the second-level tag doesn't exist, and so on).
-### Available options
-<a id="options" />
+### Moving nodes around the tree
+Nodes can be moved around to other parents, and closure_tree moves the node's descendancy to the new parent for you:
+
+```ruby
+d = Tag.find_or_create_by_path %w(a b c d)
+h = Tag.find_or_create_by_path %w(e f g h)
+e = h.root
+d.add_child(e) # "d.children << e" would work too, of course
+h.ancestry_path
+=> ["a", "b", "c", "d", "e", "f", "g", "h"]
+```
+
+### <a id="options"></a>Available options
+
When you include ```acts_as_tree``` in your model, you can provide a hash to override the following defaults:
* ```:parent_column_name``` to override the column name of the parent foreign key in the model's table. This defaults to "parent_id".
* ```:hierarchy_table_name``` to override the hierarchy table name. This defaults to the singular name of the model + "_hierarchies".
* ```:dependent``` determines what happens when a node is destroyed. Defaults to ```nil```.
* ```:nullify``` will simply set the parent column to null. Each child node will be considered a "root" node. This is the default.
* ```:delete_all``` will delete all descendant nodes (which circumvents the destroy hooks)
* ```:destroy``` will destroy all descendant nodes (which runs the destroy hooks on each child node)
* ```:name_column``` used by #```find_or_create_by_path```, #```find_by_path```, and ```ancestry_path``` instance methods. This is primarily useful if the model only has one required field (like a "tag").
+* ```:order``` used to set up [deterministic ordering](#deterministic-ordering)
## Accessing Data
### Class methods
@@ -155,38 +171,121 @@
* ```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.leaves``` returns an array of all the nodes in self_and_descendants that are leaves.
-* ```tag.level``` returns the level, or "generation", for this node in the tree. A root node == 0.
+* ```tag.leaves``` is scoped to all leaf nodes in self_and_descendants.
+* ```tag.depth``` returns the depth, or "generation", for this node in the tree. A root node will have a value of 0.
* ```tag.parent``` returns the node's immediate parent. Root nodes will return nil.
-* ```tag.children``` returns an array of immediate children (just those nodes whose parent is the current node).
-* ```tag.ancestors``` returns an array of [ parent, grandparent, great grandparent, … ]. Note that the size of this array will always equal ```tag.level```.
-* ```tag.self_and_ancestors``` returns an array of self, parent, grandparent, great grandparent, etc.
-* ```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.
+* ```tag.children``` is a ```has_many``` of immediate children (just those nodes whose parent is the current node).
+* ```tag.ancestors``` is a ordered scope of [ parent, grandparent, great grandparent, … ]. Note that the size of this array will always equal ```tag.depth```.
+* ```tag.self_and_ancestors``` returns a scope containing self, parent, grandparent, great grandparent, etc.
+* ```tag.siblings``` returns a scope containing all nodes with the same parent as ```tag```, excluding self.
+* ```tag.self_and_siblings``` returns a scope containing all nodes with the same parent as ```tag```, including self.
+* ```tag.descendants``` returns a scope of all children, childrens' children, etc., excluding self ordered by depth.
+* ```tag.self_and_descendants``` returns a scope of all children, childrens' children, etc., including self, ordered by depth.
* ```tag.destroy``` will destroy a node and do <em>something</em> to its children, which is determined by the ```:dependent``` option passed to ```acts_as_tree```.
-## Polymorphic hierarchies
+## <a id="sti"></a>Polymorphic hierarchies with STI
-Polymorphic models are supported:
+Polymorphic models using single table inheritance (STI) are supported:
1. Create a db migration that adds a String ```type``` column to your model
-2. Subclass the model class. You only need to add acts_as_tree to your base class.
+2. Subclass the model class. You only need to add ```acts_as_tree``` to your base class:
```ruby
class Tag < ActiveRecord::Base
acts_as_tree
end
class WhenTag < Tag ; end
class WhereTag < Tag ; end
class WhatTag < Tag ; end
```
+## Deterministic ordering
+
+By default, children will be ordered by your database engine, which may not be what you want.
+
+If you want to order children alphabetically, and your model has a ```name``` column, you'd do this:
+
+```ruby
+class Tag < ActiveRecord::Base
+ acts_as_tree :order => 'name'
+end
+```
+
+If you want a specific order, add a new integer column to your model in a migration:
+
+```ruby
+t.integer :sort_order
+```
+
+and in your model:
+
+```ruby
+class OrderedTag < ActiveRecord::Base
+ acts_as_tree :order => 'sort_order'
+end
+```
+
+When you enable ```order```, you'll also have the following new methods injected into your model:
+
+* ```tag.siblings_before``` is a scope containing all nodes with the same parent as ```tag```,
+ whose sort order column is less than ```self```. These will be ordered properly, so the ```last```
+ element in scope will be the sibling immediately before ```self```
+* ```tag.siblings_after``` is a scope containing all nodes with the same parent as ```tag```,
+ whose sort order column is more than ```self```. These will be ordered properly, so the ```first```
+ element in scope will be the sibling immediately "after" ```self```
+
+If your ```order``` column is an integer attribute, you'll also have these:
+
+* ```tag.add_sibling_before(sibling_node)``` which will
+ 1. move ```tag``` to the same parent as ```sibling_node```,
+ 2. decrement the sort_order values of the nodes before the ```sibling_node``` by one, and
+ 3. set ```tag```'s order column to 1 less than the ```sibling_node```'s value.
+
+* ```tag.add_sibling_after(sibling_node)``` which will
+ 1. move ```tag``` to the same parent as ```sibling_node```,
+ 2. increment the sort_order values of the nodes after the ```sibling_node``` by one, and
+ 3. set ```tag```'s order column to 1 more than the ```sibling_node```'s value.
+
+```ruby
+root = OrderedTag.create(:name => "root")
+a = OrderedTag.create(:name => "a", :parent => "root")
+b = OrderedTag.create(:name => "b")
+c = OrderedTag.create(:name => "c")
+
+a.append_sibling(b)
+root.children.collect(&:name)
+=> ["a", "b"]
+
+a.prepend_sibling(b)
+root.children.collect(&:name)
+=> ["b", "a"]
+
+a.append_sibling(c)
+root.children.collect(&:name)
+=> ["a", "c", "b"]
+
+b.append_sibling(c)
+root.children.collect(&:name)
+=> ["a", "b", "c"]
+```
+
+## Testing
+
+Closure tree is [tested under every combination](https://secure.travis-ci.org/mceachen/closure_tree.png?branch=master) of
+
+* Ruby 1.8.7 and Ruby 1.9.3
+* The latest Rails 3.0, 3.1, and 3.2 branches, and
+* MySQL, PostgreSQL, and SQLite.
+
+
## Change log
+
+### 3.2.0
+
+* Added support for deterministic ordering of nodes.
### 3.1.0
* Switched to using ```has_many :though``` rather than ```has_and_belongs_to_many```