Permalink
Browse files

Added Scopes and Callbacks

  • Loading branch information...
sled committed Jun 17, 2011
1 parent 3388011 commit 4593bf697f6592b8f96f931ca1069e6512eba6a8
Showing with 148 additions and 12 deletions.
  1. +5 −0 CHANGELOG
  2. +39 −12 lib/mongoid_acts_as_tree.rb
  3. +28 −0 test/models/user_tree.rb
  4. +43 −0 test/test_callbacks.rb
  5. +33 −0 test/test_scope.rb
View
@@ -1,3 +1,8 @@
+*0.1.5 (June 17th, 2011)*
+* Added scoped trees
+* Added callbacks for move and unlink
+* Added Tests for callbacks and scope
+
*0.1.4 (July 5th 2010)*
*parent_id attribute accessor acts as expected
@@ -17,6 +17,10 @@ def acts_as_tree(options = {})
:depth_field => "depth"
}.merge(options)
+ if options[:scope].is_a?(Symbol) && options[:scope].to_s !~ /_id$/
+ options[:scope] = "#{options[:scope]}_id".intern
+ end
+
write_inheritable_attribute :acts_as_tree_options, options
class_inheritable_reader :acts_as_tree_options
@@ -48,6 +52,9 @@ def acts_as_tree(options = {})
after_save :move_children
validate :will_save_tree
before_destroy :destroy_descendants
+
+ define_callbacks :move, :terminator => "result==false"
+ define_callbacks :unlink, :terminator => "result==false"
end
end
@@ -76,6 +83,10 @@ def will_save_tree
if @_cyclic
errors.add(:base, "Can't be children of a descendant")
end
+
+ if @_scope_missmatch
+ errors.add(:base, 'Child and Parent must be within same scope')
+ end
end
def fix_position
@@ -181,6 +192,12 @@ def move_children
def destroy_descendants
self.descendants.each &:destroy
end
+
+ def same_scope?(other)
+ Array(tree_scope).all? do |attr|
+ self.send(attr) == other.send(attr)
+ end
+ end
end
#proxy class
@@ -196,15 +213,19 @@ def initialize(owner)
def <<(object, will_save=true)
if object.descendants.include? @parent
object.instance_variable_set :@_cyclic, true
+ elsif !@parent.same_scope?(object)
+ object.instance_variable_set :@_scope_missmatch, true
else
- object.write_attribute object.parent_id_field, @parent._id
- object[object.path_field] = @parent[@parent.path_field] + [@parent._id]
- object[object.depth_field] = @parent[@parent.depth_field] + 1
- object.instance_variable_set :@_will_move, true
- object.save if will_save
+ object.run_callbacks :move do
+ object.write_attribute object.parent_id_field, @parent._id
+ object[object.path_field] = @parent[@parent.path_field] + [@parent._id]
+ object[object.depth_field] = @parent[@parent.depth_field] + 1
+ object.instance_variable_set :@_will_move, true
+ object.save if will_save
+
+ super(object)
+ end
end
-
- super(object)
end
def build(attributes)
@@ -227,12 +248,14 @@ def delete(object_or_id)
object_or_id
end
- object.write_attribute object.parent_id_field, nil
- object[object.path_field] = []
- object[object.depth_field] = 0
- object.save
+ object.run_callbacks :unlink do
+ object.write_attribute object.parent_id_field, nil
+ object[object.path_field] = []
+ object[object.depth_field] = 0
+ object.save
- super(object)
+ super(object)
+ end
end
#Clear children list
@@ -267,6 +290,10 @@ def depth_field
def tree_order
acts_as_tree_options[:order] or []
end
+
+ def tree_scope
+ acts_as_tree_options[:scope] or nil
+ end
end
end
end
View
@@ -0,0 +1,28 @@
+require "mongoid"
+require "mongoid_acts_as_tree"
+
+# every user has his own tree
+class UserTree
+ include Mongoid::Document
+ include Mongoid::Acts::Tree
+
+ field :name, :type => String
+ field :user_id, :type => Integer
+ field :unlinkable, :type => Boolean, :default => true
+ field :moveable, :type => Boolean, :default => true
+
+ acts_as_tree :scope => :user_id
+
+ set_callback :move, :before, :custom_before_move
+ set_callback :unlink, :before, :custom_before_unlink
+
+ def custom_before_move
+ return self.moveable
+ end
+
+ def custom_before_unlink
+ return self.unlinkable
+ end
+
+
+end
View
@@ -0,0 +1,43 @@
+require 'helper'
+require 'set'
+
+$verbose = false
+
+class TestMongoidActsAsTree < Test::Unit::TestCase
+ context "TreeCallbacks" do
+ setup do
+ @root_1 = UserTree.create(:name => "Root 1", :user_id => 1234)
+ @child_1 = UserTree.new(:name => "Child 1", :user_id => 1234)
+ @child_2 = UserTree.new(:name => "Child 2", :user_id => 1234)
+ @child_2_1 = UserTree.new(:name => "Child 2.1", :user_id => 1234)
+
+ @child_3 = UserTree.new(:name => "Child 3", :user_id => 1234)
+ @root_2 = UserTree.create(:name => "Root 2", :user_id => 6789)
+
+ @root_1.children << @child_1
+ @root_1.children << @child_2
+ @root_1.children << @child_3
+
+ @child_2.children << @child_2_1
+ end
+
+ should "should block unlink" do
+ @child_1.unlinkable = false
+ before = @root_1.children.size
+ @root_1.children.delete(@child_1);
+
+ assert @child_1.parent == @root_1
+ assert @root_1.children.size == before
+ @child_1.unlinkable = true
+ end
+
+ should "should block move" do
+ @child_1.moveable = false
+ @child_2.children << @child_1
+
+ assert @child_1.parent == @root_1
+ @child_1.moveable = true
+ end
+ end
+end
+
View
@@ -0,0 +1,33 @@
+require 'helper'
+require 'set'
+
+$verbose = false
+
+class TestMongoidActsAsTree < Test::Unit::TestCase
+ context "TreeScope" do
+ setup do
+ @root_1 = UserTree.create(:name => "Root 1", :user_id => 1234)
+ @child_1 = UserTree.new(:name => "Child 1", :user_id => 1234)
+ @child_2 = UserTree.new(:name => "Child 2", :user_id => 1234)
+ @child_2_1 = UserTree.new(:name => "Child 2.1", :user_id => 1234)
+
+ @child_3 = UserTree.new(:name => "Child 3", :user_id => 1234)
+ @root_2 = UserTree.create(:name => "Root 2", :user_id => 6789)
+
+ @root_1.children << @child_1
+ @root_1.children << @child_2
+ @root_1.children << @child_3
+
+ @child_2.children << @child_2_1
+ end
+
+ should "should block scope missmatch" do
+ before = @root_2.children.size
+ child = UserTree.new(:name => 'Child 4', :user_id => 1234);
+ @root_2.children << child
+ assert @root_2.children.size == before
+ end
+
+ end
+end
+

0 comments on commit 4593bf6

Please sign in to comment.