To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / .svn / pristine / 34 / 345b699f46121e103fdce4587bf3ec87db1cc445.svn-base @ 1298:4f746d8966dd

History | View | Annotate | Download (4.27 KB)

1 1295:622f24f53b42 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
          scope :roots, where("#{configuration[:foreign_key]} IS NULL").order(configuration[:order])
50
51
          send :include, ActiveRecord::Acts::Tree::InstanceMethods
52
        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