Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Removed some whitespace

  • Loading branch information...
commit 5cdd156958c16cb7d40317679cb46c462b121032 1 parent f967c41
@benedikt authored
View
64 lib/mongoid/tree.rb
@@ -4,18 +4,18 @@ module Mongoid # :nodoc:
##
# = Mongoid::Tree
#
- # This module extends any Mongoid document with tree functionality.
- #
+ # This module extends any Mongoid document with tree functionality.
+ #
# == Usage
#
# Simply include the module in any Mongoid document:
- #
+ #
# class Node
# include Mongoid::Document
# include Mongoid::Tree
# end
#
- # === Using the tree structure
+ # === Using the tree structure
#
# Each document references many children. You can access them using the <tt>#children</tt> method.
#
@@ -29,129 +29,129 @@ module Mongoid # :nodoc:
# node.parent # => nil
# node.children.create
# node.children.first.parent # => node
- #
+ #
module Tree
extend ActiveSupport::Concern
include Traversal
-
+
included do
references_many :children, :class_name => self.name, :foreign_key => :parent_id, :inverse_of => :parent
referenced_in :parent, :class_name => self.name, :inverse_of => :children
-
+
field :parent_ids, :type => Array, :default => []
-
+
set_callback :validation, :before, :rearrange
set_callback :save, :after, :rearrange_children, :if => :rearrange_children?
end
-
+
##
# :method: children
# Returns a list of the document's children. It's a <tt>references_many</tt> association.
# (Generated by Mongoid)
-
+
##
# :method: parent
# Returns the document's parent (unless it's a root document). It's a <tt>referenced_in</tt> association.
# (Generated by Mongoid)
-
+
##
# :method: parent_ids
# Returns a list of the document's parent_ids, starting with the root node.
# (Generated by Mongoid)
-
+
##
- # Is this document a root node (has no parent)?
+ # Is this document a root node (has no parent)?
def root?
parent_id.nil?
end
-
+
##
# Is this document a leaf node (has no children)?
def leaf?
children.empty?
end
-
+
##
# Returns this document's root node
def root
self.class.find(parent_ids.first)
end
-
+
##
# Returns this document's ancestors
def ancestors
self.class.find(:conditions => { :_id.in => parent_ids })
end
-
+
##
# Returns this document's ancestors and itself
def ancestors_and_self
ancestors + [self]
end
-
+
##
# Is this document an ancestor of the other document?
def ancestor_of?(other)
other.parent_ids.include?(self.id)
- end
-
+ end
+
##
# Returns this document's descendants
def descendants
self.class.find(:conditions => { :parent_ids => self.id })
end
-
+
##
# Returns this document's descendants and itself
def descendants_and_self
[self] + descendants
end
-
+
##
# Is this document a descendant of the other document?
def descendant_of?(other)
self.parent_ids.include?(other.id)
end
-
+
##
# Returns this document's siblings
def siblings
siblings_and_self - [self]
end
-
- ##
+
+ ##
# Returns this document's siblings and itself
def siblings_and_self
self.class.find(:conditions => { :parent_id => self.parent_id })
end
-
+
##
# Forces rearranging of all children after next save
def rearrange_children!
@rearrange_children = true
end
-
+
##
# Will the children be rearranged after next save?
def rearrange_children?
!!@rearrange_children
end
-
+
private
-
+
def rearrange
if self.parent_id
self.parent_ids = self.class.find(self.parent_id).parent_ids + [self.parent_id]
end
-
+
rearrange_children! if self.parent_ids_changed?
return true
end
-
+
def rearrange_children
@rearrange_children = false
self.children.find(:all).each { |c| c.save }
end
end
-end
+end
View
20 lib/mongoid/tree/traversal.rb
@@ -12,8 +12,8 @@ module Tree
# == Depth First Traversal
#
# See http://en.wikipedia.org/wiki/Depth-first_search for a proper description.
- #
- # Given a tree like:
+ #
+ # Given a tree like:
#
# node1:
# - node2:
@@ -46,7 +46,7 @@ module Tree
# node1, node2, node3, node4, node5, node6, node7
#
module Traversal
-
+
##
# Traverses the tree using the given traversal method (Default is :depth_first)
# and passes each document node to the block.
@@ -61,16 +61,16 @@ module Traversal
# end
def traverse(type = :depth_first, &block)
raise "No block given" unless block_given?
- send("#{type}_traversal", &block)
- end
-
+ send("#{type}_traversal", &block)
+ end
+
private
-
+
def depth_first_traversal(&block)
block.call(self)
self.children.each { |c| c.send(:depth_first_traversal, &block) }
end
-
+
def breadth_first_traversal(&block)
queue = [self]
while queue.any? do
@@ -79,7 +79,7 @@ def breadth_first_traversal(&block)
queue += node.children
end
end
-
+
end
end
-end
+end
View
48 spec/mongoid/tree/traversal_spec.rb
@@ -1,34 +1,34 @@
require 'spec_helper'
describe Mongoid::Tree::Traversal do
-
+
describe '#traverse' do
-
+
subject { Node.new }
-
+
it "should require a block" do
expect { subject.traverse }.to raise_error(/No block given/)
end
-
+
[:depth_first].each do |method|
it "should support #{method} traversal" do
expect { subject.traverse(method) {} }.to_not raise_error
end
end
-
+
it "should complain about unsupported traversal methods" do
expect { subject.traverse('non_existing') {} }.to raise_error
end
-
+
it "should default to depth_first traversal" do
subject.should_receive(:depth_first_traversal)
subject.traverse {}
end
-
+
end
-
+
describe 'depth first traversal' do
-
+
it "should traverse correctly" do
setup_tree <<-ENDTREE
node1:
@@ -39,38 +39,38 @@
- node6
- node7
ENDTREE
-
+
result = []
node(:node1).traverse(:depth_first) { |node| result << node }
result.collect { |n| n.name.to_sym }.should == [:node1, :node2, :node3, :node4, :node5, :node6, :node7]
end
-
+
it "should traverse correctly on merged trees" do
-
+
setup_tree <<-ENDTREE
- node4:
- node5
- node6:
- node7
-
+
- node1:
- node2:
- node3
ENDTREE
-
-
+
+
node(:node1).children << node(:node4)
-
-
+
+
result = []
node(:node1).traverse(:depth_first) { |node| result << node }
result.collect { |n| n.name.to_sym }.should == [:node1, :node2, :node3, :node4, :node5, :node6, :node7]
end
-
+
end
-
+
describe 'breadth first traversal' do
-
+
it "should traverse correctly" do
tree = setup_tree <<-ENDTREE
node1:
@@ -81,12 +81,12 @@
- node7
- node4
ENDTREE
-
+
result = []
node(:node1).traverse(:breadth_first) { |n| result << n }
result.collect { |n| n.name.to_sym }.should == [:node1, :node2, :node3, :node4, :node5, :node6, :node7]
end
-
+
end
-
-end
+
+end
View
62 spec/mongoid/tree_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Mongoid::Tree do
-
+
it "should reference many children as inverse of parent" do
a = Node.associations['children']
a.should_not be_nil
@@ -10,7 +10,7 @@
a.options.foreign_key.should == 'parent_id'
a.options.inverse_of.should == :parent
end
-
+
it "should be referenced in one parent as inverse of children" do
a = Node.associations['parent']
a.should_not be_nil
@@ -18,16 +18,16 @@
a.options.class_name.should == 'Node'
a.options.inverse_of.should == :children
end
-
+
it "should store parent_ids as Array with [] as default" do
f = Node.fields['parent_ids']
f.should_not be_nil
f.options[:type].should == Array
f.options[:default].should == []
end
-
+
describe 'when saved' do
-
+
before(:each) do
setup_tree <<-ENDTREE
- root:
@@ -38,51 +38,51 @@
- other_child
ENDTREE
end
-
+
it "should set the child's parent_id when added to parent's children" do
root = Node.create; child = Node.create
root.children << child
child.parent.should == root
child.parent_id.should == root.id
end
-
+
it "should set the child's parent_id parent is set on child" do
root = Node.create; child = Node.create
child.parent = root
child.parent.should == root
child.parent_id.should == root.id
end
-
+
it "should rebuild its parent_ids" do
root = Node.create; child = Node.create
root.children << child
child.parent_ids.should == [root.id]
end
-
+
it "should rebuild its children's parent_ids when its own parent_ids changed" do
other_root = node(:other_root); child = node(:child); subchild = node(:subchild);
other_root.children << child
subchild.reload # To get the updated version
subchild.parent_ids.should == [other_root.id, child.id]
end
-
+
it "should correctly rebuild its descendants' parent_ids when moved into an other subtree" do
subchild = node(:subchild); subsubchild = node(:subsubchild); other_child = node(:other_child)
other_child.children << subchild
subsubchild.reload
subsubchild.parent_ids.should == [node(:other_root).id, other_child.id, subchild.id]
end
-
+
it "should not rebuild its children's parent_ids when it's not required" do
root = node(:root)
root.should_not_receive(:rearrange_children)
root.save
end
-
+
end
-
+
describe 'utility methods' do
-
+
before(:each) do
setup_tree <<-ENDTREE
root:
@@ -91,72 +91,72 @@
- other_child
ENDTREE
end
-
+
describe '.root?' do
it "should return true for root documents" do
node(:root).should be_root
end
-
+
it "should return false for non-root documents" do
node(:child).should_not be_root
end
end
-
+
describe '.leaf?' do
it "should return true for leaf documents" do
node(:subchild).should be_leaf
node(:other_child).should be_leaf
Node.new.should be_leaf
end
-
+
it "should return false for non-leaf documents" do
node(:child).should_not be_leaf
node(:root).should_not be_leaf
end
end
-
+
describe '.root' do
it "should return the root for this document" do
node(:subchild).root.should == node(:root)
end
end
-
+
describe 'ancestors' do
it ".ancestors should return the documents ancestors" do
node(:subchild).ancestors.to_a.should == [node(:root), node(:child)]
end
-
+
it ".ancestors_and_self should return the documents ancestors and itself" do
node(:subchild).ancestors_and_self.to_a.should == [node(:root), node(:child), node(:subchild)]
end
-
+
describe '.ancestor_of?' do
it "should return true for ancestors" do
node(:child).should be_ancestor_of(node(:subchild))
end
-
+
it "should return false for non-ancestors" do
node(:other_child).should_not be_ancestor_of(node(:subchild))
end
end
end
-
+
describe 'descendants' do
it ".descendants should return the documents descendants" do
node(:root).descendants.to_a.should =~ [node(:child), node(:other_child), node(:subchild)]
end
-
+
it ".descendants_and_self should return the documents descendants and itself" do
node(:root).descendants_and_self.to_a.should =~ [node(:root), node(:child), node(:other_child), node(:subchild)]
end
-
+
describe '.descendant_of?' do
it "should return true for descendants" do
subchild = node(:subchild)
subchild.should be_descendant_of(node(:child))
subchild.should be_descendant_of(node(:root))
end
-
+
it "should return false for non-descendants" do
node(:subchild).should_not be_descendant_of(node(:other_child))
end
@@ -167,12 +167,12 @@
it ".siblings should return the documents siblings" do
node(:child).siblings.to_a.should == [node(:other_child)]
end
-
+
it ".siblings_and_self should return the documents siblings and itself" do
node(:child).siblings_and_self.to_a.should == [node(:child), node(:other_child)]
end
end
-
+
end
-
-end
+
+end
View
2  spec/spec_helper.rb
@@ -18,4 +18,4 @@
config.after :each do
Mongoid.master.collections.reject { |c| c.name =~ /^system\./ }.each(&:drop)
end
-end
+end
View
22 spec/support/macros/tree_macros.rb
@@ -1,15 +1,15 @@
require 'yaml'
module Mongoid::Tree::TreeMacros
-
+
def setup_tree(tree)
create_tree(YAML.load(tree))
end
-
- def node(name)
+
+ def node(name)
@nodes[name].reload
end
-
+
def print_tree(node, print_ids = false, depth = 0)
print ' ' * depth
print '- ' unless depth == 0
@@ -19,28 +19,28 @@ def print_tree(node, print_ids = false, depth = 0)
print "\n"
node.children.each { |c| print_tree(c, print_ids, depth + 1) }
end
-
- private
-
+
+ private
+
def create_tree(object)
case object
when String: return create_node(object)
when Array: object.each { |tree| create_tree(tree) }
- when Hash:
+ when Hash:
name, children = object.first
node = create_node(name)
children.each { |c| node.children << create_tree(c) }
return node
end
end
-
+
def create_node(name)
@nodes ||= HashWithIndifferentAccess.new
@nodes[name] = Node.create(:name => name)
end
-
+
end
RSpec.configure do |config|
config.include Mongoid::Tree::TreeMacros
-end
+end
View
2  spec/support/models/node.rb
@@ -1,7 +1,7 @@
class Node
include Mongoid::Document
include Mongoid::Tree
-
+
field :name
end
Please sign in to comment.
Something went wrong with that request. Please try again.