Module: SymetrieCom::Acts::NestedSet::InstanceMethods

Defined in:
lib/better_nested_set.rb

Overview

This module provides instance methods for an enhanced acts_as_nested_set mixin. Please see the README for background information, examples, and tips on usage.

Instance Method Summary collapse

Instance Method Details

#<=>(x) ⇒ Object

By default, records are compared and sorted using the left column.



450
451
452
# File 'lib/better_nested_set.rb', line 450

def <=>(x)
  self[left_col_name] <=> x[left_col_name]
end

#add_child(child) ⇒ Object

Deprecated. Adds a child to this object in the tree. If this object hasn’t been initialized, it gets set up as a root node.

This method exists only for compatibility and will be removed in future versions.



754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
# File 'lib/better_nested_set.rb', line 754

def add_child(child)
  transaction do
    self.reload; child.reload # for compatibility with old version
    # the old version allows records with nil values for lft and rgt
    unless self[left_col_name] && self[right_col_name]
      if child[left_col_name] || child[right_col_name]
        raise ActiveRecord::ActiveRecordError, "If parent lft or rgt are nil, you can't add a child with non-nil lft or rgt"
      end
      base_set_class.update_all("#{left_col_name} = CASE \
                                  WHEN id = #{self.id} \
                                    THEN 1 \
                                  WHEN id = #{child.id} \
                                    THEN 3 \
                                  ELSE #{left_col_name} END, \
                             #{right_col_name} = CASE \
                                  WHEN id = #{self.id} \
                                    THEN 2 \
                                  WHEN id = #{child.id} \
                                    THEN 4 \
                                 ELSE #{right_col_name} END",
                              scope_condition)
      self.reload; child.reload
    end
    unless child[left_col_name] && child[right_col_name]
      maxright = base_set_class.maximum(right_col_name, :conditions => scope_condition) || 0
      base_set_class.update_all("#{left_col_name} = CASE \
                                  WHEN id = #{child.id} \
                                    THEN #{maxright + 1} \
                                  ELSE #{left_col_name} END, \
                              #{right_col_name} = CASE \
                                  WHEN id = #{child.id} \
                                    THEN #{maxright + 2} \
                                  ELSE #{right_col_name} END",
                              scope_condition)
      child.reload
    end
    
    child.move_to_child_of(self)
    # self.reload ## even though move_to calls target.reload, at least one object in the tests was not reloading (near the end of test_common_usage)
  end
# self.reload
# child.reload
#
# if child.root?
#   raise ActiveRecord::ActiveRecordError, "Adding sub-tree isn\'t currently supported"
# else
#   if ( (self[left_col_name] == nil) || (self[right_col_name] == nil) )
#     # Looks like we're now the root node!  Woo
#     self[left_col_name] = 1
#     self[right_col_name] = 4
#     
#     # What do to do about validation?
#     return nil unless self.save
#     
#     child[parent_col_name] = self.id
#     child[left_col_name] = 2
#     child[right_col_name]= 3
#     return child.save
#   else
#     # OK, we need to add and shift everything else to the right
#     child[parent_col_name] = self.id
#     right_bound = self[right_col_name]
#     child[left_col_name] = right_bound
#     child[right_col_name] = right_bound + 1
#     self[right_col_name] += 2
#     self.class.transaction {
#       self.class.update_all( "#{left_col_name} = (#{left_col_name} + 2)",  "#{scope_condition} AND #{left_col_name} >= #{right_bound}" )
#       self.class.update_all( "#{right_col_name} = (#{right_col_name} + 2)",  "#{scope_condition} AND #{right_col_name} >= #{right_bound}" )
#       self.save
#       child.save
#     }
#   end
# end
end

#all_children(scope = {}) ⇒ Object

Returns all children and nested children. Pass :exclude => item, or id, or [items or id] to exclude one or more items and all of their descendants.



634
635
636
# File 'lib/better_nested_set.rb', line 634

def all_children(scope = {})
  full_set(scope) - [self]
end

#all_children_count(scope = nil) ⇒ Object

Returns the number of nested children of this object.



567
568
569
570
# File 'lib/better_nested_set.rb', line 567

def all_children_count(scope = nil)
  return all_children(scope).length if scope.is_a?(Hash)
  return (self[right_col_name] - self[left_col_name] - 1)/2
end

#all_children_through(other, scope = {}) ⇒ Object

All children until the other is reached - excluding self



679
680
681
# File 'lib/better_nested_set.rb', line 679

def all_children_through(other, scope = {})
  full_set_through(other, scope) - [self]
end

#ancestors(scope = {}) ⇒ Object

Returns an array of all parents, starting with the root.



489
490
491
# File 'lib/better_nested_set.rb', line 489

def ancestors(scope = {})
  self_and_ancestors(scope) - [self]
end

#ancestors_and_self_through(other, scope = {}) ⇒ Object

All nodes between two nodes, those nodes included in effect all ancestors until the other is reached



667
668
669
670
671
# File 'lib/better_nested_set.rb', line 667

def ancestors_and_self_through(other, scope = {})
  first, last = [self, other].sort
  self.class.find_in_nested_set(:all, { :conditions => "#{scope_condition} AND (#{last[left_col_name]} BETWEEN #{prefixed_left_col_name} AND #{prefixed_right_col_name}) AND #{prefixed_left_col_name} >= #{first[left_col_name]}", 
    :order => "#{prefixed_left_col_name}" }, scope)
end

#ancestors_through(other, scope = {}) ⇒ Object

Ancestors until the other is reached - excluding self



674
675
676
# File 'lib/better_nested_set.rb', line 674

def ancestors_through(other, scope = {})
  ancestors_and_self_through(other, scope) - [self]
end

#base_set_classObject

:nodoc:



412
413
414
# File 'lib/better_nested_set.rb', line 412

def base_set_class#:nodoc:
  acts_as_nested_set_options[:class] # for single-table inheritance
end

#check_full_treeObject

Checks the left/right indexes of the entire tree that this node belongs to, returning the number of records checked. Throws ActiveRecord::ActiveRecordError if it finds a problem. This method is needed because check_subtree alone cannot find gaps between virtual roots, orphaned nodes or endless loops.



717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
# File 'lib/better_nested_set.rb', line 717

def check_full_tree
  total_nodes = 0
  transaction do
    # virtual roots make this method more complex than it otherwise would be
    n = 1
    roots.each do |r| 
      raise ActiveRecord::ActiveRecordError, "Gaps between roots in the tree containing record ##{r.id}" if r[left_col_name] != n
      r.check_subtree
      n = r[right_col_name] + 1
    end
    total_nodes = roots.inject(0) {|sum, r| sum + r.all_children_count + 1 }
    unless base_set_class.count(:conditions => "#{scope_condition}") == total_nodes
      raise ActiveRecord::ActiveRecordError, "Orphaned nodes or endless loops in the tree containing record ##{self.id}"
    end
  end
  return total_nodes
end

#check_subtreeObject

Checks the left/right indexes of one node and all descendants. Throws ActiveRecord::ActiveRecordError if it finds a problem.



707
708
709
710
711
712
# File 'lib/better_nested_set.rb', line 707

def check_subtree
  transaction do
    self.reload
    check # this method is implemented via #check, so that we don't generate lots of unnecessary nested transactions
  end
end

#child?Boolean

Deprecated. Returns true if this is a child node

Returns:

  • (Boolean)


461
462
463
464
# File 'lib/better_nested_set.rb', line 461

def child?                          
  parent_id = self[parent_col_name]
  !(parent_id == 0 || parent_id.nil?) && (self[left_col_name] > 1) && (self[right_col_name] > self[left_col_name])
end

#child_by_id(id, scope = {}) ⇒ Object

Returns the child for the requested id within the scope of its children, otherwise nil



587
588
589
# File 'lib/better_nested_set.rb', line 587

def child_by_id(id, scope = {})
  children_by_id(id, scope).first
end

#child_of?(parent, scope = {}) ⇒ Boolean

Tests wether self is within scope of parent

Returns:

  • (Boolean)


615
616
617
618
619
620
621
# File 'lib/better_nested_set.rb', line 615

def child_of?(parent, scope = {})
  if !scope.empty? && parent.respond_to?(:child_by_id)
    parent.child_by_id(self.id, scope).is_a?(self.class)
  else
    parent.respond_to?(left_col_name) && self[left_col_name] > parent[left_col_name] && self[right_col_name] < parent[right_col_name]
  end
end

#children(scope = {}) ⇒ Object Also known as: direct_children

Returns this record’s immediate children.



