To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.
root / .svn / pristine / 6c / 6c5422ed0dce695a568f6fb2acde79357b8cbed3.svn-base @ 1297:0a574315af3e
History | View | Annotate | Download (4.45 KB)
| 1 |
module ActiveRecord |
|---|---|
| 2 |
module Acts |
| 3 |
module Tree |
| 4 |
def self.included(base) |
| 5 |
base.extend(ClassMethods) |
| 6 |
end |
| 7 |
|
| 8 |
# Specify this +acts_as+ extension if you want to model a tree structure by providing a parent association and a children |
| 9 |
# association. This requires that you have a foreign key column, which by default is called +parent_id+. |
| 10 |
# |
| 11 |
# class Category < ActiveRecord::Base |
| 12 |
# acts_as_tree :order => "name" |
| 13 |
# end |
| 14 |
# |
| 15 |
# Example: |
| 16 |
# root |
| 17 |
# \_ child1 |
| 18 |
# \_ subchild1 |
| 19 |
# \_ subchild2 |
| 20 |
# |
| 21 |
# root = Category.create("name" => "root")
|
| 22 |
# child1 = root.children.create("name" => "child1")
|
| 23 |
# subchild1 = child1.children.create("name" => "subchild1")
|
| 24 |
# |
| 25 |
# root.parent # => nil |
| 26 |
# child1.parent # => root |
| 27 |
# root.children # => [child1] |
| 28 |
# root.children.first.children.first # => subchild1 |
| 29 |
# |
| 30 |
# In addition to the parent and children associations, the following instance methods are added to the class |
| 31 |
# after calling <tt>acts_as_tree</tt>: |
| 32 |
# * <tt>siblings</tt> - Returns all the children of the parent, excluding the current node (<tt>[subchild2]</tt> when called on <tt>subchild1</tt>) |
| 33 |
# * <tt>self_and_siblings</tt> - Returns all the children of the parent, including the current node (<tt>[subchild1, subchild2]</tt> when called on <tt>subchild1</tt>) |
| 34 |
# * <tt>ancestors</tt> - Returns all the ancestors of the current node (<tt>[child1, root]</tt> when called on <tt>subchild2</tt>) |
| 35 |
# * <tt>root</tt> - Returns the root of the current node (<tt>root</tt> when called on <tt>subchild2</tt>) |
| 36 |
module ClassMethods |
| 37 |
# Configuration options are: |
| 38 |
# |
| 39 |
# * <tt>foreign_key</tt> - specifies the column name to use for tracking of the tree (default: +parent_id+) |
| 40 |
# * <tt>order</tt> - makes it possible to sort the children according to this SQL snippet. |
| 41 |
# * <tt>counter_cache</tt> - keeps a count in a +children_count+ column if set to +true+ (default: +false+). |
| 42 |
def acts_as_tree(options = {})
|
| 43 |
configuration = { :foreign_key => "parent_id", :dependent => :destroy, :order => nil, :counter_cache => nil }
|
| 44 |
configuration.update(options) if options.is_a?(Hash) |
| 45 |
|
| 46 |
belongs_to :parent, :class_name => name, :foreign_key => configuration[:foreign_key], :counter_cache => configuration[:counter_cache] |
| 47 |
has_many :children, :class_name => name, :foreign_key => configuration[:foreign_key], :order => configuration[:order], :dependent => configuration[:dependent] |
| 48 |
|
| 49 |
class_eval <<-EOV |
| 50 |
include ActiveRecord::Acts::Tree::InstanceMethods |
| 51 |
|
| 52 |
def self.roots |
| 53 |
find(:all, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})
|
| 54 |
end |
| 55 |
|
| 56 |
def self.root |
| 57 |
find(:first, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})
|
| 58 |
end |
| 59 |
EOV |
| 60 |
end |
| 61 |
end |
| 62 |
|
| 63 |
module InstanceMethods |
| 64 |
# Returns list of ancestors, starting from parent until root. |
| 65 |
# |
| 66 |
# subchild1.ancestors # => [child1, root] |
| 67 |
def ancestors |
| 68 |
node, nodes = self, [] |
| 69 |
nodes << node = node.parent while node.parent |
| 70 |
nodes |
| 71 |
end |
| 72 |
|
| 73 |
# Returns list of descendants. |
| 74 |
# |
| 75 |
# root.descendants # => [child1, subchild1, subchild2] |
| 76 |
def descendants |
| 77 |
children + children.collect(&:children).flatten |
| 78 |
end |
| 79 |
|
| 80 |
# Returns list of descendants and a reference to the current node. |
| 81 |
# |
| 82 |
# root.self_and_descendants # => [root, child1, subchild1, subchild2] |
| 83 |
def self_and_descendants |
| 84 |
[self] + descendants |
| 85 |
end |
| 86 |
|
| 87 |
# Returns the root node of the tree. |
| 88 |
def root |
| 89 |
node = self |
| 90 |
node = node.parent while node.parent |
| 91 |
node |
| 92 |
end |
| 93 |
|
| 94 |
# Returns all siblings of the current node. |
| 95 |
# |
| 96 |
# subchild1.siblings # => [subchild2] |
| 97 |
def siblings |
| 98 |
self_and_siblings - [self] |
| 99 |
end |
| 100 |
|
| 101 |
# Returns all siblings and a reference to the current node. |
| 102 |
# |
| 103 |
# subchild1.self_and_siblings # => [subchild1, subchild2] |
| 104 |
def self_and_siblings |
| 105 |
parent ? parent.children : self.class.roots |
| 106 |
end |
| 107 |
end |
| 108 |
end |
| 109 |
end |
| 110 |
end |