##// END OF EJS Templates
awesome_nested_set: not use cache for max rgt (#6579)...
Toshi MARUYAMA -
r12873:cdf9cae4a2cf
parent child
Show More
@@ -1,212 +1,204
1 1 require 'awesome_nested_set/model/prunable'
2 2 require 'awesome_nested_set/model/movable'
3 3 require 'awesome_nested_set/model/transactable'
4 4 require 'awesome_nested_set/model/relatable'
5 5 require 'awesome_nested_set/model/rebuildable'
6 6 require 'awesome_nested_set/model/validatable'
7 7 require 'awesome_nested_set/iterator'
8 8
9 9 module CollectiveIdea #:nodoc:
10 10 module Acts #:nodoc:
11 11 module NestedSet #:nodoc:
12 12
13 13 module Model
14 14 extend ActiveSupport::Concern
15 15
16 16 included do
17 17 delegate :quoted_table_name, :arel_table, :to => self
18 18 extend Validatable
19 19 extend Rebuildable
20 20 include Movable
21 21 include Prunable
22 22 include Relatable
23 23 include Transactable
24 24 end
25 25
26 26 module ClassMethods
27 27 def associate_parents(objects)
28 28 return objects unless objects.all? {|o| o.respond_to?(:association)}
29 29
30 30 id_indexed = objects.index_by(&:id)
31 31 objects.each do |object|
32 32 association = object.association(:parent)
33 33 parent = id_indexed[object.parent_id]
34 34
35 35 if !association.loaded? && parent
36 36 association.target = parent
37 37 association.set_inverse_instance(parent)
38 38 end
39 39 end
40 40 end
41 41
42 42 def children_of(parent_id)
43 43 where arel_table[parent_column_name].eq(parent_id)
44 44 end
45 45
46 46 # Iterates over tree elements and determines the current level in the tree.
47 47 # Only accepts default ordering, odering by an other column than lft
48 48 # does not work. This method is much more efficent than calling level
49 49 # because it doesn't require any additional database queries.
50 50 #
51 51 # Example:
52 52 # Category.each_with_level(Category.root.self_and_descendants) do |o, level|
53 53 #
54 54 def each_with_level(objects, &block)
55 55 Iterator.new(objects).each_with_level(&block)
56 56 end
57 57
58 58 def leaves
59 59 nested_set_scope.where "#{quoted_right_column_full_name} - #{quoted_left_column_full_name} = 1"
60 60 end
61 61
62 62 def left_of(node)
63 63 where arel_table[left_column_name].lt(node)
64 64 end
65 65
66 66 def left_of_right_side(node)
67 67 where arel_table[right_column_name].lteq(node)
68 68 end
69 69
70 70 def right_of(node)
71 71 where arel_table[left_column_name].gteq(node)
72 72 end
73 73
74 74 def nested_set_scope(options = {})
75 75 options = {:order => quoted_order_column_full_name}.merge(options)
76 76
77 77 order(options.delete(:order)).scoped options
78 78 end
79 79
80 80 def primary_key_scope(id)
81 81 where arel_table[primary_key].eq(id)
82 82 end
83 83
84 84 def root
85 85 roots.first
86 86 end
87 87
88 88 def roots
89 89 nested_set_scope.children_of nil
90 90 end
91 91 end # end class methods
92 92
93 93 # Any instance method that returns a collection makes use of Rails 2.1's named_scope (which is bundled for Rails 2.0), so it can be treated as a finder.
94 94 #
95 95 # category.self_and_descendants.count
96 96 # category.ancestors.find(:all, :conditions => "name like '%foo%'")
97 97 # Value of the parent column
98 98 def parent_id(target = self)
99 99 target[parent_column_name]
100 100 end
101 101
102 102 # Value of the left column
103 103 def left(target = self)
104 104 target[left_column_name]
105 105 end
106 106
107 107 # Value of the right column
108 108 def right(target = self)
109 109 target[right_column_name]
110 110 end
111 111
112 112 # Returns true if this is a root node.
113 113 def root?
114 114 parent_id.nil?
115 115 end
116 116
117 117 # Returns true is this is a child node
118 118 def child?
119 119 !root?
120 120 end
121 121
122 122 # Returns true if this is the end of a branch.
123 123 def leaf?
124 124 persisted? && right.to_i - left.to_i == 1
125 125 end
126 126
127 127 # All nested set queries should use this nested_set_scope, which
128 128 # performs finds on the base ActiveRecord class, using the :scope
129 129 # declared in the acts_as_nested_set declaration.
130 130 def nested_set_scope(options = {})
131 131 if (scopes = Array(acts_as_nested_set_options[:scope])).any?
132 132 options[:conditions] = scopes.inject({}) do |conditions,attr|
133 133 conditions.merge attr => self[attr]
134 134 end
135 135 end
136 136
137 137 self.class.nested_set_scope options
138 138 end
139 139
140 140 def to_text
141 141 self_and_descendants.map do |node|
142 142 "#{'*'*(node.level+1)} #{node.id} #{node.to_s} (#{node.parent_id}, #{node.left}, #{node.right})"
143 143 end.join("\n")
144 144 end
145 145
146 146 protected
147 147
148 148 def without_self(scope)
149 149 return scope if new_record?
150 150 scope.where(["#{self.class.quoted_table_name}.#{self.class.primary_key} != ?", self])
151 151 end
152 152
153 153 def store_new_parent
154 154 @move_to_new_parent_id = send("#{parent_column_name}_changed?") ? parent_id : false
155 155 true # force callback to return true
156 156 end
157 157
158 158 def has_depth_column?
159 159 nested_set_scope.column_names.map(&:to_s).include?(depth_column_name.to_s)
160 160 end
161 161
162 def right_most_node
163 @right_most_node ||= self.class.base_class.unscoped.nested_set_scope(
164 :order => "#{quoted_right_column_full_name} desc"
165 ).first
166 end
167
168 162 def right_most_bound
169 @right_most_bound ||= begin
170 return 0 if right_most_node.nil?
171
172 right_most_node.lock!
173 right_most_node[right_column_name] || 0
174 end
163 right_most_node =
164 self.class.base_class.unscoped.
165 order("#{quoted_right_column_full_name} desc").limit(1).lock(true).first
166 right_most_node ? (right_most_node[right_column_name] || 0) : 0
175 167 end
176 168
177 169 def set_depth!
178 170 return unless has_depth_column?
179 171
180 172 in_tenacious_transaction do
181 173 reload
182 174 nested_set_scope.primary_key_scope(id).
183 175 update_all(["#{quoted_depth_column_name} = ?", level])
184 176 end
185 177 self[depth_column_name] = self.level
186 178 end
187 179
188 180 def set_default_left_and_right
189 181 # adds the new node to the right of all existing nodes
190 182 self[left_column_name] = right_most_bound + 1
191 183 self[right_column_name] = right_most_bound + 2
192 184 end
193 185
194 186 # reload left, right, and parent
195 187 def reload_nested_set
196 188 reload(
197 189 :select => "#{quoted_left_column_full_name}, #{quoted_right_column_full_name}, #{quoted_parent_column_full_name}",
198 190 :lock => true
199 191 )
200 192 end
201 193
202 194 def reload_target(target)
203 195 if target.is_a? self.class.base_class
204 196 target.reload
205 197 else
206 198 nested_set_scope.find(target)
207 199 end
208 200 end
209 201 end
210 202 end
211 203 end
212 204 end
General Comments 0
You need to be logged in to leave comments. Login now