Skip to content

Commit

Permalink
Merge pull request take-five#11 from vkuznetsov/master
Browse files Browse the repository at this point in the history
correct behavior of callbacks
  • Loading branch information
take-five committed Oct 29, 2012
2 parents fbc9e73 + 9f35e52 commit 7857554
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 38 deletions.
77 changes: 47 additions & 30 deletions lib/acts_as_ordered_tree/instance_methods.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -306,6 +306,9 @@ def move_to(target, pos) #:nodoc:
# nothing changed - quit # nothing changed - quit
return if parent_id == parent_id_was && position == position_was return if parent_id == parent_id_was && position == position_was


self.class.find(self, :lock => true)
self[position_column], self[parent_column] = position, parent_id

move_kind = case move_kind = case
when id_was && parent_id != parent_id_was then :move when id_was && parent_id != parent_id_was then :move
when id_was && position != position_was then :reorder when id_was && position != position_was then :reorder
Expand Down Expand Up @@ -363,55 +366,69 @@ def move!(id, parent_id_was, parent_id, position_was, position, depth) #:nodoc:
"THEN :depth " + "THEN :depth " +
"ELSE #{depth_column} " + "ELSE #{depth_column} " +
"END" if depth_column) "END" if depth_column)
].compact.join(', ') ]


conditions = arel[pk].eq(id).or( conditions = arel[pk].eq(id).or(
arel[parent_column].eq(parent_id_was) arel[parent_column].eq(parent_id_was)
).or( ).or(
arel[parent_column].eq(parent_id) arel[parent_column].eq(parent_id)
) )


binds = {:id => id, binds = {:parent_id_was => parent_id_was,
:parent_id_was => parent_id_was,
:parent_id => parent_id, :parent_id => parent_id,
:position_was => position_was, :position_was => position_was,
:position => position, :position => position,
:depth => depth} :depth => depth}


ordered_tree_scope.where(conditions).update_all([assignments, binds]) update_changed_attributes! conditions, assignments, binds
end end


# Internal # Internal
def reorder!(parent_id, position_was, position) def reorder!(parent_id, position_was, position)
assignments = if position_was assignments = [
<<-SQL if position_was
#{position_column} = CASE <<-SQL
WHEN #{position_column} = :position_was #{position_column} = CASE
THEN :position WHEN #{position_column} = :position_was
WHEN #{position_column} <= :position AND #{position_column} > :position_was AND :position > :position_was THEN :position
THEN #{position_column} - 1 WHEN #{position_column} <= :position AND #{position_column} > :position_was AND :position > :position_was
WHEN #{position_column} >= :position AND #{position_column} < :position_was AND :position < :position_was THEN #{position_column} - 1
THEN #{position_column} + 1 WHEN #{position_column} >= :position AND #{position_column} < :position_was AND :position < :position_was
ELSE #{position_column} THEN #{position_column} + 1
END ELSE #{position_column}
SQL END
else SQL
<<-SQL else
#{position_column} = CASE <<-SQL
WHEN #{position_column} > :position #{position_column} = CASE
THEN #{position_column} + 1 WHEN #{position_column} > :position
WHEN #{position_column} IS NULL THEN #{position_column} + 1
THEN :position WHEN #{position_column} IS NULL
ELSE #{position_column} THEN :position
END ELSE #{position_column}
SQL END
end SQL
end
]


conditions = arel[parent_column].eq(parent_id) conditions = arel[parent_column].eq(parent_id)

binds = {:position_was => position_was, :position => position} binds = {:position_was => position_was, :position => position}


ordered_tree_scope.where(conditions).update_all([assignments, binds]) update_changed_attributes! conditions, assignments, binds
end

def update_changed_attributes!(scope_conditions, assignments, binds)
# add assignments for externally changed attributes
internal_attributes = [parent_column.to_s, position_column.to_s, depth_column.to_s, self.class.primary_key]
external_changed_attrs = changed - internal_attributes
unless external_changed_attrs.empty?
external_changed_attrs.each do |attr|
assignments << "#{attr} = CASE WHEN #{self.class.primary_key} = :id THEN :#{attr} ELSE #{attr} END"
binds[attr.to_sym] = self[attr]
end
end

ordered_tree_scope.where(scope_conditions).update_all([assignments.compact.join(', '), {:id => id}.merge(binds)])
end end


