Created
January 19, 2010 04:14
-
-
Save zerowidth/280663 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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