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