643
644
645
# File 'lib/better_nested_set.rb', line 643

def children(scope = {})
  self.class.find_in_nested_set(:all, { :conditions => "#{scope_condition} AND #{prefixed_parent_col_name} = #{self.id}", :order => "#{prefixed_left_col_name}" }, scope)
end

#children?(scope = {}) ⇒ Boolean

Returns:

  • (Boolean)


647
648
649
# File 'lib/better_nested_set.rb', line 647

def children?(scope = {})
  children_count(scope) > 0
end

#children_by_id(*args) ⇒ Object

Returns a child collection for the requested ids within the scope of its children, otherwise empty array



592
593
594
595
596
597
598
# File 'lib/better_nested_set.rb', line 592

def children_by_id(*args)
  scope = args.last.is_a?(Hash) ? args.pop : {}
  ids = args.flatten.compact.uniq
  self.class.find_in_nested_set(:all, { 
    :conditions => ["#{scope_condition} AND (#{prefixed_left_col_name} BETWEEN #{self[left_col_name]} AND #{self[right_col_name]}) AND #{self.class.table_name}.#{self.class.primary_key} IN (?)", ids] 
  }, scope)
end

#children_count(scope = {}) ⇒ Object



638
639
640
# File 'lib/better_nested_set.rb', line 638

def children_count(scope= {})
  self.class.count_in_nested_set({ :conditions => "#{scope_condition} AND #{prefixed_parent_col_name} = #{self.id}" }, scope)
end

#destroy_descendantsObject

On destruction, delete all children and shift the lft/rgt values back to the left so the counts still work.



429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
# File 'lib/better_nested_set.rb', line 429

def destroy_descendants # already protected by a transaction within #destroy
  return if self[right_col_name].nil? || self[left_col_name].nil? || self.skip_before_destroy
  reloaded = self.reload rescue nil # in case a concurrent move has altered the indexes - rescue if non-existent
  return unless reloaded
  dif = self[right_col_name] - self[left_col_name] + 1
  if acts_as_nested_set_options[:dependent] == :delete_all
    base_set_class.delete_all( "#{scope_condition} AND (#{prefixed_left_col_name} BETWEEN #{self[left_col_name]} AND #{self[right_col_name]})" )          
  else 
    set = base_set_class.find(:all, :conditions => "#{scope_condition} AND (#{prefixed_left_col_name} BETWEEN #{self[left_col_name]} AND #{self[right_col_name]})", :order => "#{prefixed_right_col_name} DESC")     
    set.each { |child| child.skip_before_destroy = true; remove_descendant(child) } 
  end
  base_set_class.update_all("#{left_col_name} = CASE \
                              WHEN #{left_col_name} > #{self[right_col_name]} THEN (#{left_col_name} - #{dif}) \
                              ELSE #{left_col_name} END, \
                         #{right_col_name} = CASE \
                              WHEN #{right_col_name} > #{self[right_col_name]} THEN (#{right_col_name} - #{dif} ) \
                              ELSE #{right_col_name} END",
                         scope_condition)
end

#direct_child_by_id(id, scope = {}) ⇒ Object

Returns the child for the requested id within the scope of its immediate children, otherwise nil



601
602
603
# File 'lib/better_nested_set.rb', line 601

def direct_child_by_id(id, scope = {})
  direct_children_by_id(id, scope).first
end

#direct_child_of?(parent, scope = {}) ⇒ Boolean

Tests wether self is within immediate scope of parent

Returns:

  • (Boolean)


624
625
626
627
628
629
630
# File 'lib/better_nested_set.rb', line 624

def direct_child_of?(parent, scope = {})
  if !scope.empty? && parent.respond_to?(:direct_child_by_id)
    parent.direct_child_by_id(self.id, scope).is_a?(self.class)
  else
    parent.respond_to?(parent_col_name) && self[parent_col_name] == parent.id
  end
end

#direct_children_by_id(*args) ⇒ Object

Returns a child collection for the requested ids within the scope of its immediate children, otherwise empty array



606
607
608
609
610
611
612
# File 'lib/better_nested_set.rb', line 606

def direct_children_by_id(*args)
  scope = args.last.is_a?(Hash) ? args.pop : {}
  ids = args.flatten.compact.uniq
  self.class.find_in_nested_set(:all, { 
    :conditions => ["#{scope_condition} AND #{prefixed_parent_col_name} = #{self.id} AND #{self.class.table_name}.#{self.class.primary_key} IN (?)", ids]
  }, scope)          
