Skip to content

Instantly share code, notes, and snippets.

@zerowidth
Created January 19, 2010 04:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zerowidth/280663 to your computer and use it in GitHub Desktop.
Save zerowidth/280663 to your computer and use it in GitHub Desktop.
class ParentDocument
include MongoMapper::Document
# parent document has ... well, whatever.
timestamps!
many :nodes do
# root documents only
def roots
load_target
target.select { |n| n.parent_id.nil? }
end
# move nodes around in the tree.
#
# To insert a node into the tree, insert directly into the
# nodes collection, e.g. #<<
#
# direction options are:
# :into, where # insert at end, nil means top-level
# :before, where # move before the node
# :after, where # move after the node
def move(node, direction, where)
node = object_from_id(node)
where = object_from_id(where)
case direction
when :into
move_into(node, where)
when :before
move_before(node, where)
when :after
move_after(node, where)
else
raise ArgumentError, "unknown direction #{direction}"
end
end
def remove(node)
node = object_from_id(node)
# dup because array is modified in-place when removing things from parent
node.child_ids.dup.each { |cid| remove(cid) }
if parent = find(node.parent_id)
parent.child_ids.delete(node.id)
end
target.delete(node)
end
protected
def object_from_id(node)
if node.kind_of?(String) || node.kind_of?(Mongo::ObjectID)
find(node)
else
node
end
end
def move_into(node, where)
# remove from current parent
if parent = find(node.parent_id)
parent.child_ids.delete(node.id)
end
# insert into new parent
if where
node.parent_id = where.id
where.child_ids << node.id
else
node.parent_id = nil
target << target.delete(node) # move it to the end
end
end
def move_before(node, where)
# remove from current parent
if parent = find(node.parent_id)
parent.child_ids.delete(node.id)
else
target.delete(node)
end
# insert into new parent before node
if parent = find(where.parent_id)
parent.child_ids.insert(parent.child_ids.index(where.id), node.id)
else
target.delete(node)
target.insert(target.index(where), node)
end
node.parent_id = where.parent_id
end
def move_after(node, where)
# remove from current parent
if parent = find(node.parent_id)
parent.child_ids.delete(node.id)
else
target.delete(node)
end
# insert into new parent after node
if parent = find(where.parent_id)
parent.child_ids.insert(parent.child_ids.index(where.id) + 1, node.id)
else
target.delete(node)
target.insert(target.index(where) + 1, node)
end
node.parent_id = where.parent_id
end
end
end
class Node
include MongoMapper::EmbeddedDocument
key :name
key :parent_id, Mongo::ObjectID
key :child_ids, Array
# convenience methods on the individual nodes
def parent
filters.find(parent_id)
end
def parents
if parent
list = []
p = self
while p = p.parent
list << p
end
list
else
[]
end
end
def children
child_ids.map { |id| nodes.find(id) }
end
# proxy back to the collection
def move(direction, where)
nodes.move(self, direction, where)
end
def remove
nodes.remove(self)
end
protected
def nodes
_root_document.nodes
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment