From ee5fdbf21a4a837f59b8726c10726ddd020181f9 Mon Sep 17 00:00:00 2001 From: Kamal Fariz Mahyuddin Date: Wed, 9 Feb 2011 23:37:30 +0800 Subject: [PATCH] Fix observer callbacks firing multiple times on descendant instances --- activerecord/lib/active_record/observer.rb | 9 ++++++++- activerecord/test/cases/lifecycle_test.rb | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb index 8b011ad..0893d7e 100644 --- a/activerecord/lib/active_record/observer.rb +++ b/activerecord/lib/active_record/observer.rb @@ -104,10 +104,17 @@ module ActiveRecord def define_callbacks(klass) observer = self + observer_name = observer.class.name.underscore.gsub('/', '__') ActiveRecord::Callbacks::CALLBACKS.each do |callback| next unless respond_to?(callback) - klass.send(callback){|record| observer.send(callback, record)} + callback_meth = :"_notify_#{observer_name}_for_#{callback}" + unless klass.respond_to?(callback_meth) + klass.send(:define_method, callback_meth) do + observer.send(callback, self) + end + klass.send(callback, callback_meth) + end end end end diff --git a/activerecord/test/cases/lifecycle_test.rb b/activerecord/test/cases/lifecycle_test.rb index 0558deb..a42f5d9 100644 --- a/activerecord/test/cases/lifecycle_test.rb +++ b/activerecord/test/cases/lifecycle_test.rb @@ -7,6 +7,12 @@ require 'models/comment' class SpecialDeveloper < Developer; end +class DeveloperObserver < ActiveRecord::Observer + def before_save(developer) + developer.salary += 1 + end +end + class SalaryChecker < ActiveRecord::Observer observe :special_developer attr_accessor :last_saved @@ -196,4 +202,12 @@ class LifecycleTest < ActiveRecord::TestCase assert_equal developer, SalaryChecker.instance.last_saved end + test "callback observing the ancestor does not fire multiple times on descendent" do + DeveloperObserver.instance # activate + developer = Developer.create! :name => 'Ancestor', :salary => 100000 + assert_equal 100001, developer.salary, 'ancestor callback fired multiple times' + developer = SpecialDeveloper.create! :name => 'Descendent', :salary => 100000 + assert_equal 100001, developer.salary, 'descendent callback fired multiple times' + end + end -- 1.7.3.3