Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Adding support for new search_class option

Useful in STI scenarios.
  • Loading branch information...
commit ceb91621495ff5e9c1b91e1ddf2ce2e272bab379 1 parent d2dabf8
@gus gus authored
View
5 README.rdoc
@@ -21,12 +21,13 @@ Enable the tree functionality by declaring acts_as_tree on your model
acts_as_tree
end
-The method accepts :parent_id_field, :path_field, :depth_field, :order as a hash.
+The method accepts :parent_id_field, :path_field, :depth_field, :order, :search_class as a hash.
:parent_id_field, :path_field, :depth_field => override the default field names
:order => control the order (format "_field-name_ _[asc|desc]_")
+ :search_class => expects a Class that is a MongoMapper::Document to be used for search
-Check the test_tree.rb for examples.
+Check test_tree.rb and test_search_class.rb for examples.
== About bugs
View
33 lib/mongo_mapper_acts_as_tree.rb
@@ -49,7 +49,7 @@ def ==(other)
end
def parent=(var)
- var = self.class.find(var) if var.is_a? String
+ var = search_class.find(var) if var.is_a? String
if self.descendants.include? var
@_cyclic = true
@@ -79,7 +79,7 @@ def fix_position
end
def parent
- @_parent or (self[parent_id_field].nil? ? nil : self.class.find(self[parent_id_field]))
+ @_parent or (self[parent_id_field].nil? ? nil : search_class.find(self[parent_id_field]))
end
def root?
@@ -87,12 +87,12 @@ def root?
end
def root
- self[path_field].first.nil? ? self : self.class.find(self[path_field].first)
+ self[path_field].first.nil? ? self : search_class.find(self[path_field].first)
end
def ancestors
return [] if root?
- self.class.find(self[path_field])
+ search_class.find(self[path_field])
end
def self_and_ancestors
@@ -100,20 +100,20 @@ def self_and_ancestors
end
def siblings
- self.class.all(:_id => {"$ne" => self._id}, parent_id_field => self[parent_id_field], :order => tree_order)
+ search_class.all(:_id => {"$ne" => self._id}, parent_id_field => self[parent_id_field], :order => tree_order)
end
def self_and_siblings
- self.class.all(parent_id_field => self[parent_id_field], :order => tree_order)
+ search_class.all(parent_id_field => self[parent_id_field], :order => tree_order)
end
def children
- self.class.all(parent_id_field => self._id, :order => tree_order)
+ search_class.all(parent_id_field => self._id, :order => tree_order)
end
def descendants
return [] if new_record?
- self.class.all(path_field => self._id, :order => tree_order)
+ search_class.all(path_field => self._id, :order => tree_order)
end
def self_and_descendants
@@ -156,7 +156,7 @@ def move_children
end
def destroy_descendants
- self.class.destroy(self.descendants.map(&:_id))
+ search_class.destroy(self.descendants.map(&:_id))
end
end
@@ -176,6 +176,21 @@ def depth_field
def tree_order
acts_as_tree_options[:order] or ""
end
+
+ # When added as an option to acts_as_tree, search class will be used as the base from which to
+ # find tree objects. This is handy should you have a tree of objects that are of different types, but
+ # might be related through single table inheritance.
+ #
+ # acts_as_tree :search_class => Shape
+ #
+ # In the above example, you could have a working tree ofShape, Circle and Square types (assuming
+ # Circle and Square were subclasses of Shape). If you want to do the same thing and you don't provide
+ # search_class, nesting mixed types will not work.
+ #
+ # The default is +self.class+
+ def search_class
+ acts_as_tree_options[:search_class] or self.class
+ end
end
end
end
View
14 test/models/shapes.rb
@@ -0,0 +1,14 @@
+require "mongo_mapper"
+require "mongo_mapper_acts_as_tree"
+
+class Shape
+ include MongoMapper::Document
+ include MongoMapper::Acts::Tree
+
+ key :name, String
+
+ acts_as_tree :search_class => Shape
+end
+
+class Circle < Shape; end
+class Square < Shape; end
View
90 test/test_search_class.rb
@@ -0,0 +1,90 @@
+require 'helper'
+
+class TestSearchScope < Test::Unit::TestCase
+ context "Simple, mixed type tree" do
+ setup do
+ shape = Shape.create(:name => "Root")
+ Circle.create(:name => "Circle", :parent => shape)
+ Square.create(:name => "Square", :parent => shape)
+ end
+
+ setup do
+ # We are loading from the database here because this process proves the point. If we never did it this
+ # way, there would be no reason to change the code.
+ @shape, @circle, @square = Shape.first, Circle.first, Square.first
+ end
+
+ should "return circle and square as children of shape" do
+ assert_equal [@circle, @square], @shape.children
+ end
+
+ should("return shape as parent of circle") { assert_equal @shape, @circle.parent }
+ should("return shape as parent of square") { assert_equal @shape, @square.parent }
+
+ should("return square as exclusive sibling of circle") { assert_equal [@square], @circle.siblings }
+ should "return self and square as inclusive siblings of circle" do
+ assert_equal [@circle, @square], @circle.self_and_siblings
+ end
+
+ should("return circle as exclusive sibling of square") { assert_equal [@circle], @square.siblings }
+ should "return self and circle as inclusive siblings of square" do
+ assert_equal [@circle, @square], @square.self_and_siblings
+ end
+
+ should "return circle and square as exclusive descendants of shape" do
+ assert_equal [@circle, @square], @shape.descendants
+ end
+ should "return shape, circle and square as inclusive descendants of shape" do
+ assert_equal [@shape, @circle, @square], @shape.self_and_descendants
+ end
+
+ should("return shape as exclusive ancestor of circle") { assert_equal [@shape], @circle.ancestors }
+ should "return self and shape as inclusive ancestors of circle" do
+ assert_equal [@shape, @circle], @circle.self_and_ancestors
+ end
+
+ should("return shape as exclusive ancestor of square") { assert_equal [@shape], @square.ancestors }
+ should "return self and shape as inclusive ancestors of square" do
+ assert_equal [@shape, @square], @square.self_and_ancestors
+ end
+
+ should("return shape as root of circle") { assert_equal @shape, @square.root }
+ should("return shape as root of square") { assert_equal @shape, @circle.root }
+ end
+
+ context "A tree with mixed types on either side of a branch" do
+ setup do
+ shape = Shape.create(:name => "Root")
+ circle = Circle.create(:name => "Circle", :parent => shape)
+ Square.create(:name => "Square", :parent => circle)
+ end
+
+ setup do
+ @shape, @circle, @square = Shape.first, Circle.first, Square.first
+ end
+
+ should("return circle as child of shape") { assert_equal [@circle], @shape.children }
+ should("return square as child of circle") { assert_equal [@square], @circle.children }
+ should("return circle as parent of square") { assert_equal @circle, @square.parent }
+ should("return shape as parent of circle") { assert_equal @shape, @circle.parent }
+
+ should "return circle and square as descendants of shape" do
+ assert_equal [@circle, @square], @shape.descendants
+ end
+
+ should("return square as descendant of circle") { assert_equal [@square], @circle.descendants }
+
+ should "return shape and circle as ancestors of square" do
+ assert_equal [@shape, @circle], @square.ancestors
+ end
+
+ should("return shape as ancestor of circle") { assert_equal [@shape], @circle.ancestors }
+
+ should "destroy descendants of shape" do
+ @shape.destroy_descendants
+ assert_nil Shape.find(@circle._id)
+ assert_nil Shape.find(@square._id)
+ end
+ end
+
+end # TestSearchScope
View
6 test/test_tree.rb
@@ -12,9 +12,9 @@ class TestMongomapperActsAsTree < Test::Unit::TestCase
@root_2 = Category.create(:name => "Root 2")
end
- should "create node from id " do
- assert Category.create(:name => "Child 2.2", :parent => @root_1.id.to_s).parent == @root_1
- end
+ should "create node from id " do
+ assert Category.create(:name => "Child 2.2", :parent => @root_1.id.to_s).parent == @root_1
+ end
should "have roots" do
assert eql_arrays?(Category.roots, [@root_1, @root_2])
Please sign in to comment.
Something went wrong with that request. Please try again.