# recursively load descendants # recursively load descendants
Expand Down Expand Up @@ -448,7 +465,7 @@ def update_descendants_depth #:nodoc:


# Used in built-in around_move routine # Used in built-in around_move routine
def update_counter_cache #:nodoc: def update_counter_cache #:nodoc:
parent_id_was = self[parent_column] parent_id_was = send "#{parent_column}_was"


yield yield


Expand Down
2 changes: 1 addition & 1 deletion lib/acts_as_ordered_tree/version.rb
Original file line number Original file line Diff line number Diff line change
@@ -1,3 +1,3 @@
module ActsAsOrderedTree module ActsAsOrderedTree
VERSION = "1.1.1" VERSION = "1.1.2"
end end
26 changes: 26 additions & 0 deletions spec/acts_as_ordered_tree_spec.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -729,6 +729,32 @@
child_3.move_to_child_of child_1 child_3.move_to_child_of child_1
record.reload.depth.should eq 3 record.reload.depth.should eq 3
end end

context "DefaultWithCallbacks" do
let!(:cb_root_1) { create :default_with_callbacks, :name => 'root_1' }
let!(:cb_root_2) { create :default_with_callbacks, :name => 'root_2' }
let!(:cb_child_1) { create :default_with_callbacks, :name => 'child_1', :parent => cb_root_1 }
let!(:cb_child_2) { create :default_with_callbacks, :name => 'child_2', :parent => cb_root_1 }

specify "new parent_id should be available in before_move" do
cb_root_2.stub(:before_move) { cb_root_2.parent_id.should eq cb_root_1.id }
cb_root_2.move_to_left_of cb_child_1
end

specify "new position should be available in before_reorder" do
cb_child_2.stub(:before_reorder) { cb_child_2.position.should eq 1 }
cb_child_2.move_to_left_of cb_child_1
end
end

end

context "changed attributes" do
specify "changed attributes should be saved" do
child_2.name = 'name100'
child_2.move_to_left_of child_1
child_2.reload.name.should eq 'name100'
end
end end


end end
Expand Down
11 changes: 4 additions & 7 deletions spec/concurrency_support_spec.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -7,14 +7,11 @@
module Concurrency module Concurrency
# run block in its own thread, create +size+ threads # run block in its own thread, create +size+ threads
def pool(size) def pool(size)
body = proc do |x| size.times.map { |x|
ActiveRecord::Base.connection_pool.with_connection do Thread.new do
yield x ActiveRecord::Base.connection_pool.with_connection { yield x }
end end
end }.each(&:join)
threads = size.times.map { |x| Thread.new { body.call(x) } }

threads.each(&:join)
end end
end end
include Concurrency include Concurrency
Expand Down
1 change: 1 addition & 0 deletions spec/spec_helper.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
ensure ensure
Default.delete_all Default.delete_all
DefaultWithCounterCache.delete_all DefaultWithCounterCache.delete_all
DefaultWithCallbacks.delete_all
Scoped.delete_all Scoped.delete_all
end end
end end
Expand Down
4 changes: 4 additions & 0 deletions spec/support/factories.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
sequence(:name) { |n| "category #{n}" } sequence(:name) { |n| "category #{n}" }
end end


factory :default_with_callbacks do
sequence(:name) { |n| "category #{n}" }
end

factory :scoped do factory :scoped do
sequence(:scope_type) { |n| "type_#{n}" } sequence(:scope_type) { |n| "type_#{n}" }
sequence(:name) { |n| "category #{n}" } sequence(:name) { |n| "category #{n}" }
Expand Down
16 changes: 16 additions & 0 deletions spec/support/models.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -16,6 +16,22 @@ class DefaultWithCounterCache < ActiveRecord::Base
acts_as_ordered_tree :counter_cache => :categories_count acts_as_ordered_tree :counter_cache => :categories_count
end end


class DefaultWithCallbacks < ActiveRecord::Base
self.table_name = "categories"

acts_as_ordered_tree

after_move :after_move
before_move :before_move
after_reorder :after_reorder
before_reorder :before_reorder

def after_move; end
def before_move; end
def after_reorder; end
def before_reorder; end
end

class Scoped < ActiveRecord::Base class Scoped < ActiveRecord::Base
self.table_name = "scoped" self.table_name = "scoped"


Expand Down

0 comments on commit 7857554

Please sign in to comment.