module NoBrainer::Tree::Ordering

NoBrainer::Tree::Ordering

NoBrainer::Tree doesn’t order the tree by default. To enable ordering of children include both NoBrainer::Tree and NoBrainer::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 [NoBrainer::Criteria] NoBrainer criteria to retrieve the document’s ancestors

# File lib/nobrainer/tree/ordering.rb, line 53
def ancestors
  base_class.without_index.unscoped.where(:id.in => self.parent_ids).order_by(:depth => :asc)
end
at_bottom?() click to toggle source

Is this the lowest sibling?

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

# File lib/nobrainer/tree/ordering.rb, line 113
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/nobrainer/tree/ordering.rb, line 105
def at_top?
  higher_siblings.empty?
end
first_sibling_in_list() click to toggle source

Returns the highest sibling (could be self)

@return [NoBrainer::Document] The highest sibling

# File lib/nobrainer/tree/ordering.rb, line 97
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 [NoBrainer::Criteria] NoBrainer criteria to retrieve the document’s higher siblings

# File lib/nobrainer/tree/ordering.rb, line 71
def higher_siblings
  self.siblings.where(:position.lt => self.position)
end
inc(increments) click to toggle source
# File lib/nobrainer/tree/ordering.rb, line 45
def inc(increments)
  update(increments.symbolize_keys.map{ |field, value| { field => (self[field] || 0) + value } }.first)
end
last_sibling_in_list() click to toggle source

Returns the lowest sibling (could be self)

@return [NoBrainer::Document] The lowest sibling

# File lib/nobrainer/tree/ordering.rb, line 89
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 [NoBrainer::Criteria] NoBrainer criteria to retrieve the document’s lower siblings

# File lib/nobrainer/tree/ordering.rb, line 62
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 [NoBrainer::Tree] other document to move this document above

@return [undefined]

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

  if position > other.position
    new_position = other.position
    self.siblings_between(other).each{ |object| object.inc(:position => 1) }
    other.inc(:position => 1)
  else
    new_position = other.position - 1
    self.siblings_between(other).each{ |object| object.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 [NoBrainer::Tree] other document to move this document below

@return [undefined]

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

  if position > other.position
    new_position = other.position + 1
    self.siblings_between(other).each{ |object| object.inc(:position => 1) }
  else
    new_position = other.position
    self.siblings_between(other).each{ |object| object.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 [undefined]

# File lib/nobrainer/tree/ordering.rb, line 147
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 [undefined]

# File lib/nobrainer/tree/ordering.rb, line 130
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 [undefined]

# File lib/nobrainer/tree/ordering.rb, line 121
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 [undefined]

# File lib/nobrainer/tree/ordering.rb, line 139
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 [NoBrainer::Criteria] NoBrainer criteria to retrieve the documents between this and the other document

# File lib/nobrainer/tree/ordering.rb, line 80
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/nobrainer/tree/ordering.rb, line 227
def assign_default_position
  self.position = self.last_sibling_in_list.position + 1 rescue 0
end
assign_default_position?() click to toggle source
# File lib/nobrainer/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/nobrainer/tree/ordering.rb, line 206
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/nobrainer/tree/ordering.rb, line 212
def move_lower_siblings_up
  lower_siblings.each{ |object| object.inc(:position => -1) }
end
reposition_former_siblings() click to toggle source
# File lib/nobrainer/tree/ordering.rb, line 216
def reposition_former_siblings
  former_siblings = base_class.where(:parent_id => self.parent_id_was).
                               where(:position.gt => (self.position_was || 0)).
                               where(:id.ne => self.id)
  former_siblings.to_a.each{ |object| object.inc(:position => -1) }
end
sibling_reposition_required?() click to toggle source
# File lib/nobrainer/tree/ordering.rb, line 223
def sibling_reposition_required?
  parent_id_changed? && persisted?
end
switch_with_sibling_at_offset(offset) click to toggle source
# File lib/nobrainer/tree/ordering.rb, line 201
def switch_with_sibling_at_offset(offset)
  siblings.where(:position => self.position + offset).first.inc(:position => -offset)
  inc(:position => offset)
end