mongo_nested_set 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Rakefile ADDED
@@ -0,0 +1,55 @@
1
+ begin
2
+ require 'jeweler'
3
+ rescue LoadError
4
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
5
+ exit 1
6
+ end
7
+ require 'rake/testtask'
8
+ require 'rake/rdoctask'
9
+ require 'rcov/rcovtask'
10
+ require "load_multi_rails_rake_tasks"
11
+
12
+ Jeweler::Tasks.new do |s|
13
+ s.name = "mongo_nested_set"
14
+ s.summary = "Port of awesome_nested_set for MongoMapper"
15
+ s.description = s.summary
16
+ s.email = "fauxparse@gmail.com"
17
+ s.homepage = "http://github.com/fauxparse/mongo_nested_set"
18
+ s.authors = ["Matt Powell", "Brandon Keepers", "Daniel Morrison"]
19
+ s.add_dependency "mongo_mapper", ['>= 0.6.10']
20
+ s.has_rdoc = true
21
+ s.extra_rdoc_files = [ "README.rdoc"]
22
+ s.rdoc_options = ["--main", "README.rdoc", "--inline-source", "--line-numbers"]
23
+ s.test_files = Dir['test/**/*.{yml,rb}']
24
+ end
25
+ Jeweler::GemcutterTasks.new
26
+
27
+ desc 'Default: run unit tests.'
28
+ task :default => :test
29
+
30
+ desc 'Test the mongo_nested_set plugin.'
31
+ Rake::TestTask.new(:test) do |t|
32
+ t.libs += ['lib', 'test']
33
+ t.pattern = 'test/**/*_test.rb'
34
+ t.verbose = true
35
+ end
36
+
37
+ desc 'Generate documentation for the awesome_nested_set plugin.'
38
+ Rake::RDocTask.new(:rdoc) do |rdoc|
39
+ rdoc.rdoc_dir = 'rdoc'
40
+ rdoc.title = 'MongoNestedSet'
41
+ rdoc.options << '--line-numbers' << '--inline-source'
42
+ rdoc.rdoc_files.include('README.rdoc')
43
+ rdoc.rdoc_files.include('lib/**/*.rb')
44
+ end
45
+
46
+ namespace :test do
47
+ desc "just rcov minus html output"
48
+ Rcov::RcovTask.new(:coverage) do |t|
49
+ t.libs << 'test'
50
+ t.test_files = FileList['test/**/*_test.rb']
51
+ t.output_dir = 'coverage'
52
+ t.verbose = true
53
+ t.rcov_opts = %w(--exclude test,/usr/lib/ruby,/Library/Ruby,lib/mongo_nested_set/named_scope.rb --sort coverage)
54
+ end
55
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + "/rails/init"
@@ -0,0 +1,606 @@
1
+ module MongoNestedSet
2
+ def self.included(base)
3
+ base.extend(SingletonMethods)
4
+ end
5
+
6
+ module SingletonMethods
7
+ def acts_as_nested_set(options = {})
8
+ options = {
9
+ :parent_column => 'parent_id',
10
+ :left_column => 'lft',
11
+ :right_column => 'rgt',
12
+ :dependent => :delete_all, # or :destroy
13
+ }.merge(options)
14
+
15
+ if options[:scope].is_a?(Symbol) && options[:scope].to_s !~ /_id$/
16
+ options[:scope] = "#{options[:scope]}_id".intern
17
+ end
18
+
19
+ write_inheritable_attribute :acts_as_nested_set_options, options
20
+ class_inheritable_reader :acts_as_nested_set_options
21
+
22
+ unless self.is_a?(ClassMethods)
23
+ include Comparable
24
+ include Columns
25
+ include InstanceMethods
26
+ extend Columns
27
+ extend ClassMethods
28
+
29
+ belongs_to :parent, :class_name => self.base_class.to_s,
30
+ :foreign_key => parent_column_name
31
+ many :children, :class_name => self.base_class.to_s,
32
+ :foreign_key => parent_column_name, :order => quoted_left_column_name
33
+
34
+ attr_accessor :skip_before_destroy
35
+
36
+ key left_column_name.intern, Integer
37
+ key right_column_name.intern, Integer
38
+ key parent_column_name.intern, ObjectId
39
+
40
+ # no bulk assignment
41
+ # if accessible_attributes.blank?
42
+ # attr_protected left_column_name.intern, right_column_name.intern
43
+ # end
44
+
45
+ before_create :set_default_left_and_right
46
+ before_save :store_new_parent
47
+ after_save :move_to_new_parent
48
+ before_destroy :destroy_descendants
49
+
50
+ # no assignment to structure fields
51
+ # [left_column_name, right_column_name].each do |column|
52
+ # module_eval <<-"end_eval", __FILE__, __LINE__
53
+ # def #{column}=(x)
54
+ # raise "Unauthorized assignment to #{column}: it's an internal field handled by acts_as_nested_set code, use move_to_* methods instead."
55
+ # end
56
+ # end_eval
57
+ # end
58
+
59
+ # named_scope :roots, :conditions => {parent_column_name => nil}, :order => quoted_left_column_name
60
+ # named_scope :leaves, :conditions => "#{quoted_right_column_name} - #{quoted_left_column_name} = 1", :order => quoted_left_column_name
61
+
62
+ define_callbacks("before_move", "after_move")
63
+ end
64
+ end
65
+ end
66
+
67
+ module ClassMethods
68
+ def base_class
69
+ if superclass == Object
70
+ self
71
+ else
72
+ super
73
+ end
74
+ end
75
+
76
+ # Returns the first root
77
+ def root
78
+ find :first, :parent_id => nil
79
+ end
80
+
81
+ def roots
82
+ find :all, :parent_id => nil, :order => "#{left_column_name} ASC"
83
+ end
84
+
85
+ def valid?
86
+ left_and_rights_valid? && no_duplicates_for_columns? && all_roots_valid?
87
+ end
88
+
89
+ def left_and_rights_valid?
90
+ find(:all).detect { |node|
91
+ node.send(left_column_name).nil? ||
92
+ node.send(right_column_name).nil? ||
93
+ node.send(left_column_name) >= node.send(right_column_name) ||
94
+ !node.parent.nil? && (
95
+ node.send(left_column_name) <= node.parent.send(left_column_name) ||
96
+ node.send(right_column_name) >= node.parent.send(right_column_name)
97
+ )
98
+ }.nil?
99
+ end
100
+
101
+ def no_duplicates_for_columns?
102
+ find(:all).inject(true) { |memo, node|
103
+ memo && [left_column_name, right_column_name].inject(true) { |v, column|
104
+ v && count(node.scoped(column.to_sym => node.send(column))) == 1
105
+ }
106
+ }
107
+ end
108
+
109
+ # Wrapper for each_root_valid? that can deal with scope.
110
+ def all_roots_valid?
111
+ if acts_as_nested_set_options[:scope]
112
+ roots.group_by{|record| scope_column_names.collect{|col| record.send(col.to_sym)}}.all? do |scope, grouped_roots|
113
+ each_root_valid?(grouped_roots)
114
+ end
115
+ else
116
+ each_root_valid?(roots)
117
+ end
118
+ end
119
+
120
+ def each_root_valid?(roots_to_validate)
121
+ left = right = 0
122
+ roots_to_validate.all? do |root|
123
+ returning(root.left > left && root.right > right) do
124
+ left = root.left
125
+ right = root.right
126
+ end
127
+ end
128
+ end
129
+
130
+ # Rebuilds the left & rights if unset or invalid. Also very useful for converting from acts_as_tree.
131
+ def rebuild!
132
+ # Don't rebuild a valid tree.
133
+ return true if valid?
134
+
135
+ scope = lambda{ |node| {} }
136
+ if acts_as_nested_set_options[:scope]
137
+ scope = lambda { |node|
138
+ scope_column_names.inject({}) { |hash, column_name|
139
+ hash[column_name] = node.send(column_name.to_sym)
140
+ hash
141
+ }
142
+ }
143
+ end
144
+ indices = {}
145
+
146
+ set_left_and_rights = lambda do |node|
147
+ # set left
148
+ node.send(:"#{left_column_name}=", (indices[scope.call(node)] += 1))
149
+ # find
150
+ find(:all, scope.call(node).merge(parent_column_name => node.id)).each{|n| set_left_and_rights.call(n) }
151
+ # set right
152
+ node.send(:"#{right_column_name}=", (indices[scope.call(node)] += 1))
153
+ node.save!
154
+ end
155
+
156
+ # Find root node(s)
157
+ root_nodes = find(:all, { parent_column_name => nil, :order => "#{left_column_name}, #{right_column_name}, id" }).each do |root_node|
158
+ # setup index for this scope
159
+ indices[scope.call(root_node)] ||= 0
160
+ set_left_and_rights.call(root_node)
161
+ end
162
+ end
163
+
164
+ # Iterates over tree elements and determines the current level in the tree.
165
+ # Only accepts default ordering, odering by an other column than lft
166
+ # does not work. This method is much more efficent than calling level
167
+ # because it doesn't require any additional database queries.
168
+ #
169
+ # Example:
170
+ # Category.each_with_level(Category.root.self_and_descendants) do |o, level|
171
+ #
172
+ def each_with_level(objects)
173
+ path = [nil]
174
+ objects.sort_by(&left_column_name.to_sym).each do |o|
175
+ if o._parent_id != path.last
176
+ # we are on a new level, did we decent or ascent?
177
+ if path.include?(o._parent_id)
178
+ # remove wrong wrong tailing paths elements
179
+ path.pop while path.last != o._parent_id
180
+ else
181
+ path << o._parent_id
182
+ end
183
+ end
184
+ yield(o, path.length - 1)
185
+ end
186
+ end
187
+ end
188
+
189
+ # Mixed into both classes and instances to provide easy access to the column names
190
+ module Columns
191
+ def left_column_name
192
+ acts_as_nested_set_options[:left_column]
193
+ end
194
+
195
+ def right_column_name
196
+ acts_as_nested_set_options[:right_column]
197
+ end
198
+
199
+ def parent_column_name
200
+ acts_as_nested_set_options[:parent_column]
201
+ end
202
+
203
+ def scope_column_names
204
+ Array(acts_as_nested_set_options[:scope])
205
+ end
206
+
207
+ def quoted_left_column_name
208
+ left_column_name
209
+ end
210
+
211
+ def quoted_right_column_name
212
+ right_column_name
213
+ end
214
+
215
+ def quoted_parent_column_name
216
+ parent_column_name
217
+ end
218
+
219
+ def quoted_scope_column_names
220
+ scope_column_names
221
+ end
222
+ end
223
+
224
+ module InstanceMethods
225
+ def base_class
226
+ self.class.base_class
227
+ end
228
+
229
+ # Value of the parent column
230
+ def _parent_id
231
+ send parent_column_name
232
+ end
233
+
234
+ # Value of the left column
235
+ def left
236
+ send left_column_name
237
+ end
238
+
239
+ # Value of the right column
240
+ def right
241
+ send right_column_name
242
+ end
243
+
244
+ # Returns true if this is a root node.
245
+ def root?
246
+ _parent_id.nil?
247
+ end
248
+
249
+ def leaf?
250
+ !new? && right - left == 1
251
+ end
252
+
253
+ # Returns true is this is a child node
254
+ def child?
255
+ !_parent_id.nil?
256
+ end
257
+
258
+ # order by left column
259
+ def <=>(x)
260
+ left <=> x.left
261
+ end
262
+
263
+ # Redefine to act like active record
264
+ def ==(comparison_object)
265
+ comparison_object.equal?(self) ||
266
+ (comparison_object.instance_of?(self.class) &&
267
+ comparison_object.id == id &&
268
+ !comparison_object.new?)
269
+ end
270
+
271
+ def scope_hash
272
+ Hash[*Array(acts_as_nested_set_options[:scope]).collect { |s| [s, send(s)] }.flatten]
273
+ end
274
+
275
+ def scoped(conditions = {})
276
+ conditions.reverse_merge(scope_hash)
277
+ end
278
+
279
+ # Returns root
280
+ def root
281
+ base_class.find :first, scoped(left_column_name => { '$lte' => left }, right_column_name => { '$gte' => right })
282
+ end
283
+
284
+ # Returns the array of all parents and self
285
+ def self_and_ancestors
286
+ base_class.find :all, scoped(left_column_name => { '$lte' => left }, right_column_name => { '$gte' => right })
287
+ end
288
+
289
+ # Returns an array of all parents
290
+ def ancestors
291
+ without_self self_and_ancestors
292
+ end
293
+
294
+ # Returns the array of all children of the parent, including self
295
+ def self_and_siblings
296
+ base_class.find :all, scoped(parent_column_name => _parent_id)
297
+ end
298
+
299
+ # Returns the array of all children of the parent, except self
300
+ def siblings
301
+ without_self self_and_siblings
302
+ end
303
+
304
+ # Returns a set of all of its nested children which do not have children
305
+ # def leaves
306
+ # descendants.scoped :conditions => "#{self.class.collection_name}.#{quoted_right_column_name} - #{self.class.collection_name}.#{quoted_left_column_name} = 1"
307
+ # end
308
+
309
+ # Returns the level of this object in the tree
310
+ # root level is 0
311
+ def level
312
+ _parent_id.nil? ? 0 : ancestors.count
313
+ end
314
+
315
+ # Returns a set of itself and all of its nested children
316
+ def self_and_descendants
317
+ base_class.find :all, scoped(left_column_name => { '$gte' => left }, right_column_name => { '$lte' => right })
318
+ end
319
+
320
+ # Returns a set of all of its children and nested children
321
+ def descendants
322
+ without_self self_and_descendants
323
+ end
324
+
325
+ def is_descendant_of?(other)
326
+ other.left < self.left && self.left < other.right && same_scope?(other)
327
+ end
328
+
329
+ def is_or_is_descendant_of?(other)
330
+ other.left <= self.left && self.left < other.right && same_scope?(other)
331
+ end
332
+
333
+ def is_ancestor_of?(other)
334
+ self.left < other.left && other.left < self.right && same_scope?(other)
335
+ end
336
+
337
+ def is_or_is_ancestor_of?(other)
338
+ self.left <= other.left && other.left < self.right && same_scope?(other)
339
+ end
340
+
341
+ # Check if other model is in the same scope
342
+ def same_scope?(other)
343
+ Array(acts_as_nested_set_options[:scope]).all? do |attr|
344
+ self.send(attr) == other.send(attr)
345
+ end
346
+ end
347
+
348
+ # Find the first sibling to the left
349
+ def left_sibling
350
+ base_class.find :first, scoped(parent_column_name => _parent_id, left_column_name => { '$lt' => left }, :order => "#{left_column_name} DESC")
351
+ end
352
+
353
+ # Find the first sibling to the right
354
+ def right_sibling
355
+ base_class.find :first, scoped(parent_column_name => _parent_id, left_column_name => { '$gt' => right }, :order => "#{left_column_name}")
356
+ end
357
+
358
+ # Shorthand method for finding the left sibling and moving to the left of it.
359
+ def move_left
360
+ move_to_left_of left_sibling
361
+ end
362
+
363
+ # Shorthand method for finding the right sibling and moving to the right of it.
364
+ def move_right
365
+ move_to_right_of right_sibling
366
+ end
367
+
368
+ # Move the node to the left of another node (you can pass id only)
369
+ def move_to_left_of(node)
370
+ move_to node, :left
371
+ end
372
+
373
+ # Move the node to the left of another node (you can pass id only)
374
+ def move_to_right_of(node)
375
+ move_to node, :right
376
+ end
377
+
378
+ # Move the node to the child of another node (you can pass id only)
379
+ def move_to_child_of(node)
380
+ move_to node, :child
381
+ end
382
+
383
+ # Move the node to root nodes
384
+ def move_to_root
385
+ move_to nil, :root
386
+ end
387
+
388
+ def move_possible?(target)
389
+ self != target && # Can't target self
390
+ same_scope?(target) && # can't be in different scopes
391
+ # !(left..right).include?(target.left..target.right) # this needs tested more
392
+ # detect impossible move
393
+ !((left <= target.left && right >= target.left) or (left <= target.right && right >= target.right))
394
+ end
395
+
396
+ def to_text
397
+ self_and_descendants.map do |node|
398
+ "#{'*'*(node.level+1)} #{node.id} #{node.to_s} (#{node._parent_id}, #{node.left}, #{node.right})"
399
+ end.join("\n")
400
+ end
401
+
402
+ protected
403
+ def without_self(set)
404
+ set.reject { |node| node.id == id }
405
+ end
406
+
407
+ # All nested set queries should use this nested_set_scope, which performs finds on
408
+ # the base ActiveRecord class, using the :scope declared in the acts_as_nested_set
409
+ # declaration.
410
+ def nested_set_scope
411
+ raise "called nested_set_scope"
412
+ options = {:order => quoted_left_column_name}
413
+ scopes = Array(acts_as_nested_set_options[:scope])
414
+ options[:conditions] = scopes.inject({}) do |conditions,attr|
415
+ conditions.merge attr => self[attr]
416
+ end unless scopes.empty?
417
+ self.class.base_class.scoped options
418
+ end
419
+
420
+ def store_new_parent
421
+ unless @skip_nested_set_callbacks
422
+ @move_to_new_parent_id = send("#{parent_column_name}_changed?") ? _parent_id : false
423
+ end
424
+ true # force callback to return true
425
+ end
426
+
427
+ def move_to_new_parent
428
+ unless @skip_nested_set_callbacks
429
+ if @move_to_new_parent_id.nil?
430
+ move_to_root
431
+ elsif @move_to_new_parent_id
432
+ move_to_child_of(@move_to_new_parent_id)
433
+ end
434
+ end
435
+ end
436
+
437
+ # on creation, set automatically lft and rgt to the end of the tree
438
+ def set_default_left_and_right
439
+ unless @skip_nested_set_callbacks
440
+ maxright = base_class.find(:first, scoped(:order => "#{right_column_name} DESC")).try(right_column_name) || 0
441
+ # adds the new node to the right of all existing nodes
442
+ self[left_column_name] = maxright + 1
443
+ self[right_column_name] = maxright + 2
444
+ end
445
+ end
446
+
447
+ # Prunes a branch off of the tree, shifting all of the elements on the right
448
+ # back to the left so the counts still work.
449
+ def destroy_descendants
450
+ return if right.nil? || left.nil? || skip_before_destroy
451
+
452
+ if acts_as_nested_set_options[:dependent] == :destroy
453
+ descendants.each do |model|
454
+ model.skip_before_destroy = true
455
+ model.destroy
456
+ end
457
+ else
458
+ base_class.delete_all scoped(left_column_name => { '$gt' => left }, right_column_name => { '$lt' => right })
459
+ end
460
+
461
+ # update lefts and rights for remaining nodes
462
+ diff = right - left + 1
463
+ base_class.find(:all, scoped(left_column_name => { '$gt' => right })).each do |node|
464
+ node.update_attributes left_column_name => node.left - diff
465
+ end
466
+ base_class.find(:all, scoped(right_column_name => { '$gt' => right })).each do |node|
467
+ node.update_attributes right_column_name => node.right - diff
468
+ end
469
+
470
+ # Don't allow multiple calls to destroy to corrupt the set
471
+ self.skip_before_destroy = true
472
+ end
473
+
474
+ # reload left, right, and parent
475
+ def reload_nested_set
476
+ doc = self.class.find(_id)
477
+ self.class.associations.each { |name, assoc| send(name).reset if respond_to?(name) }
478
+ [ left_column_name, right_column_name, parent_column_name ].each do |column|
479
+ send :"#{column}=", doc.send(column.to_sym)
480
+ end
481
+ self
482
+ end
483
+
484
+ def move_to(target, position)
485
+ raise ArgumentError, "You cannot move a new node" if self.new_record?
486
+ return if run_callbacks(:before_move) == false
487
+
488
+ if target.is_a? base_class
489
+ target.reload_nested_set
490
+ elsif position != :root
491
+ # load object if node is not an object
492
+ target = base_class.find(target, scoped)
493
+ end
494
+ self.reload_nested_set
495
+
496
+ unless position == :root || move_possible?(target)
497
+ raise ArgumentError, "Impossible move, target node cannot be inside moved tree."
498
+ end
499
+
500
+ bound = case position
501
+ when :child; target.send(right_column_name)
502
+ when :left; target.send(left_column_name)
503
+ when :right; target.send(right_column_name) + 1
504
+ when :root; 1
505
+ else raise ArgumentError, "Position should be :child, :left, :right or :root ('#{position}' received)."
506
+ end
507
+
508
+ if bound > self.send(right_column_name)
509
+ bound = bound - 1
510
+ other_bound = self.send(right_column_name) + 1
511
+ else
512
+ other_bound = self.send(left_column_name) - 1
513
+ end
514
+
515
+ # there would be no change
516
+ return if bound == self.send(right_column_name) || bound == self.send(left_column_name)
517
+
518
+ # we have defined the boundaries of two non-overlapping intervals,
519
+ # so sorting puts both the intervals and their boundaries in order
520
+ a, b, c, d = [self.send(left_column_name), self.send(right_column_name), bound, other_bound].sort
521
+
522
+ new_parent = case position
523
+ when :child; target.id
524
+ when :root; nil
525
+ else target.send(parent_column_name)
526
+ end
527
+
528
+ # base_class.collection.update({
529
+ # left_column_name => { '$gte' => a },
530
+ # left_column_name => { '$lte' => b }
531
+ # }, {
532
+ # '$inc' => { left_column_name => d - b }
533
+ # }, :multi => true)
534
+ # base_class.collection.update({
535
+ # left_column_name => { '$gte' => c },
536
+ # left_column_name => { '$lte' => d }
537
+ # }, {
538
+ # '$inc' => { left_column_name => a - c }
539
+ # }, :multi => true)
540
+ # base_class.collection.update({
541
+ # right_column_name => { '$gte' => a },
542
+ # right_column_name => { '$lte' => b }
543
+ # }, {
544
+ # '$inc' => { right_column_name => d - b }
545
+ # }, :multi => true)
546
+ # base_class.collection.update({
547
+ # right_column_name => { '$gte' => c },
548
+ # right_column_name => { '$lte' => d }
549
+ # }, {
550
+ # '$inc' => { right_column_name => a - c }
551
+ # }, :multi => true)
552
+ # base_class.collection.update({
553
+ # :_id => self.id
554
+ # }, {
555
+ # parent_column_name => new_parent
556
+ # })
557
+
558
+ to_update = {}
559
+
560
+ base_class.find(:all, scoped).each do |node|
561
+ to_update_this_node = {}
562
+ if (a..b).include? node.left
563
+ node.send :"#{left_column_name}=", node.left + d - b
564
+ elsif (c..d).include? node.left
565
+ node.send :"#{left_column_name}=", node.left + a - c
566
+ end
567
+ if (a..b).include? node.right
568
+ node.send :"#{right_column_name}=", node.right + d - b
569
+ elsif (c..d).include? node.right
570
+ node.send :"#{right_column_name}=", node.right + a - c
571
+ end
572
+ node.send :"#{parent_column_name}=", new_parent if self.id == node.id
573
+ node.save_without_nested_set_callbacks if node.changed?
574
+ end
575
+
576
+ # self.class.base_class.update_all([
577
+ # "#{quoted_left_column_name} = CASE " +
578
+ # "WHEN #{quoted_left_column_name} BETWEEN :a AND :b " +
579
+ # "THEN #{quoted_left_column_name} + :d - :b " +
580
+ # "WHEN #{quoted_left_column_name} BETWEEN :c AND :d " +
581
+ # "THEN #{quoted_left_column_name} + :a - :c " +
582
+ # "ELSE #{quoted_left_column_name} END, " +
583
+ # "#{quoted_right_column_name} = CASE " +
584
+ # "WHEN #{quoted_right_column_name} BETWEEN :a AND :b " +
585
+ # "THEN #{quoted_right_column_name} + :d - :b " +
586
+ # "WHEN #{quoted_right_column_name} BETWEEN :c AND :d " +
587
+ # "THEN #{quoted_right_column_name} + :a - :c " +
588
+ # "ELSE #{quoted_right_column_name} END, " +
589
+ # "#{quoted_parent_column_name} = CASE " +
590
+ # "WHEN id = :id THEN :new_parent " +
591
+ # "ELSE #{quoted_parent_column_name} END",
592
+ # {:a => a, :b => b, :c => c, :d => d, :id => self.id, :new_parent => new_parent}
593
+ # ], nested_set_scope.proxy_options[:conditions])
594
+
595
+ target.reload_nested_set if target
596
+ self.reload_nested_set
597
+ run_callbacks(:after_move)
598
+ end
599
+
600
+ def save_without_nested_set_callbacks
601
+ @skip_nested_set_callbacks = true
602
+ save!
603
+ @skip_nested_set_callbacks = false
604
+ end
605
+ end
606
+ end