end

#first_sibling(scope = {}) ⇒ Object

Returns first siblings amongst it’s siblings.



516
517
518
# File 'lib/better_nested_set.rb', line 516

def first_sibling(scope = {})
  self_and_siblings(scope.merge(:limit => 1, :order => "#{prefixed_left_col_name} ASC")).first
end

#first_sibling?(scope = {}) ⇒ Boolean Also known as: first?

Returns:

  • (Boolean)


520
521
522
# File 'lib/better_nested_set.rb', line 520

def first_sibling?(scope = {})
  self == first_sibling(scope)
end

#full_set(scope = {}) ⇒ Object

Returns itself and all nested children. Pass :exclude => item, or id, or [items or id] to exclude one or more items and all of their descendants.



574
575
576
577
578
579
580
581
582
583
584
# File 'lib/better_nested_set.rb', line 574

def full_set(scope = {})
  if exclude = scope.delete(:exclude)
    exclude_str = " AND NOT (#{base_set_class.sql_for(exclude)}) "
  elsif new_record? || self[right_col_name] - self[left_col_name] == 1
    return [self]
  end
  self.class.find_in_nested_set(:all, { 
    :order => "#{prefixed_left_col_name}",
    :conditions => "#{scope_condition} #{exclude_str} AND (#{prefixed_left_col_name} BETWEEN #{self[left_col_name]} AND #{self[right_col_name]})"
  }, scope)
end

#full_set_through(other, scope = {}) ⇒ Object

All children until the other is reached - including self



684
685
686
687
688
# File 'lib/better_nested_set.rb', line 684

def full_set_through(other, scope = {})
  first, last = [self, other].sort
  self.class.find_in_nested_set(:all,  
    { :conditions => "#{scope_condition} AND (#{prefixed_left_col_name} BETWEEN #{first[left_col_name]} AND #{first[right_col_name]}) AND #{prefixed_left_col_name} <= #{last[left_col_name]}", :order => "#{prefixed_left_col_name}" }, scope)
end

#insert_at(target, index = :last, scope = {}) ⇒ Object

Insert a node at a specific position among the children of target.



830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
# File 'lib/better_nested_set.rb', line 830

def insert_at(target, index = :last, scope = {})
  level_nodes = target.children(scope)
  current_index = level_nodes.index(self)
  last_index = level_nodes.length - 1 
  as_first = (index == :first)
  as_last  = (index == :last || (index.is_a?(Fixnum) && index > last_index))         
  index = 0 if as_first
  index = last_index if as_last
  if last_index < 0
    move_to_child_of(target)
  elsif index >= 0 && index <= last_index && level_nodes[index]            
    if as_last && index != current_index
      move_to_right_of(level_nodes[index])
    elsif (as_first || index == 0) && index != current_index
      move_to_left_of(level_nodes[index])
    elsif !current_index.nil? && index > current_index
      move_to_right_of(level_nodes[index])
    elsif !current_index.nil? && index < current_index
      move_to_left_of(level_nodes[index])
    elsif current_index.nil?
      move_to_left_of(level_nodes[index])
    end        
  end
end

#last_sibling(scope = {}) ⇒ Object

Returns last siblings amongst it’s siblings.



526
527
528
# File 'lib/better_nested_set.rb', line 526

def last_sibling(scope = {})
  self_and_siblings(scope.merge(:limit => 1, :order => "#{prefixed_left_col_name} DESC")).first
end

#last_sibling?(scope = {}) ⇒ Boolean Also known as: last?

Returns:

  • (Boolean)


530
531
532
# File 'lib/better_nested_set.rb', line 530

def last_sibling?(scope = {})
  self == last_sibling(scope)
end

#leaves(scope = {}) ⇒ Object

Returns this record’s terminal children (nodes without children).



655
656
657
658
# File 'lib/better_nested_set.rb', line 655

def leaves(scope = {})
  self.class.find_in_nested_set(:all, 
    { :conditions => "#{scope_condition} AND (#{prefixed_left_col_name} BETWEEN #{self[left_col_name]} AND #{self[right_col_name]}) AND #{prefixed_left_col_name} + 1 = #{prefixed_right_col_name}", :order => "#{prefixed_left_col_name}" }, scope)
