DataMapper::Is::NestedSet::InstanceMethods

Public Instance Methods

ancestor() click to toggle source

get the parent of this node. Same as parent, but finds it from lft/rgt instead of parent-key

@return <Resource, NilClass> returns the parent-object, or nil if this is root/detached

# File lib/dm-is-nested_set/is/nested_set.rb, line 282
def ancestor
  ancestors.last
end
ancestors() click to toggle source

get all ancestors of this node

@return <Collection> collection of all parents, with root as first item @see self_and_ancestors

# File lib/dm-is-nested_set/is/nested_set.rb, line 273
def ancestors
  nested_set.all(:lft.lt => lft, :rgt.gt => rgt)
  #self_and_ancestors.reject{|r| r.key == key } # because identitymap is not used in console
end
descendants() click to toggle source

get all descendants of this node

@return <Collection> flat collection, sorted according to nested_set positions @see self_and_descendants

# File lib/dm-is-nested_set/is/nested_set.rb, line 316
def descendants
  # TODO add argument for returning as a nested array.
  # TODO supply filtering-option?
  nested_set.all(:lft => (lft + 1)..(rgt - 1))
end
leaf?() click to toggle source

check if this node is a leaf (does not have subnodes). use this instead ofdescendants.empty?

# File lib/dm-is-nested_set/is/nested_set.rb, line 334
def leaf?
  rgt - lft == 1
end
leaves() click to toggle source

get all descendants of this node that does not have any children

@return <Collection>

# File lib/dm-is-nested_set/is/nested_set.rb, line 326
def leaves
  # TODO supply filtering-option?
  nested_set.all(:lft => (lft + 1)..rgt, :conditions => [ 'rgt = lft + ?',  1 ])
end
left_sibling() click to toggle source

get sibling to the left of/above this node in the nested tree

@return <Resource, NilClass> the resource to the left, or nil if self is leftmost @see self_and_siblings

# File lib/dm-is-nested_set/is/nested_set.rb, line 362
def left_sibling
  self_and_siblings.detect { |v| v.rgt == lft - 1 }
end
level() click to toggle source

get the level of this node, where 0 is root. temporary solution

@return <Integer>

# File lib/dm-is-nested_set/is/nested_set.rb, line 255
def level
  # TODO make a level-property that is cached and intelligently adjusted when saving objects
  ancestors.length
end
move(vector) click to toggle source

move self / node to a position in the set. position can only be changed through this

@example [Usage]

* node.move :higher           # moves node higher unless it is at the top of parent
* node.move :lower            # moves node lower unless it is at the bottom of parent
* node.move :below => other   # moves this node below other resource in the set
* node.move :into => other    # same as setting a parent-relationship

@param vector <Symbol, Hash> A symbol, or a key-value pair that describes the requested movement

@option :higher<Symbol> move node higher @option :highest<Symbol> move node to the top of the list (within its parent) @option :lower<Symbol> move node lower @option :lowest<Symbol> move node to the bottom of the list (within its parent) @option :indent<Symbol> move node into sibling above @option :outdent<Symbol> move node out below its current parent @option :into<Resource> move node into another node @option :above<Resource> move node above other node @option :below<Resource> move node below other node @option :to<Integer> move node to a specific location in the nested set

@return <FalseClass> returns false if it cannot move to the position, or if it is already there @raise <RecursiveNestingError> if node is asked to position itself into one of its descendants @raise <UnableToPositionError> if node is unable to calculate a new position for the element @see move_without_saving

# File lib/dm-is-nested_set/is/nested_set.rb, line 168
def move(vector)
  move_without_saving(vector) && save
end
move_without_saving(vector) click to toggle source

@see move

