To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.
root / lib / plugins / acts_as_tree / lib / active_record / acts / tree.rb @ 1298:4f746d8966dd
History | View | Annotate | Download (4.27 KB)
| 1 | 1115:433d4f72a19b | Chris | 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 | 1295:622f24f53b42 | Chris | scope :roots, where("#{configuration[:foreign_key]} IS NULL").order(configuration[:order]) |
| 50 | 1115:433d4f72a19b | Chris | |
| 51 | 1295:622f24f53b42 | Chris | send :include, ActiveRecord::Acts::Tree::InstanceMethods |
| 52 | 1115:433d4f72a19b | Chris | end
|
| 53 | end
|
||
| 54 | |||
| 55 | module InstanceMethods |
||
| 56 | # Returns list of ancestors, starting from parent until root.
|
||
| 57 | #
|
||
| 58 | # subchild1.ancestors # => [child1, root]
|
||
| 59 | def ancestors |
||
| 60 | node, nodes = self, []
|
||
| 61 | nodes << node = node.parent while node.parent
|
||
| 62 | nodes |
||
| 63 | end
|
||
| 64 | |||
| 65 | # Returns list of descendants.
|
||
| 66 | #
|
||
| 67 | # root.descendants # => [child1, subchild1, subchild2]
|
||
| 68 | def descendants(depth=nil) |
||
| 69 | depth ||= 0
|
||
| 70 | result = children.dup |
||
| 71 | unless depth == 1 |
||
| 72 | result += children.collect {|child| child.descendants(depth-1)}.flatten
|
||
| 73 | end
|
||
| 74 | result |
||
| 75 | end
|
||
| 76 | |||
| 77 | # Returns list of descendants and a reference to the current node.
|
||
| 78 | #
|
||
| 79 | # root.self_and_descendants # => [root, child1, subchild1, subchild2]
|
||
| 80 | def self_and_descendants(depth=nil) |
||
| 81 | [self] + descendants(depth)
|
||
| 82 | end
|
||
| 83 | |||
| 84 | # Returns the root node of the tree.
|
||
| 85 | def root |
||
| 86 | node = self
|
||
| 87 | node = node.parent while node.parent
|
||
| 88 | node |
||
| 89 | end
|
||
| 90 | |||
| 91 | # Returns all siblings of the current node.
|
||
| 92 | #
|
||
| 93 | # subchild1.siblings # => [subchild2]
|
||
| 94 | def siblings |
||
| 95 | self_and_siblings - [self]
|
||
| 96 | end
|
||
| 97 | |||
| 98 | # Returns all siblings and a reference to the current node.
|
||
| 99 | #
|
||
| 100 | # subchild1.self_and_siblings # => [subchild1, subchild2]
|
||
| 101 | def self_and_siblings |
||
| 102 | parent ? parent.children : self.class.roots
|
||
| 103 | end
|
||
| 104 | end
|
||
| 105 | end
|
||
| 106 | end
|
||
| 107 | end |