end

#leaves_count(scope = {}) ⇒ Object

Returns the count of this record’s terminal children (nodes without children).



661
662
663
# File 'lib/better_nested_set.rb', line 661

def leaves_count(scope = {})
  self.class.count_in_nested_set({ :conditions => "#{scope_condition} AND (#{prefixed_left_col_name} BETWEEN #{self[left_col_name]} AND #{self[right_col_name]}) AND #{prefixed_left_col_name} + 1 = #{prefixed_right_col_name}" }, scope)
end

#left_col_nameObject

convenience methods to make the code more readable



393
394
395
# File 'lib/better_nested_set.rb', line 393

def left_col_name#:nodoc:
  self.class.left_col_name
end

#level(scope = {}) ⇒ Object

Returns the level of this object in the tree, root level being 0.



561
562
563
564
# File 'lib/better_nested_set.rb', line 561

def level(scope = {})
  return 0 if self[parent_col_name].nil?
  self.class.count_in_nested_set({ :conditions => "#{scope_condition} AND (#{self[left_col_name]} BETWEEN #{prefixed_left_col_name} AND #{prefixed_right_col_name})" }, scope) - 1
end

#move_higherObject

Moves a node one down amongst its siblings. Does nothing if it’s already the last sibling.



887
888
889
890
# File 'lib/better_nested_set.rb', line 887

def move_higher         
  prev_sib = previous_sibling
  move_to_left_of(prev_sib) if prev_sib
end

#move_lowerObject

Moves a node one up amongst its siblings. Does nothing if it’s already the first sibling.



880
881
882
883
# File 'lib/better_nested_set.rb', line 880

def move_lower
  next_sib = next_sibling
  move_to_right_of(next_sib) if next_sib
end

#move_to_bottomObject

Moves a node one to be the last amongst its siblings. Does nothing if it’s already the last sibling.



901
902
903
904
# File 'lib/better_nested_set.rb', line 901

def move_to_bottom
  last_sib = last_sibling
  move_to_right_of(last_sib) if last_sib && self != last_sib
end

#move_to_child_of(target) ⇒ Object

Make this node a child of target (you can pass an object or just an id). Unsaved changes in either object will be lost. Raises ActiveRecord::ActiveRecordError if it encounters a problem.



869
870
871
# File 'lib/better_nested_set.rb', line 869

def move_to_child_of(target)
  self.move_to target, :child
end

#move_to_left_of(target) ⇒ Object

Move this node to the left of target (you can pass an object or just an id). Unsaved changes in either object will be lost. Raises ActiveRecord::ActiveRecordError if it encounters a problem.



857
858
859
# File 'lib/better_nested_set.rb', line 857

def move_to_left_of(target)
  self.move_to target, :left
end

#move_to_position(index, scope = {}) ⇒ Object

Moves a node to a certain position amongst its siblings.



874
875
876
# File 'lib/better_nested_set.rb', line 874

def move_to_position(index, scope = {})
  insert_at(self.parent, index, scope)
end

#move_to_right_of(target) ⇒ Object

Move this node to the right of target (you can pass an object or just an id). Unsaved changes in either object will be lost. Raises ActiveRecord::ActiveRecordError if it encounters a problem.



863
864
865
# File 'lib/better_nested_set.rb', line 863

def move_to_right_of(target)
  self.move_to target, :right
end

#move_to_topObject

Moves a node one to be the first amongst its siblings. Does nothing if it’s already the first sibling.



894
895
896
897
# File 'lib/better_nested_set.rb', line 894

def move_to_top
  first_sib = first_sibling
  move_to_left_of(first_sib) if first_sib && self != first_sib
end

#next_sibling(num = 1, scope = {}) ⇒ Object Also known as: lower_item

Returns next sibling of node or nil if there is none.



544
545
546
547
548
# File 'lib/better_nested_set.rb', line 544

def next_sibling(num = 1, scope = {})
  scope[:limit] = num
  siblings = next_siblings(scope)
  num == 1 ? siblings.first : siblings
end

#next_siblings(scope = {}) ⇒ Object

Returns all siblings to the right of self, in ascending order, so the first sibling is the one closest to the right of self



510
511
512
513
# File 'lib/better_nested_set.rb', line 510

