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 / 28 / 28460f7863cf0e308a71ddfcfde8f112f06716f0.svn-base @ 1298:4f746d8966dd

History | View | Annotate | Download (4.61 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(depth=nil)
77
          depth ||= 0
78
          result = children.dup
79
          unless depth == 1
80
            result += children.collect {|child| child.descendants(depth-1)}.flatten
81
          end
82
          result
83
        end
84

    
85
        # Returns list of descendants and a reference to the current node.
86
        #
87
        #   root.self_and_descendants # => [root, child1, subchild1, subchild2]
88
        def self_and_descendants(depth=nil)
89
          [self] + descendants(depth)
90
        end
91

    
92
        # Returns the root node of the tree.
93
        def root
94
          node = self
95
          node = node.parent while node.parent
96
          node
97
        end
98

    
99
        # Returns all siblings of the current node.
100
        #
101
        #   subchild1.siblings # => [subchild2]
102
        def siblings
103
          self_and_siblings - [self]
104
        end
105

    
106
        # Returns all siblings and a reference to the current node.
107
        #
108
        #   subchild1.self_and_siblings # => [subchild1, subchild2]
109
        def self_and_siblings
110
          parent ? parent.children : self.class.roots
111
        end
112
      end
113
    end
114
  end
115
end