module Mongoid::Tree::Ordering

Mongoid::Tree::Ordering

Mongoid::Tree doesn’t order the tree by default. To enable ordering of children include both Mongoid::Tree and Mongoid::Tree::Ordering into your document.

Utility methods

This module adds methods to get related siblings depending on their position:

node.lower_siblings
node.higher_siblings
node.first_sibling_in_list
node.last_sibling_in_list

There are several methods to move nodes around in the list:

node.move_up
node.move_down
node.move_to_top
node.move_to_bottom
node.move_above(other)
node.move_below(other)

Additionally there are some methods to check aspects of the document in the list of children:

node.at_top?
node.at_bottom?

Public Instance Methods

ancestors() click to toggle source

Returns a chainable criteria for this document’s ancestors

@return [Mongoid::Criteria] Mongoid criteria to retrieve the document’s ancestors

Calls superclass method
# File lib/mongoid/tree/ordering.rb, line 49
def ancestors
  base_class.unscoped { super }
end
at_bottom?() click to toggle source

Is this the lowest sibling?

@return [Boolean] Whether the document is the lowest sibling

# File lib/mongoid/tree/ordering.rb, line 109
def at_bottom?
  lower_siblings.empty?
end
at_top?() click to toggle source

Is this the highest sibling?

@return [Boolean] Whether the document is the highest sibling

# File lib/mongoid/tree/ordering.rb, line 101
def at_top?
  higher_siblings.empty?
end
first_sibling_in_list() click to toggle source

Returns the highest sibling (could be self)

@return [Mongoid::Document] The highest sibling

# File lib/mongoid/tree/ordering.rb, line 93
def first_sibling_in_list
  siblings_and_self.first
end
higher_siblings() click to toggle source

Returns siblings above the current document. Siblings with a position lower than this document’s position.

@return [Mongoid::Criteria] Mongoid criteria to retrieve the document’s higher siblings

# File lib/mongoid/tree/ordering.rb, line 67
def higher_siblings
  self.siblings.where(:position.lt => self.position)
end
last_sibling_in_list() click to toggle source

Returns the lowest sibling (could be self)

@return [Mongoid::Document] The lowest sibling

# File lib/mongoid/tree/ordering.rb, line 85
def last_sibling_in_list
  siblings_and_self.last
end
lower_siblings() click to toggle source

Returns siblings below the current document. Siblings with a position greater than this document’s position.

@return [Mongoid::Criteria] Mongoid criteria to retrieve the document’s lower siblings

# File lib/mongoid/tree/ordering.rb, line 58
def lower_siblings
  self.siblings.where(:position.gt => self.position)
end
move_above(other) click to toggle source

Move this node above the specified node

This method changes the node’s parent if nescessary.

@param [Mongoid::Tree] other document to move this document above

@return [void]

# File lib/mongoid/tree/ordering.rb, line 155
def move_above(other)
  ensure_to_be_sibling_of(other)

  if position > other.position
    new_position = other.position
    self.siblings_between(other).inc(position: 1)
    other.inc(position: 1)
  else
    new_position = other.position - 1
    self.siblings_between(other).inc(position: -1)
  end

  self.position = new_position
  save!
end
move_below(other) click to toggle source

Move this node below the specified node

This method changes the node’s parent if nescessary.

@param [Mongoid::Tree] other document to move this document below

@return [void]

# File lib/mongoid/tree/ordering.rb, line 179
def move_below(other)
  ensure_to_be_sibling_of(other)

  if position > other.position
    new_position = other.position + 1
    self.siblings_between(other).inc(position: 1)
  else
    new_position = other.position
    self.siblings_between(other).inc(position: -1)
    other.inc(position: -1)
  end

  self.position = new_position
  save!
end
move_down() click to toggle source

Move this node one position down

@return [void]

# File lib/mongoid/tree/ordering.rb, line 143
def move_down
  switch_with_sibling_at_offset(1) unless at_bottom?
end
move_to_bottom() click to toggle source

Move this node below all its siblings

@return [void]

# File lib/mongoid/tree/ordering.rb, line 126
def move_to_bottom
  return true if at_bottom?
  move_below(last_sibling_in_list)
end
move_to_top() click to toggle source

Move this node above all its siblings

@return [void]

# File lib/mongoid/tree/ordering.rb, line 117
def move_to_top
  return true if at_top?
  move_above(first_sibling_in_list)
end
move_up() click to toggle source

Move this node one position up

@return [void]

# File lib/mongoid/tree/ordering.rb, line 135
def move_up
  switch_with_sibling_at_offset(-1) unless at_top?
end
siblings_between(other) click to toggle source

Returns siblings between the current document and the other document Siblings with a position between this document’s position and the other document’s position.

@return [Mongoid::Criteria] Mongoid criteria to retrieve the documents between this and the other document

# File lib/mongoid/tree/ordering.rb, line 76
def siblings_between(other)
  range = [self.position, other.position].sort
  self.siblings.where(:position.gt => range.first, :position.lt => range.last)
end

Private Instance Methods

assign_default_position() click to toggle source
# File lib/mongoid/tree/ordering.rb, line 223
def assign_default_position
  self.position = if self.siblings.where(:position.ne => nil).any?
    self.last_sibling_in_list.position + 1
  else
    0
  end
end
assign_default_position?() click to toggle source
# File lib/mongoid/tree/ordering.rb, line 231
def assign_default_position?
  self.position.nil? || self.parent_id_changed?
end
ensure_to_be_sibling_of(other) click to toggle source
# File lib/mongoid/tree/ordering.rb, line 202
def ensure_to_be_sibling_of(other)
  return if sibling_of?(other)
  self.parent_id = other.parent_id
  save!
end
move_lower_siblings_up() click to toggle source
# File lib/mongoid/tree/ordering.rb, line 208
def move_lower_siblings_up
  lower_siblings.inc(position: -1)
end
reposition_former_siblings() click to toggle source
# File lib/mongoid/tree/ordering.rb, line 212
def reposition_former_siblings
  former_siblings = base_class.where(:parent_id => attribute_was('parent_id')).
                               and(:position.gt => (attribute_was('position') || 0)).
                               excludes(:id => self.id)
  former_siblings.inc(position:  -1)
end
sibling_reposition_required?() click to toggle source
# File lib/mongoid/tree/ordering.rb, line 219
def sibling_reposition_required?
  parent_id_changed? && persisted?
end
switch_with_sibling_at_offset(offset) click to toggle source
# File lib/mongoid/tree/ordering.rb, line 197
def switch_with_sibling_at_offset(offset)
  siblings.where(position: self.position + offset).first.inc(position: -offset)
  inc(position: offset)
end