def next_siblings(scope = {})
  self.class.find_in_nested_set(:all, 
    { :conditions => ["#{scope_condition} AND #{sibling_condition} AND #{self.class.table_name}.id != ? AND #{prefixed_left_col_name} > ?", self.id, self[right_col_name]], :order => "#{prefixed_left_col_name} ASC"}, scope)
end

#parentObject

Returns this record’s parent.



484
485
486
# File 'lib/better_nested_set.rb', line 484

def parent
  self.class.find_in_nested_set(self[parent_col_name]) if self[parent_col_name]
end

#parent_col_nameObject Also known as: parent_column

:nodoc:



405
406
407
# File 'lib/better_nested_set.rb', line 405

def parent_col_name#:nodoc:
  self.class.parent_col_name
end

#prefixed_left_col_nameObject

:nodoc:



396
397
398
# File 'lib/better_nested_set.rb', line 396

def prefixed_left_col_name#:nodoc:
  self.class.prefixed_left_col_name
end

#prefixed_parent_col_nameObject

:nodoc:



408
409
410
# File 'lib/better_nested_set.rb', line 408

def prefixed_parent_col_name#:nodoc:
  self.class.prefixed_parent_col_name
end

#prefixed_right_col_nameObject

:nodoc:



402
403
404
# File 'lib/better_nested_set.rb', line 402

def prefixed_right_col_name#:nodoc:
  self.class.prefixed_right_col_name
end

#previous_sibling(num = 1, scope = {}) ⇒ Object Also known as: higher_item

Returns previous sibling of node or nil if there is none.



536
537
538
539
540
# File 'lib/better_nested_set.rb', line 536

def previous_sibling(num = 1, scope = {})
  scope[:limit] = num
  siblings = previous_siblings(scope)
  num == 1 ? siblings.first : siblings
end

#previous_siblings(scope = {}) ⇒ Object

Returns all siblings to the left of self, in descending order, so the first sibling is the one closest to the left of self



504
505
506
507
# File 'lib/better_nested_set.rb', line 504

def previous_siblings(scope = {})
  self.class.find_in_nested_set(:all, 
    { :conditions => ["#{scope_condition} AND #{sibling_condition} AND #{self.class.table_name}.id != ? AND #{prefixed_right_col_name} < ?", self.id, self[left_col_name]], :order => "#{prefixed_left_col_name} DESC" }, scope)
end

#renumber_full_treeObject

Re-calculate the left/right values of all nodes in this record’s tree. Can be used to convert an ordinary tree into a nested set.



736
737
738
739
740
741
742
743
744
745
746
747
748
# File 'lib/better_nested_set.rb', line 736

def renumber_full_tree
  indexes = []
  n = 1
  transaction do
    for r in roots # because we may have virtual roots
      n = 1 + r.calc_numbers(n, indexes)
    end
    for i in indexes
      base_set_class.update_all("#{left_col_name} = #{i[:lft]}, #{right_col_name} = #{i[:rgt]}", "#{self.class.primary_key} = #{i[:id]}")
    end
  end
  ## reload?
end

#reorder_children(*ids) ⇒ Object

Reorder children according to an array of ids



913
914
915
916
917
918
919
920
921
922
# File 'lib/better_nested_set.rb', line 913

def reorder_children(*ids)
  transaction do
    ordered_ids = ids.flatten.uniq
    current_children = children({ :conditions => { :id => ordered_ids } })
    current_children_ids = current_children.map(&:id)
    ordered_ids = ordered_ids & current_children_ids
    return [] unless ordered_ids.length > 1 && ordered_ids != current_children_ids
    perform_reorder_of_children(ordered_ids, current_children)
  end         
end

#right_col_nameObject

:nodoc:



399
400
401
# File 'lib/better_nested_set.rb', line 399

def right_col_name#:nodoc:
 self.class.right_col_name
end

#root(scope = {}) ⇒ Object

Returns this record’s root ancestor.



472
473
474
475
476
# File 'lib/better_nested_set.rb', line 472

def root(scope = {})
  # the BETWEEN clause is needed to ensure we get the right virtual root, if using those
  self.class.find_in_nested_set(:first, { :conditions => "#{scope_condition} \
    AND (#{prefixed_parent_col_name} IS NULL OR #{prefixed_parent_col_name} = 0) AND (#{self[left_col_name]} BETWEEN #{prefixed_left_col_name} AND #{prefixed_right_col_name})" }, scope)
