Skip to content
This repository
Browse code

Fix observer callbacks firing multiple times on descendant instances

  • Loading branch information...
commit ee491b064bff126a67600d694511986b8636d47b 1 parent 30a0e3f
Kamal Fariz Mahyuddin authored February 09, 2011 tenderlove committed February 22, 2011
9  activerecord/lib/active_record/observer.rb
@@ -108,10 +108,17 @@ def add_observer!(klass)
108 108
 
109 109
       def define_callbacks(klass)
110 110
         observer = self
  111
+        observer_name = observer.class.name.underscore.gsub('/', '__')
111 112
 
112 113
         ActiveRecord::Callbacks::CALLBACKS.each do |callback|
113 114
           next unless respond_to?(callback)
114  
-          klass.send(callback){|record| observer.send(callback, record)}
  115
+          callback_meth = :"_notify_#{observer_name}_for_#{callback}"
  116
+          unless klass.respond_to?(callback_meth)
  117
+            klass.send(:define_method, callback_meth) do
  118
+              observer.send(callback, self)
  119
+            end
  120
+            klass.send(callback, callback_meth)
  121
+          end
115 122
         end
116 123
       end
117 124
   end
14  activerecord/test/cases/lifecycle_test.rb
@@ -7,6 +7,12 @@
7 7
 
8 8
 class SpecialDeveloper < Developer; end
9 9
 
  10
+class DeveloperObserver < ActiveRecord::Observer
  11
+  def before_save(developer)
  12
+    developer.salary += 1
  13
+  end
  14
+end
  15
+
10 16
 class SalaryChecker < ActiveRecord::Observer
11 17
   observe :special_developer
12 18
   attr_accessor :last_saved
@@ -195,4 +201,12 @@ def test_invalid_observer
195 201
     assert_equal developer, SalaryChecker.instance.last_saved
196 202
   end
197 203
 
  204
+  test "callback observing the ancestor does not fire multiple times on descendent" do
  205
+    DeveloperObserver.instance # activate
  206
+    developer = Developer.create! :name => 'Ancestor', :salary => 100000
  207
+    assert_equal 100001, developer.salary, 'ancestor callback fired multiple times'
  208
+    developer = SpecialDeveloper.create! :name => 'Descendent', :salary => 100000
  209
+    assert_equal 100001, developer.salary, 'descendent callback fired multiple times'
  210
+  end
  211
+
198 212
 end

4 notes on commit ee491b0

Robert Pankowecki

Why was this fireing twice on descendants ? I think the callbacks are copied right ? Maybe then they should not be copied ?

Aaron Patterson
Owner

I'm not sure. I'm looking in to it as I don't really like observer code. However, this is a bug so I had to apply. :-(

Robert Pankowecki

I'm not saying that it was bad to apply, quite contrary, good job on catching and fixing it guys.
Now when we have it working and a proper test is written, we can think about keeping it beautiful :-)

Aaron Patterson
Owner

Exactly. :-D

Please sign in to comment.
Something went wrong with that request. Please try again.