| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
|
|---|
| 17 |
|
|---|
| 18 |
require 'active_record' |
|---|
| 19 |
require 'thread' |
|---|
| 20 |
require 'pp' |
|---|
| 21 |
|
|---|
| 22 |
|
|---|
| 23 |
|
|---|
| 24 |
|
|---|
| 25 |
|
|---|
| 26 |
|
|---|
| 27 |
|
|---|
| 28 |
|
|---|
| 29 |
|
|---|
| 30 |
|
|---|
| 31 |
|
|---|
| 32 |
|
|---|
| 33 |
|
|---|
| 34 |
|
|---|
| 35 |
|
|---|
| 36 |
|
|---|
| 37 |
|
|---|
| 38 |
|
|---|
| 39 |
|
|---|
| 40 |
|
|---|
| 41 |
|
|---|
| 42 |
|
|---|
| 43 |
|
|---|
| 44 |
|
|---|
| 45 |
|
|---|
| 46 |
|
|---|
| 47 |
|
|---|
| 48 |
|
|---|
| 49 |
|
|---|
| 50 |
|
|---|
| 51 |
|
|---|
| 52 |
|
|---|
| 53 |
|
|---|
| 54 |
|
|---|
| 55 |
|
|---|
| 56 |
|
|---|
| 57 |
|
|---|
| 58 |
|
|---|
| 59 |
|
|---|
| 60 |
|
|---|
| 61 |
|
|---|
| 62 |
|
|---|
| 63 |
|
|---|
| 64 |
|
|---|
| 65 |
|
|---|
| 66 |
|
|---|
| 67 |
|
|---|
| 68 |
|
|---|
| 69 |
|
|---|
| 70 |
|
|---|
| 71 |
|
|---|
| 72 |
|
|---|
| 73 |
|
|---|
| 74 |
|
|---|
| 75 |
|
|---|
| 76 |
|
|---|
| 77 |
|
|---|
| 78 |
|
|---|
| 79 |
|
|---|
| 80 |
|
|---|
| 81 |
|
|---|
| 82 |
|
|---|
| 83 |
|
|---|
| 84 |
|
|---|
| 85 |
|
|---|
| 86 |
|
|---|
| 87 |
|
|---|
| 88 |
|
|---|
| 89 |
|
|---|
| 90 |
|
|---|
| 91 |
|
|---|
| 92 |
|
|---|
| 93 |
|
|---|
| 94 |
|
|---|
| 95 |
|
|---|
| 96 |
|
|---|
| 97 |
|
|---|
| 98 |
|
|---|
| 99 |
|
|---|
| 100 |
|
|---|
| 101 |
|
|---|
| 102 |
|
|---|
| 103 |
|
|---|
| 104 |
|
|---|
| 105 |
|
|---|
| 106 |
|
|---|
| 107 |
|
|---|
| 108 |
|
|---|
| 109 |
|
|---|
| 110 |
|
|---|
| 111 |
|
|---|
| 112 |
|
|---|
| 113 |
|
|---|
| 114 |
|
|---|
| 115 |
|
|---|
| 116 |
|
|---|
| 117 |
|
|---|
| 118 |
|
|---|
| 119 |
|
|---|
| 120 |
|
|---|
| 121 |
|
|---|
| 122 |
|
|---|
| 123 |
|
|---|
| 124 |
|
|---|
| 125 |
module Rsp |
|---|
| 126 |
module Acts |
|---|
| 127 |
module FasterNestedSet |
|---|
| 128 |
module ChildrenExtension |
|---|
| 129 |
def build(attributes) |
|---|
| 130 |
result = super |
|---|
| 131 |
@node.child_added(result) |
|---|
| 132 |
result.parent_assoc = @node |
|---|
| 133 |
result |
|---|
| 134 |
end |
|---|
| 135 |
|
|---|
| 136 |
def node=(node) |
|---|
| 137 |
@node = node |
|---|
| 138 |
end |
|---|
| 139 |
|
|---|
| 140 |
def add_to_cache(child) |
|---|
| 141 |
@target << child |
|---|
| 142 |
end |
|---|
| 143 |
def empty_cache |
|---|
| 144 |
@target = [] |
|---|
| 145 |
end |
|---|
| 146 |
end |
|---|
| 147 |
|
|---|
| 148 |
def self.included(mod) |
|---|
| 149 |
mod.extend(ClassMethods) |
|---|
| 150 |
end |
|---|
| 151 |
|
|---|
| 152 |
|
|---|
| 153 |
|
|---|
| 154 |
|
|---|
| 155 |
module ClassMethods |
|---|
| 156 |
def acts_as_nested_set(options=nil) |
|---|
| 157 |
|
|---|
| 158 |
before_save("self.nested_set_before_save; true") |
|---|
| 159 |
after_save("self.nested_set_after_save; true") |
|---|
| 160 |
|
|---|
| 161 |
|
|---|
| 162 |
|
|---|
| 163 |
configuration = { |
|---|
| 164 |
:foreign_key => "parent_id", |
|---|
| 165 |
:order => nil, |
|---|
| 166 |
:counter_cache => nil, |
|---|
| 167 |
:left_column => "lft", |
|---|
| 168 |
:right_column => "rgt", |
|---|
| 169 |
:scope => "1 = 1" , |
|---|
| 170 |
:text_column => nil, |
|---|
| 171 |
:level_column => nil |
|---|
| 172 |
} |
|---|
| 173 |
configuration.update(options) if options.is_a?(Hash) |
|---|
| 174 |
|
|---|
| 175 |
configuration[:scope] = "#{configuration[:scope]}_id".intern if configuration[:scope].is_a?(Symbol) && configuration[:scope].to_s !~ /_id$/ |
|---|
| 176 |
|
|---|
| 177 |
if configuration[:scope].is_a?(Symbol) |
|---|
| 178 |
scope_condition_method = %( |
|---|
| 179 |
def scope_condition |
|---|
| 180 |
if #{configuration[:scope].to_s}.nil? |
|---|
| 181 |
"#{table_name}.#{configuration[:scope].to_s} IS NULL" |
|---|
| 182 |
else |
|---|
| 183 |
"#{table_name}.#{configuration[:scope].to_s} = \#{#{configuration[:scope].to_s}}" |
|---|
| 184 |
end |
|---|
| 185 |
end |
|---|
| 186 |
) |
|---|
| 187 |
else |
|---|
| 188 |
scope_condition_method = "def scope_condition() \"#{configuration[:scope]}\" end" |
|---|
| 189 |
end |
|---|
| 190 |
|
|---|
| 191 |
belongs_to :parent_assoc, :class_name => name, :foreign_key => configuration[:foreign_key], :counter_cache => configuration[:counter_cache] |
|---|
| 192 |
|
|---|
| 193 |
has_many :children, :class_name => name, :foreign_key => configuration[:foreign_key], :order => configuration[:order], :dependent => :destroy, :extend => Rsp::Acts::FasterNestedSet::ChildrenExtension |
|---|
| 194 |
|
|---|
| 195 |
class_eval <<-EOV |
|---|
| 196 |
#{scope_condition_method} |
|---|
| 197 |
|
|---|
| 198 |
def left_col_name() "#{configuration[:left_column]}" end |
|---|
| 199 |
|
|---|
| 200 |
def right_col_name() "#{configuration[:right_column]}" end |
|---|
| 201 |
|
|---|
| 202 |
def level_col_name() "#{configuration[:level_column]}" end |
|---|
| 203 |
|
|---|
| 204 |
def has_level_column?() #{not configuration[:level_column].nil?} end |
|---|
| 205 |
|
|---|
| 206 |
def parent_col_name() "#{configuration[:foreign_key]}" end |
|---|
| 207 |
|
|---|
| 208 |
def parent_column() "#{configuration[:foreign_key]}" end |
|---|
| 209 |
|
|---|
| 210 |
def self.roots |
|---|
| 211 |
find(:all, |
|---|
| 212 |
:conditions => "#{configuration[:foreign_key]} IS NULL", |
|---|
| 213 |
:order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}}) |
|---|
| 214 |
end |
|---|
| 215 |
|
|---|
| 216 |
def self.root |
|---|
| 217 |
find(:first, |
|---|
| 218 |
:conditions => "#{configuration[:foreign_key]} IS NULL", |
|---|
| 219 |
:order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}}) |
|---|
| 220 |
end |
|---|
| 221 |
|
|---|
| 222 |
def children_count |
|---|
| 223 |
children_count_internal |
|---|
| 224 |
end |
|---|
| 225 |
EOV |
|---|
| 226 |
|
|---|
| 227 |
include Rsp::Acts::FasterNestedSet::InstanceMethods |
|---|
| 228 |
end |
|---|
| 229 |
end |
|---|
| 230 |
|
|---|
| 231 |
|
|---|
| 232 |
module InstanceMethods |
|---|
| 233 |
|
|---|
| 234 |
def initialize(attributes) |
|---|
| 235 |
if (not attributes.nil?) and (attributes.has_key?(:parent) or attributes.has_key?(parent_column)) then |
|---|
| 236 |
raise "Do not specify parent value on initialization; instead, use parent.children.build or parent.children.create" |
|---|
| 237 |
end |
|---|
| 238 |
super |
|---|
| 239 |
end |
|---|
| 240 |
|
|---|
| 241 |
def parent=(_parent) |
|---|
| 242 |
prev_parent = self.parent_assoc |
|---|
| 243 |
if not prev_parent.nil? |
|---|
| 244 |
|
|---|
| 245 |
prev_parent_lft = prev_parent[left_col_name] |
|---|
| 246 |
prev_parent_rgt = prev_parent[right_col_name] |
|---|
| 247 |
prev_parent_id = prev_parent.id |
|---|
| 248 |
|
|---|
| 249 |
self.parent_assoc = _parent |
|---|
| 250 |
_parent.child_added(self) |
|---|
| 251 |
|
|---|
| 252 |
if _parent[left_col_name] > prev_parent_lft then |
|---|
| 253 |
node_new_right = _parent[right_col_name] - 1 |
|---|
| 254 |
else |
|---|
| 255 |
node_new_right = _parent[right_col_name] + (self[right_col_name] - self[left_col_name]) |
|---|
| 256 |
end |
|---|
| 257 |
node_new_left = node_new_right - (self[right_col_name] - self[left_col_name]) |
|---|
| 258 |
|
|---|
| 259 |
moved_subtree_left_inclusive = self[left_col_name] |
|---|
| 260 |
moved_subtree_right_inclusive = self[right_col_name] |
|---|
| 261 |
moved_subtree_offset = node_new_left - self[left_col_name] |
|---|
| 262 |
|
|---|
| 263 |
if _parent[left_col_name] > prev_parent_lft then |
|---|
| 264 |
interior_subtree_left_inclusive = self[right_col_name] + 1 |
|---|
| 265 |
interior_subtree_right_inclusive = node_new_right |
|---|
| 266 |
interior_subtree_offset = - (self[right_col_name] - self[left_col_name] + 1) |
|---|
| 267 |
else |
|---|
| 268 |
interior_subtree_left_inclusive = node_new_left |
|---|
| 269 |
interior_subtree_right_inclusive = self[left_col_name] - 1 |
|---|
| 270 |
interior_subtree_offset = (self[right_col_name] - self[left_col_name] + 1) |
|---|
| 271 |
end |
|---|
| 272 |
|
|---|
| 273 |
self.class.update_all( [ |
|---|
| 274 |
"#{left_col_name} = #{left_col_name} + (CASE " + \ |
|---|
| 275 |
" WHEN #{left_col_name} >= ? AND #{left_col_name} <= ? THEN ? " + \ |
|---|
| 276 |
" WHEN #{left_col_name} >= ? AND #{left_col_name} <= ? THEN ? " + \ |
|---|
| 277 |
" ELSE 0 " + \ |
|---|
| 278 |
"END), " + \ |
|---|
| 279 |
"#{right_col_name} = #{right_col_name} + (CASE " + \ |
|---|
| 280 |
" WHEN #{right_col_name} >= ? AND #{right_col_name} <= ? THEN ? " + \ |
|---|
| 281 |
" WHEN #{right_col_name} >= ? AND #{right_col_name} <= ? THEN ? " + \ |
|---|
| 282 |
" ELSE 0 " + \ |
|---|
| 283 |
"END)", |
|---|
| 284 |
moved_subtree_left_inclusive, |
|---|
| 285 |
moved_subtree_right_inclusive, |
|---|
| 286 |
moved_subtree_offset, |
|---|
| 287 |
|
|---|
| 288 |
interior_subtree_left_inclusive, |
|---|
| 289 |
interior_subtree_right_inclusive, |
|---|
| 290 |
interior_subtree_offset, |
|---|
| 291 |
|
|---|
| 292 |
moved_subtree_left_inclusive, |
|---|
| 293 |
moved_subtree_right_inclusive, |
|---|
| 294 |
moved_subtree_offset, |
|---|
| 295 |
|
|---|
| 296 |
interior_subtree_left_inclusive, |
|---|
| 297 |
interior_subtree_right_inclusive, |
|---|
| 298 |
interior_subtree_offset |
|---|
| 299 |
], |
|---|
| 300 |
[ |
|---|
| 301 |
"#{scope_condition} AND " + \ |
|---|
| 302 |
"(" + \ |
|---|
| 303 |
" (#{right_col_name} >= ? AND #{left_col_name} <= ?)" + \ |
|---|
| 304 |
" OR (#{right_col_name} >= ? AND #{left_col_name} <= ?)" + \ |
|---|
| 305 |
")", |
|---|
| 306 |
moved_subtree_left_inclusive, |
|---|
| 307 |
moved_subtree_right_inclusive, |
|---|
| 308 |
|
|---|
| 309 |
interior_subtree_left_inclusive, |
|---|
| 310 |
interior_subtree_right_inclusive |
|---|
| 311 |
]) |
|---|
| 312 |
|
|---|
| 313 |
else |
|---|
| 314 |
self.parent_assoc = _parent |
|---|
| 315 |
_parent.child_added(self) |
|---|
| 316 |
end |
|---|
| 317 |
end |
|---|
| 318 |
|
|---|
| 319 |
def parent |
|---|
| 320 |
self.parent_assoc || @parent_internal |
|---|
| 321 |
end |
|---|
| 322 |
|
|---|
| 323 |
def has_parent? |
|---|
| 324 |
not self.parent.nil? |
|---|
| 325 |
end |
|---|
| 326 |
|
|---|
| 327 |
def after_initialize |
|---|
| 328 |
self.children.node = self |
|---|
| 329 |
end |
|---|
| 330 |
|
|---|
| 331 |
def after_find |
|---|
| 332 |
self.children.node = self |
|---|
| 333 |
end |
|---|
| 334 |
|
|---|
| 335 |
def reload |
|---|
| 336 |
result = super |
|---|
| 337 |
self.children.node = self |
|---|
| 338 |
result |
|---|
| 339 |
end |
|---|
| 340 |
|
|---|
| 341 |
def parent_internal=(parent) |
|---|
| 342 |
@parent_internal = parent |
|---|
| 343 |
end |
|---|
| 344 |
|
|---|
| 345 |
def child_added(node) |
|---|
| 346 |
node.parent_internal = self |
|---|
| 347 |
if not self.new_record? then |
|---|
| 348 |
self.add_dirty_child(node) |
|---|
| 349 |
end |
|---|
| 350 |
end |
|---|
| 351 |
|
|---|
| 352 |
def dirty= (val) |
|---|
| 353 |
@dirty = true |
|---|
| 354 |
end |
|---|
| 355 |
|
|---|
| 356 |
def add_dirty_child(child) |
|---|
| 357 |
if true |
|---|
| 358 |
found = false |
|---|
| 359 |
self.children.each_index do |index| |
|---|
| 360 |
if self.children[index].id == child.id then |
|---|
| 361 |
self.children[index] = child |
|---|
| 362 |
found = true |
|---|
| 363 |
end |
|---|
| 364 |
end |
|---|
| 365 |
if not found |
|---|
| 366 |
self.children << child |
|---|
| 367 |
end |
|---|
| 368 |
end |
|---|
| 369 |
child.dirty = true |
|---|
| 370 |
if @parent_internal then |
|---|
| 371 |
@parent_internal.add_dirty_child(self) |
|---|
| 372 |
else |
|---|
| 373 |
self.dirty = true |
|---|
| 374 |
end |
|---|
| 375 |
end |
|---|
| 376 |
|
|---|
| 377 |
def destroy |
|---|
| 378 |
if Thread.current["root_deleted_node"].nil? |
|---|
| 379 |
begin |
|---|
| 380 |
Thread.current["root_deleted_node"] = self |
|---|
| 381 |
|
|---|
| 382 |
return false if callback(:before_destroy) == false |
|---|
| 383 |
|
|---|
| 384 |
if not self.new_record? |
|---|
| 385 |
self.class.transaction do |
|---|
| 386 |
|
|---|
| 387 |
result = self.class.find(:first, :select => [ left_col_name, right_col_name ].join(", "), :conditions => [ "#{self.class.primary_key} = ?", self.id ]) |
|---|
| 388 |
left = result[left_col_name] |
|---|
| 389 |
right = result[right_col_name] |
|---|
| 390 |
|
|---|
| 391 |
self.class.delete_all([ |
|---|
| 392 |
"#{scope_condition} AND #{left_col_name} BETWEEN ? AND ?", |
|---|
| 393 |
left, |
|---|
| 394 |
right |
|---|
| 395 |
]) |
|---|
| 396 |
|
|---|
| 397 |
self.class.update_all([ |
|---|
| 398 |
"#{left_col_name} = #{left_col_name} - (CASE " + \ |
|---|
| 399 |
" WHEN #{left_col_name} > ? " + \ |
|---|
| 400 |
" THEN ? " + \ |
|---|
| 401 |
" ELSE 0 " + \ |
|---|
| 402 |
"END), " + \ |
|---|
| 403 |
"#{right_col_name} = #{right_col_name} - (CASE " + \ |
|---|
| 404 |
" WHEN #{right_col_name} > ? " + \ |
|---|
| 405 |
" THEN ? " + \ |
|---|
| 406 |
" ELSE 0 " + \ |
|---|
| 407 |
"END)", |
|---|
| 408 |
right, |
|---|
| 409 |
right-left+1, |
|---|
| 410 |
right, |
|---|
| 411 |
right-left+1 |
|---|
| 412 |
], |
|---|
| 413 |
[ |
|---|
| 414 |
"#{scope_condition} AND #{right_col_name} > ?", |
|---|
| 415 |
right |
|---|
| 416 |
]) |
|---|
| 417 |
|
|---|
| 418 |
@children.each do |child| |
|---|
| 419 |
child.destroy |
|---|
| 420 |
end |
|---|
| 421 |
end |
|---|
| 422 |
end |
|---|
| 423 |
callback(:after_destroy) |
|---|
| 424 |
ensure |
|---|
| 425 |
Thread.current["root_deleted_node"] = nil |
|---|
| 426 |
end |
|---|
| 427 |
else |
|---|
| 428 |
return false if callback(:before_destroy) == false |
|---|
| 429 |
@children.each do |child| |
|---|
| 430 |
child.destroy |
|---|
| 431 |
end |
|---|
| 432 |
callback(:after_destroy) |
|---|
| 433 |
end |
|---|
| 434 |
end |
|---|
| 435 |
|
|---|
| 436 |
def update_edge_data(left, level) |
|---|
| 437 |
self[left_col_name] = left |
|---|
| 438 |
self[level_col_name] = level unless not has_level_column? |
|---|
| 439 |
child_left = left + 1 |
|---|
| 440 |
child_right = child_left |
|---|
| 441 |
if @children |
|---|
| 442 |
@children.each do |child| |
|---|
| 443 |
child_right = child.update_edge_data(child_left, level + 1) + 1 |
|---|
| 444 |
child_left = child_right |
|---|
| 445 |
end |
|---|
| 446 |
end |
|---|
| 447 |
right = child_right |
|---|
| 448 |
self[right_col_name] = right |
|---|
| 449 |
right |
|---|
| 450 |
end |
|---|
| 451 |
|
|---|
| 452 |
def nested_set_after_save |
|---|
| 453 |
if Thread.current["saved_node"] == self |
|---|
| 454 |
Thread.current["saved_node"] = nil |
|---|
| 455 |
|
|---|
| 456 |
if not @insert_range.nil? |
|---|
| 457 |
insert_offset = "((" + @left_select_expression + ") + #{@insert_range})" |
|---|
| 458 |
|
|---|
| 459 |
self.class.update_all( "#{left_col_name} = #{left_col_name} + (CASE WHEN #{left_col_name} >= (#{@left_select_expression}) THEN #{@insert_range} WHEN #{left_col_name} < 0 THEN #{insert_offset} ELSE 0 END), #{right_col_name} = #{right_col_name} + (CASE WHEN #{right_col_name} >= (#{@left_select_expression}) THEN #{@insert_range} WHEN #{right_col_name} < 0 THEN #{insert_offset} ELSE 0 END)", |
|---|
| 460 |
"#{scope_condition} AND (#{right_col_name} >= (#{@left_select_expression}) OR #{right_col_name} < 0)" ) |
|---|
| 461 |
if not self.parent_assoc.nil? |
|---|
| 462 |
self.parent_assoc[right_col_name] += 2 |
|---|
| 463 |
end |
|---|
| 464 |
end |
|---|
| 465 |
|
|---|
| 466 |
end |
|---|
| 467 |
|
|---|
| 468 |
@dirty = false |
|---|
| 469 |
|
|---|
| 470 |
if Thread.current["unsaved_children"] then |
|---|
| 471 |
unsaved_children = Thread.current["unsaved_children"] |
|---|
| 472 |
Thread.current["unsaved_children"] = [] |
|---|
| 473 |
unsaved_children.each do |unsaved_child| |
|---|
| 474 |
unsaved_child.save |
|---|
| 475 |
end |
|---|
| 476 |
end |
|---|
| 477 |
|
|---|
| 478 |
end |
|---|
| 479 |
|
|---|
| 480 |
def dirty? |
|---|
| 481 |
@dirty |
|---|
| 482 |
end |
|---|
| 483 |
|
|---|
| 484 |
def save |
|---|
| 485 |
if Thread.current["transaction_node"].nil? |
|---|
| 486 |
Thread.current["transaction_node"] = self |
|---|
| 487 |
self.class.transaction { |
|---|
| 488 |
super |
|---|
| 489 |
} |
|---|
| 490 |
Thread.current["transaction_node"] = nil |
|---|
| 491 |
else |
|---|
| 492 |
super |
|---|
| 493 |
end |
|---|
| 494 |
end |
|---|
| 495 |
|
|---|
| 496 |
|
|---|
| 497 |
def nested_set_before_save |
|---|
| 498 |
@insert_range = nil |
|---|
| 499 |
|
|---|
| 500 |
if @new_record or @save_required then |
|---|
| 501 |
self.nested_set_before_save_new_record |
|---|
| 502 |
elsif @dirty then |
|---|
| 503 |
self.nested_set_before_save_dirty_record |
|---|
| 504 |
end |
|---|
| 505 |
end |
|---|
| 506 |
|
|---|
| 507 |
def nested_set_before_save_dirty_record |
|---|
| 508 |
if @dirty and self.children.loaded then |
|---|
| 509 |
self.children.each do |child| |
|---|
| 510 |
if child.dirty? |
|---|
| 511 |
if child.new_record? or child.save_required? |
|---|
| 512 |
Thread.current["unsaved_children"] = [child] + (Thread.current["unsaved_children"] || []) |
|---|
| 513 |
else |
|---|
| 514 |
child.nested_set_before_save_dirty_record |
|---|
| 515 |
end |
|---|
| 516 |
end |
|---|
| 517 |
end |
|---|
| 518 |
end |
|---|
| 519 |
end |
|---|
| 520 |
|
|---|
| 521 |
def nested_set_before_save_new_record |
|---|
| 522 |
@insert_range = nil |
|---|
| 523 |
|
|---|
| 524 |
if Thread.current["saved_node"].nil? |
|---|
| 525 |
Thread.current["saved_node"] = self |
|---|
| 526 |
|
|---|
| 527 |
if self.parent_assoc.nil? |
|---|
| 528 |
left = 1 |
|---|
| 529 |
level = 1 |
|---|
| 530 |
@left_select_expression = "SELECT CASE WHEN MAX(#{right_col_name}) IS NOT NULL THEN MAX(#{right_col_name}) + 1 ELSE 1 END FROM #{self.class.table_name} WHERE #{scope_condition} AND #{parent_col_name} IS NULL AND #{right_col_name} > 0" |
|---|
| 531 |
else |
|---|
| 532 |
left = self.parent_assoc[right_col_name] |
|---|
| 533 |
if has_level_column? |
|---|
| 534 |
level = self.parent_assoc[level_col_name] + 1 |
|---|
| 535 |
else |
|---|
| 536 |
level = 0 |
|---|
| 537 |
end |
|---|
| 538 |
@left_select_expression = "SELECT #{right_col_name} FROM #{self.class.table_name} WHERE #{self.class.primary_key} = #{self.parent_assoc.id}" |
|---|
| 539 |
end |
|---|
| 540 |
|
|---|
| 541 |
@insert_range = self.update_edge_data(0, level) + 1 |
|---|
| 542 |
|
|---|
| 543 |
self.update_edge_data(-@insert_range, level) |
|---|
| 544 |
end |
|---|
| 545 |
end |
|---|
| 546 |
|
|---|
| 547 |
|
|---|
| 548 |
|
|---|
| 549 |
|
|---|
| 550 |
|
|---|
| 551 |
|
|---|
| 552 |
def ancestors |
|---|
| 553 |
self.class.find(:all, :conditions => [ "#{left_col_name} < (select #{left_col_name} from #{self.class.table_name} where #{self.class.primary_key} = ?) and #{right_col_name} > (select #{right_col_name} from #{self.class.table_name} where #{self.class.primary_key} = ?) and #{scope_condition}", self.id, self.id ], :order => "#{left_col_name} desc") |
|---|
| 554 |
end |
|---|
| 555 |
|
|---|
| 556 |
|
|---|
| 557 |
|
|---|
| 558 |
|
|---|
| 559 |
def self_and_ancestors_up_to(other) |
|---|
| 560 |
self.class.find(:all, :conditions => [ "#{left_col_name} <= (select #{left_col_name} from #{self.class.table_name} where #{self.class.primary_key} = ?) and #{right_col_name} >= (select #{right_col_name} from #{self.class.table_name} where #{self.class.primary_key} = ?) and #{level_col_name} > (select #{level_col_name} from #{self.class.table_name} where #{self.class.primary_key} = ?) and #{scope_condition}", self.id, self.id, other.id ], :order => "#{left_col_name} desc") |
|---|
| 561 |
end |
|---|
| 562 |
|
|---|
| 563 |
def root |
|---|
| 564 |
node = self |
|---|
| 565 |
node = node.parent until not node.has_parent? |
|---|
| 566 |
node |
|---|
| 567 |
end |
|---|
| 568 |
|
|---|
| 569 |
def siblings |
|---|
| 570 |
self_and_siblings - [self] |
|---|
| 571 |
end |
|---|
| 572 |
|
|---|
| 573 |
def self_and_siblings |
|---|
| 574 |
has_parent? ? parent.children : self.class.roots |
|---|
| 575 |
end |
|---|
| 576 |
|
|---|
| 577 |
|
|---|
| 578 |
|
|---|
| 579 |
|
|---|
| 580 |
|
|---|
| 581 |
|
|---|
| 582 |
def full_set |
|---|
| 583 |
self.class.base_class.find(:all, :conditions => [ |
|---|
| 584 |
"#{scope_condition} AND (#{left_col_name} BETWEEN (SELECT #{left_col_name} FROM #{self.class.table_name} WHERE #{self.class.primary_key} = ?) AND (SELECT #{right_col_name} FROM #{self.class.table_name} WHERE #{self.class.primary_key} = ?))", |
|---|
| 585 |
self.id, |
|---|
| 586 |
self.id |
|---|
| 587 |
]) |
|---|
| 588 |
end |
|---|
| 589 |
|
|---|
| 590 |
|
|---|
| 591 |
def all_children |
|---|
| 592 |
self.class.base_class.find(:all, :conditions => [ |
|---|
| 593 |
"#{scope_condition} AND (#{left_col_name} > (SELECT #{left_col_name} FROM #{self.class.table_name} WHERE #{self.class.primary_key} = ?)) AND (#{right_col_name} < (SELECT #{right_col_name} FROM #{self.class.table_name} WHERE #{self.class.primary_key} = ?))", |
|---|
| 594 |
self.id, |
|---|
| 595 |
self.id |
|---|
| 596 |
]) |
|---|
| 597 |
end |
|---|
| 598 |
|
|---|
| 599 |
|
|---|
| 600 |
def direct_children |
|---|
| 601 |
self.class.base_class.find(:all, :conditions => [ "#{scope_condition} and #{parent_column} = ?", self.id ]) |
|---|
| 602 |
end |
|---|
| 603 |
|
|---|
| 604 |
|
|---|
| 605 |
def root? |
|---|
| 606 |
parent_id = self[parent_column] |
|---|
| 607 |
(parent_id == 0 || parent_id.nil?) && (self[left_col_name] == 1) && (self[right_col_name] > self[left_col_name]) |
|---|
| 608 |
end |
|---|
| 609 |
|
|---|
| 610 |
|
|---|
| 611 |
def child? |
|---|
| 612 |
parent_id = self[parent_column] |
|---|
| 613 |
!(parent_id == 0 || parent_id.nil?) && (self[left_col_name] > 1) && (self[right_col_name] > self[left_col_name]) |
|---|
| 614 |
end |
|---|
| 615 |
|
|---|
| 616 |
|
|---|
| 617 |
def unknown? |
|---|
| 618 |
!root? && !child? |
|---|
| 619 |
end |
|---|
| 620 |
|
|---|
| 621 |
|
|---|
| 622 |
|
|---|
| 623 |
|
|---|
| 624 |
|
|---|
| 625 |
|
|---|
| 626 |
def self_and_ancestors |
|---|
| 627 |
[self] + ancestors |
|---|
| 628 |
end |
|---|
| 629 |
|
|---|
| 630 |
|
|---|
| 631 |
|
|---|
| 632 |
def save_required? |
|---|
| 633 |
@save_required |
|---|
| 634 |
end |
|---|
| 635 |
|
|---|
| 636 |
def save_required= (val) |
|---|
| 637 |
@save_required = val |
|---|
| 638 |
end |
|---|
| 639 |
|
|---|
| 640 |
def add_child(child) |
|---|
| 641 |
self.children << child |
|---|
| 642 |
child.save |
|---|
| 643 |
end |
|---|
| 644 |
|
|---|
| 645 |
def parent_assigned(*args) |
|---|
| 646 |
self.save_required = true |
|---|
| 647 |
parent_assoc.child_added(self) |
|---|
| 648 |
end |
|---|
| 649 |
|
|---|
| 650 |
def child_create(attributes) |
|---|
| 651 |
children.create(attributes) |
|---|
| 652 |
end |
|---|
| 653 |
|
|---|
| 654 |
def child_delete(child) |
|---|
| 655 |
children.delete(child) |
|---|
| 656 |
end |
|---|
| 657 |
|
|---|
| 658 |
|
|---|
| 659 |
|
|---|
| 660 |
def update |
|---|
| 661 |
if not self.new_record? |
|---|
| 662 |
return false if callback(:before_update) == false |
|---|
| 663 |
a = attributes_with_quotes(false) |
|---|
| 664 |
a.delete(left_col_name) |
|---|
| 665 |
a.delete(right_col_name) |
|---|
| 666 |
connection.update("UPDATE #{self.class.table_name} " + \ |
|---|
| 667 |
"SET #{quoted_comma_pair_list(connection, a)} " + \ |
|---|
| 668 |
"WHERE #{self.class.primary_key} = #{id}", |
|---|
| 669 |
"#{self.class.name} Update" |
|---|
| 670 |
) |
|---|
| 671 |
callback(:after_update) |
|---|
| 672 |
return true |
|---|
| 673 |
else |
|---|
| 674 |
super |
|---|
| 675 |
end |
|---|
| 676 |
end |
|---|
| 677 |
|
|---|
| 678 |
def ensure_consistency |
|---|
| 679 |
load_all_children |
|---|
| 680 |
begin |
|---|
| 681 |
self.ensure_consistency_recursive |
|---|
| 682 |
rescue |
|---|
| 683 |
self.print_recursive |
|---|
| 684 |
raise |
|---|
| 685 |
end |
|---|
| 686 |
end |
|---|
| 687 |
|
|---|
| 688 |
def print_recursive(indent=0) |
|---|
| 689 |
indentation = " " * indent |
|---|
| 690 |
puts "#{indentation}#{name} (id=#{id}, lft=#{self[left_col_name]}, rgt=#{self[right_col_name]}, level=#{self[level_col_name]})" |
|---|
| 691 |
self.children.each { |child| child.print_recursive(indent+1) } |
|---|
| 692 |
end |
|---|
| 693 |
|
|---|
| 694 |
def ensure_consistency_recursive |
|---|
| 695 |
expect_left = self[left_col_name] + 1 |
|---|
| 696 |
children.each do |child| |
|---|
| 697 |
if child[left_col_name] != expect_left |
|---|
| 698 |
raise "in node #{child.id}, left expected to be #{expect_left} but is #{child[left_col_name]}" |
|---|
| 699 |
end |
|---|
| 700 |
expect_right = child.ensure_consistency_recursive |
|---|
| 701 |
if child[right_col_name] != expect_right |
|---|
| 702 |
raise "in node #{child.id}, right expected to be #{expect_right} but is #{child[right_col_name]}" |
|---|
| 703 |
end |
|---|
| 704 |
if child[level_col_name] != self[level_col_name] + 1 |
|---|
| 705 |
raise "in node #{child.id}, level expected to be #{self[level_col_name] + 1} but is #{child[level_col_name]}" |
|---|
| 706 |
end |
|---|
| 707 |
expect_left = expect_right + 1 |
|---|
| 708 |
end |
|---|
| 709 |
if self[right_col_name] != expect_left |
|---|
| 710 |
raise "in node #{id}, right expected to be #{expect_left} but is #{self[right_col_name]}" |
|---|
| 711 |
end |
|---|
| 712 |
return self[right_col_name] |
|---|
| 713 |
end |
|---|
| 714 |
|
|---|
| 715 |
|
|---|
| 716 |
|
|---|
| 717 |
def load_all_children(depth=0, options=nil) |
|---|
| 718 |
|
|---|
| 719 |
if depth == 0 or not has_level_column? |
|---|
| 720 |
depth_condition = "1=1" |
|---|
| 721 |
else |
|---|
| 722 |
depth_condition = "#{self.class.table_name}.#{level_col_name} <= #{level + depth}" |
|---|
| 723 |
end |
|---|
| 724 |
|
|---|
| 725 |
options = options || Hash.new |
|---|
| 726 |
|
|---|
| 727 |
options[:conditions] = [ |
|---|
| 728 |
"#{depth_condition} AND #{scope_condition} AND (#{self.class.table_name}.#{left_col_name} > (SELECT #{left_col_name} FROM #{self.class.table_name} WHERE #{self.class.primary_key} = ?)) AND (#{self.class.table_name}.#{right_col_name} < (SELECT #{right_col_name} FROM #{self.class.table_name} WHERE #{self.class.primary_key} = ?))" ] + [ |
|---|
| 729 |
self.id, |
|---|
| 730 |
self.id |
|---|
| 731 |
] |
|---|
| 732 |
|
|---|
| 733 |
options[:order] = self.class.table_name + "." +left_col_name |
|---|
| 734 |
|
|---|
| 735 |
result = self.class.find(:all, options) |
|---|
| 736 |
|
|---|
| 737 |
idMap = Hash.new |
|---|
| 738 |
result.each do |child| |
|---|
| 739 |
idMap[child[:id]] = child |
|---|
| 740 |
end |
|---|
| 741 |
|
|---|
| 742 |
idMap[self.id] = self |
|---|
| 743 |
self.children.empty_cache |
|---|
| 744 |
|
|---|
| 745 |
result.each do |child| |
|---|
| 746 |
idMap[child[:parent_id]].children.add_to_cache(child) unless child[:parent_id].nil? |
|---|
| 747 |
child.parent_assoc = idMap[child[:parent_id]] |
|---|
| 748 |
end |
|---|
| 749 |
|
|---|
| 750 |
idMap.each do |key, child| |
|---|
| 751 |
child.children.loaded |
|---|
| 752 |
end |
|---|
| 753 |
end |
|---|
| 754 |
|
|---|
| 755 |
private |
|---|
| 756 |
|
|---|
| 757 |
def children_count_internal(force_reload = false) |
|---|
| 758 |
if self.new_record? |
|---|
| 759 |
self.children.inject(0) { |sum, child| sum + 1 + child.children_count } |
|---|
| 760 |
else |
|---|
| 761 |
left_and_right = self.class.find(:first, |
|---|
| 762 |
:select => "#{left_col_name} AS left, #{right_col_name} AS right", |
|---|
| 763 |
:conditions => [ "#{self.class.primary_key} = ?", self.id ]) |
|---|
| 764 |
(left_and_right["right"].to_i - left_and_right["left"].to_i - 1) / 2 |
|---|
| 765 |
end |
|---|
| 766 |
end |
|---|
| 767 |
end |
|---|
| 768 |
end |
|---|
| 769 |
end |
|---|
| 770 |
end |
|---|
| 771 |
|
|---|
| 772 |
|
|---|
| 773 |
|
|---|
| 774 |
|
|---|
| 775 |
ActiveRecord::Base.class_eval do |
|---|
| 776 |
include Rsp::Acts::FasterNestedSet |
|---|
| 777 |
end |
|---|