# File lib/dm-is-nested_set/is/nested_set.rb, line 174
def move_without_saving(vector)
  if vector.kind_of?(Hash)
    action, object = vector.keys[0], vector.values[0]
  else
    action = vector
  end

  changed_scope = nested_set_scope != original_nested_set_scope

  position = case action
    when :higher  then left_sibling  ? left_sibling.lft      : nil  # : "already at the top"
    when :highest then ancestor      ? ancestor.lft + 1      : nil  # : "is root, or has no parent"
    when :lower   then right_sibling ? right_sibling.rgt + 1 : nil  # : "already at the bottom"
    when :lowest  then ancestor      ? ancestor.rgt          : nil  # : "is root, or has no parent"
    when :indent  then left_sibling  ? left_sibling.rgt      : nil  # : "cannot find a sibling to indent into"
    when :outdent then ancestor      ? ancestor.rgt + 1      : nil  # : "is root, or has no parent"
    when :into    then object        ? object.rgt            : nil  # : "supply an object"
    when :above   then object        ? object.lft            : nil  # : "supply an object"
    when :below   then object        ? object.rgt + 1        : nil  # : "supply an object"
    when :to      then object        ? object.to_i           : nil  # : "supply a number"
  end

  ##
  # raising an error whenever it couldnt move seems a bit harsh. want to return self for nesting.
  # if anyone has a good idea about how it should react when it cant set a valid position,
  # don't hesitate to find me in #datamapper, or send me an email at sindre -a- identu -dot- no
  #
  # raise UnableToPositionError unless position.is_a?(Integer) && position > 0
  return false if !position || position < 1
  # return false if you are trying to move this into another scope
  return false if [ :into, :above, :below ].include?(action) && nested_set_scope != object.nested_set_scope
  # if node is already in the requested position
  if lft == position || rgt == position - 1
    self.parent = ancestor # must set this again, because it might have been changed by the user before move.
    return false
  end

  ##
  # if this node is already positioned we need to move it, and close the gap it leaves behind etc
  # otherwise we only need to open a gap in the set, and smash that buggar in
  #
  if lft && rgt
    # raise exception if node is trying to move into one of its descendants (infinite loop, spacetime will warp)
    raise RecursiveNestingError if position > lft && position < rgt
    # find out how wide this node is, as we need to make a gap large enough for it to fit in
    gap = rgt - lft + 1

    # make a gap at position, that is as wide as this node
    model.base_model.adjust_gap!(nested_set, position - 1, gap)

    eager_props = model.properties.values_at(:lft, :rgt)

    # FIXME don't use @api private
    # offset this node (and all its descendants) to the right position
    eager_load(eager_props)

    old_position = lft
    offset = position - old_position

    nested_set.all(:rgt => lft..rgt).adjust!({ :lft => offset, :rgt => offset }, true)

    # close the gap this movement left behind.
    model.base_model.adjust_gap!(nested_set, old_position, -gap)

    # FIXME don't use @api private
    eager_load(eager_props)

  else
    # make a gap where the new node can be inserted
    model.base_model.adjust_gap!(nested_set, position - 1, 2)
    # set the position fields
    self.lft, self.rgt = position, position + 1
  end

  self.parent = ancestor
end
nested_set() click to toggle source

the whole nested set this node belongs to. served flat like a pancake!

# File lib/dm-is-nested_set/is/nested_set.rb, line 137
def nested_set
  # TODO add option for serving it as a nested array
  model.base_model.all(nested_set_scope.merge(:order => [ :lft ]))
end
nested_set_scope() click to toggle source

@private

# File lib/dm-is-nested_set/is/nested_set.rb, line 119
def nested_set_scope
  Hash[ model.base_model.nested_set_scope.map { |p| [ p, attribute_get(p) ] } ]
end
original_nested_set_scope() click to toggle source

@private

# File lib/dm-is-nested_set/is/nested_set.rb, line 126
def original_nested_set_scope
  # TODO commit
  pairs = model.base_model.nested_set_scope.map do |p|
    [ p, (property = properties[p]) && original_attributes.key?(property) ? original_attributes[property] : attribute_get(p) ]
  end
  Hash[ pairs ]
end
right_sibling() click to toggle source

get sibling to the right of/above this node in the nested tree

@return <Resource, NilClass> the resource to the right, or nil if self is rightmost @see self_and_siblings

# File lib/dm-is-nested_set/is/nested_set.rb, line 371
def right_sibling
  self_and_siblings.detect { |v| v.lft == rgt + 1 }
end
root() click to toggle source

get the root this node belongs to. this will atm always be the same as Resource.root, but has a meaning when scoped sets is implemented

@return <Resource, NilClass>

# File lib/dm-is-nested_set/is/nested_set.rb, line 291
def root
  nested_set.first
end
root?() click to toggle source

check if this node is a root

# File lib/dm-is-nested_set/is/nested_set.rb, line 298
def root?
  !parent && !new?
end
self_and_ancestors() click to toggle source

get all ancestors of this node, up to (and including) self

@return <Collection>

# File lib/dm-is-nested_set/is/nested_set.rb, line 264
def self_and_ancestors
  nested_set.all(:lft.lte => lft, :rgt.gte => rgt)
end
self_and_descendants() click to toggle source

get all descendants of this node, including self

@return <Collection> flat collection, sorted according to nested_set positions

# File lib/dm-is-nested_set/is/nested_set.rb, line 306
def self_and_descendants
  # TODO supply filtering-option?
  nested_set.all(:lft => lft..rgt)
end
self_and_siblings() click to toggle source

get all siblings of this node, and include self

@return <Collection>

# File lib/dm-is-nested_set/is/nested_set.rb, line 342
def self_and_siblings
  parent ? parent.children : [ self ]
end
siblings() click to toggle source

get all siblings of this node

@return <Collection> @see self_and_siblings

# File lib/dm-is-nested_set/is/nested_set.rb, line 351
def siblings
  # TODO find a way to return this as a collection?
  # TODO supply filtering-option?
  self_and_siblings.reject { |r| r.key == key } # because identitymap is not used in console
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.