@@ -66,162 +66,160 @@ def acts_as_tree(options = {})
66
66
67
67
module Model
68
68
extend ActiveSupport ::Concern
69
- module InstanceMethods
70
69
71
- # Returns true if this node has no parents.
72
- def root?
73
- parent . nil?
74
- end
70
+ # Returns true if this node has no parents.
71
+ def root?
72
+ parent . nil?
73
+ end
75
74
76
- # Returns true if this node has a parent, and is not a root.
77
- def child?
78
- !parent . nil?
79
- end
75
+ # Returns true if this node has a parent, and is not a root.
76
+ def child?
77
+ !parent . nil?
78
+ end
80
79
81
- # Returns true if this node has no children.
82
- def leaf?
83
- children . empty?
84
- end
80
+ # Returns true if this node has no children.
81
+ def leaf?
82
+ children . empty?
83
+ end
85
84
86
- # Returns the farthest ancestor, or self if +root?+
87
- def root
88
- root? ? self : ancestors . last
89
- end
85
+ # Returns the farthest ancestor, or self if +root?+
86
+ def root
87
+ root? ? self : ancestors . last
88
+ end
90
89
91
- def leaves
92
- return [ self ] if leaf?
93
- self . class . leaves . where ( <<-SQL
90
+ def leaves
91
+ return [ self ] if leaf?
92
+ self . class . leaves . where ( <<-SQL
94
93
#{ quoted_table_name } .#{ self . class . primary_key } IN (
95
- SELECT descendant_id
96
- FROM #{ quoted_hierarchy_table_name }
97
- WHERE ancestor_id = #{ id } )
98
- SQL
99
- )
100
- end
101
-
102
- def level
103
- ancestors . size
104
- end
94
+ SELECT descendant_id
95
+ FROM #{ quoted_hierarchy_table_name }
96
+ WHERE ancestor_id = #{ id } )
97
+ SQL
98
+ )
99
+ end
105
100
106
- def ancestors
107
- without_self ( self_and_ancestors )
108
- end
101
+ def level
102
+ ancestors . size
103
+ end
109
104
110
- # Returns an array, root first, of self_and_ancestors' values of the +to_s_column+, which defaults
111
- # to the +name_column+.
112
- # (so child.ancestry_path == +%w{grandparent parent child}+
113
- def ancestry_path ( to_s_column = name_column )
114
- self_and_ancestors . reverse . collect { |n | n . send to_s_column . to_sym }
115
- end
105
+ def ancestors
106
+ without_self ( self_and_ancestors )
107
+ end
116
108
117
- def descendants
118
- without_self ( self_and_descendants )
119
- end
109
+ # Returns an array, root first, of self_and_ancestors' values of the +to_s_column+, which defaults
110
+ # to the +name_column+.
111
+ # (so child.ancestry_path == +%w{grandparent parent child}+
112
+ def ancestry_path ( to_s_column = name_column )
113
+ self_and_ancestors . reverse . collect { |n | n . send to_s_column . to_sym }
114
+ end
120
115
121
- def self_and_siblings
122
- self . class . scoped . where ( :parent => parent )
123
- end
116
+ def descendants
117
+ without_self ( self_and_descendants )
118
+ end
124
119
125
- def siblings
126
- without_self ( self_and_siblings )
127
- end
120
+ def self_and_siblings
121
+ self . class . scoped . where ( :parent => parent )
122
+ end
128
123
129
- # Alias for appending to the children collection.
130
- # You can also add directly to the children collection, if you'd prefer.
131
- def add_child ( child_node )
132
- children << child_node
133
- child_node
134
- end
124
+ def siblings
125
+ without_self ( self_and_siblings )
126
+ end
135
127
136
- # Find a child node whose +ancestry_path+ minus self.ancestry_path is +path+.
137
- def find_by_path ( path )
138
- path = [ path ] unless path . is_a? Enumerable
139
- node = self
140
- while ( !path . empty? && node )
141
- node = node . children . send ( "find_by_#{ name_column } " , path . shift )
142
- end
143
- node
144
- end
128
+ # Alias for appending to the children collection.
129
+ # You can also add directly to the children collection, if you'd prefer.
130
+ def add_child ( child_node )
131
+ children << child_node
132
+ child_node
133
+ end
145
134
146
- # Find a child node whose +ancestry_path+ minus self.ancestry_path is +path+
147
- def find_or_create_by_path ( path , attributes = { } )
148
- path = [ path ] unless path . is_a? Enumerable
149
- node = self
150
- attrs = { }
151
- attrs [ :type ] = self . type if ct_subclass? && ct_has_type?
152
- path . each do |name |
153
- attrs [ name_sym ] = name
154
- child = node . children . where ( attrs ) . first
155
- unless child
156
- child = self . class . new ( attributes . merge attrs )
157
- node . children << child
158
- end
159
- node = child
160
- end
161
- node
135
+ # Find a child node whose +ancestry_path+ minus self.ancestry_path is +path+.
136
+ def find_by_path ( path )
137
+ path = [ path ] unless path . is_a? Enumerable
138
+ node = self
139
+ while ( !path . empty? && node )
140
+ node = node . children . send ( "find_by_#{ name_column } " , path . shift )
162
141
end
142
+ node
143
+ end
163
144
164
- protected
165
-
166
- def acts_as_tree_before_save
167
- @was_new_record = new_record?
168
- if changes [ parent_column_name ] &&
169
- parent . present? &&
170
- parent . self_and_ancestors . include? ( self )
171
- # TODO: raise Ouroboros or Philip J. Fry error:
172
- raise ActiveRecord ::ActiveRecordError "You cannot add an ancestor as a descendant"
145
+ # Find a child node whose +ancestry_path+ minus self.ancestry_path is +path+
146
+ def find_or_create_by_path ( path , attributes = { } )
147
+ path = [ path ] unless path . is_a? Enumerable
148
+ node = self
149
+ attrs = { }
150
+ attrs [ :type ] = self . type if ct_subclass? && ct_has_type?
151
+ path . each do |name |
152
+ attrs [ name_sym ] = name
153
+ child = node . children . where ( attrs ) . first
154
+ unless child
155
+ child = self . class . new ( attributes . merge attrs )
156
+ node . children << child
173
157
end
158
+ node = child
174
159
end
160
+ node
161
+ end
175
162
176
- def acts_as_tree_after_save
177
- rebuild! if changes [ parent_column_name ] || @was_new_record
178
- end
163
+ protected
179
164
180
- def rebuild!
181
- delete_hierarchy_references unless @was_new_record
182
- hierarchy_class . create! ( :ancestor => self , :descendant => self , :generations => 0 )
183
- unless root?
184
- connection . execute <<-SQL
185
- INSERT INTO #{ quoted_hierarchy_table_name }
186
- (ancestor_id, descendant_id, generations)
187
- SELECT x.ancestor_id, #{ id } , x.generations + 1
188
- FROM #{ quoted_hierarchy_table_name } x
189
- WHERE x.descendant_id = #{ self . _parent_id }
190
- SQL
191
- end
192
- children . each { |c | c . rebuild! }
165
+ def acts_as_tree_before_save
166
+ @was_new_record = new_record?
167
+ if changes [ parent_column_name ] &&
168
+ parent . present? &&
169
+ parent . self_and_ancestors . include? ( self )
170
+ # TODO: raise Ouroboros or Philip J. Fry error:
171
+ raise ActiveRecord ::ActiveRecordError "You cannot add an ancestor as a descendant"
193
172
end
173
+ end
194
174
195
- def acts_as_tree_before_destroy
196
- delete_hierarchy_references
197
- if closure_tree_options [ :dependent ] == :nullify
198
- children . each { |c | c . rebuild! }
199
- end
200
- end
175
+ def acts_as_tree_after_save
176
+ rebuild! if changes [ parent_column_name ] || @was_new_record
177
+ end
201
178
202
- def delete_hierarchy_references
203
- # The crazy double-wrapped sub-subselect works around MySQL's limitation of subselects on the same table that is being mutated.
204
- # It shouldn't affect performance of postgresql.
205
- # See http://dev.mysql.com/doc/refman/5.0/en/subquery-errors.html
179
+ def rebuild!
180
+ delete_hierarchy_references unless @was_new_record
181
+ hierarchy_class . create! ( :ancestor => self , :descendant => self , :generations => 0 )
182
+ unless root?
206
183
connection . execute <<-SQL
207
- DELETE FROM #{ quoted_hierarchy_table_name }
208
- WHERE descendant_id IN (
209
- SELECT DISTINCT descendant_id
210
- FROM ( SELECT descendant_id
211
- FROM #{ quoted_hierarchy_table_name }
212
- WHERE ancestor_id = #{ id }
213
- ) AS x )
214
- OR descendant_id = #{ id }
184
+ INSERT INTO #{ quoted_hierarchy_table_name }
185
+ (ancestor_id, descendant_id, generations)
186
+ SELECT x.ancestor_id, #{ id } , x.generations + 1
187
+ FROM #{ quoted_hierarchy_table_name } x
188
+ WHERE x.descendant_id = #{ self . _parent_id }
215
189
SQL
216
190
end
191
+ children . each { |c | c . rebuild! }
192
+ end
217
193
218
- def without_self ( scope )
219
- scope . where ( [ "#{ quoted_table_name } .#{ self . class . primary_key } != ?" , self ] )
194
+ def acts_as_tree_before_destroy
195
+ delete_hierarchy_references
196
+ if closure_tree_options [ :dependent ] == :nullify
197
+ children . each { |c | c . rebuild! }
220
198
end
199
+ end
221
200
222
- def _parent_id
223
- send ( parent_column_name )
224
- end
201
+ def delete_hierarchy_references
202
+ # The crazy double-wrapped sub-subselect works around MySQL's limitation of subselects on the same table that is being mutated.
203
+ # It shouldn't affect performance of postgresql.
204
+ # See http://dev.mysql.com/doc/refman/5.0/en/subquery-errors.html
205
+ connection . execute <<-SQL
206
+ DELETE FROM #{ quoted_hierarchy_table_name }
207
+ WHERE descendant_id IN (
208
+ SELECT DISTINCT descendant_id
209
+ FROM ( SELECT descendant_id
210
+ FROM #{ quoted_hierarchy_table_name }
211
+ WHERE ancestor_id = #{ id }
212
+ ) AS x )
213
+ OR descendant_id = #{ id }
214
+ SQL
215
+ end
216
+
217
+ def without_self ( scope )
218
+ scope . where ( [ "#{ quoted_table_name } .#{ self . class . primary_key } != ?" , self ] )
219
+ end
220
+
221
+ def _parent_id
222
+ send ( parent_column_name )
225
223
end
226
224
227
225
module ClassMethods
0 commit comments