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 / 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