end

#root?Boolean

Deprecated. Returns true if this is a root node.

Returns:

  • (Boolean)


455
456
457
458
# File 'lib/better_nested_set.rb', line 455

def root?
  parent_id = self[parent_col_name]
  (parent_id == 0 || parent_id.nil?) && self[right_col_name] && self[left_col_name] && (self[right_col_name] > self[left_col_name])
end

#roots(scope = {}) ⇒ Object

Returns the root or virtual roots of this record’s tree (a tree cannot have more than one real root). See the explanation of virtual roots in the README.



479
480
481
# File 'lib/better_nested_set.rb', line 479

def roots(scope = {})
  self.class.find_in_nested_set(:all, { :conditions => "#{scope_condition} AND (#{prefixed_parent_col_name} IS NULL OR #{prefixed_parent_col_name} = 0)", :order => "#{prefixed_left_col_name}" }, scope)
end

#self_and_ancestors(scope = {}) ⇒ Object

Returns an array of all parents plus self, starting with the root.



494
495
496
# File 'lib/better_nested_set.rb', line 494

def self_and_ancestors(scope = {})
  self.class.find_in_nested_set(:all, { :conditions => "#{scope_condition} AND (#{self[left_col_name]} BETWEEN #{prefixed_left_col_name} AND #{prefixed_right_col_name})", :order => "#{prefixed_left_col_name}" }, scope)
end

#self_and_siblings(scope = {}) ⇒ Object

Returns all the children of this node’s parent, including self.



552
553
554
555
556
557
558
# File 'lib/better_nested_set.rb', line 552

def self_and_siblings(scope = {})
  if self[parent_col_name].nil? || self[parent_col_name].zero?
    [self]
  else
    self.class.find_in_nested_set(:all, { :conditions => "#{scope_condition} AND #{sibling_condition}", :order => "#{prefixed_left_col_name}" }, scope)
  end
end

#self_and_siblings_through(other, scope = {}) ⇒ Object

All siblings until the other is reached - including self



691
692
693
694
695
696
697
698
# File 'lib/better_nested_set.rb', line 691

def self_and_siblings_through(other, scope = {})
  if self[parent_col_name].nil? || self[parent_col_name].zero?
    [self]
  else
    first, last = [self, other].sort
    self.class.find_in_nested_set(:all, { :conditions => "#{scope_condition} AND #{sibling_condition} AND (#{prefixed_left_col_name} BETWEEN #{first[left_col_name]} AND #{last[right_col_name]})", :order => "#{prefixed_left_col_name}" }, scope)
  end
end

#set_left_rightObject

On creation, automatically add the new node to the right of all existing nodes in this tree.



422
423
424
425
426
# File 'lib/better_nested_set.rb', line 422

def set_left_right # already protected by a transaction within #create
  maxright = base_set_class.maximum(right_col_name, :conditions => scope_condition) || 0
  self[left_col_name] = maxright+1
  self[right_col_name] = maxright+2
end

#sibling_conditionObject

This takes care of valid queries when called on a root node



417
418
419
# File 'lib/better_nested_set.rb', line 417

def sibling_condition
  self[parent_col_name] ? "#{prefixed_parent_col_name} = #{self[parent_col_name]}" : "(#{prefixed_parent_col_name} IS NULL OR #{prefixed_parent_col_name} = 0)"
end

#siblings(scope = {}) ⇒ Object

Returns all the children of this node’s parent, except self.



499
500
501
# File 'lib/better_nested_set.rb', line 499

def siblings(scope = {})
  self_and_siblings(scope) - [self]
end

#siblings_through(other, scope = {}) ⇒ Object

All siblings until the other is reached - excluding self



701
702
703
# File 'lib/better_nested_set.rb', line 701

def siblings_through(other, scope = {})
  self_and_siblings_through(other, scope) - [self]
end

#swap(target, transact = true) ⇒ Object

Swaps the position of two sibling nodes preserving a sibling’s descendants. The current implementation only works amongst siblings.



908
909
910
# File 'lib/better_nested_set.rb', line 908

def swap(target, transact = true)
  move_to(target, :swap, transact)     
end

#unknown?Boolean

Deprecated. Returns true if we have no idea what this is

Returns:

  • (Boolean)


467
468
469
# File 'lib/better_nested_set.rb', line 467

def unknown?
  !root? && !child?
end