Skip to content

Commit

Permalink
ActiveModel::Observing: stop using Observable Ruby module, re-impleme…
Browse files Browse the repository at this point in the history
…nt `notify_observers`

`Observable#notify_observers` from Ruby always returns false (which halts ActiveRecord
callback chains) and has extra features (like `changed`) that were never used.

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
  • Loading branch information
mislav authored and jeremy committed Apr 16, 2010
1 parent cf616e4 commit c2ca73c
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 7 deletions.
23 changes: 16 additions & 7 deletions activemodel/lib/active_model/observing.rb
@@ -1,4 +1,3 @@
require 'observer'
require 'singleton'
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/module/aliasing'
Expand All @@ -8,10 +7,6 @@ module ActiveModel
module Observing
extend ActiveSupport::Concern

included do
extend Observable
end

module ClassMethods
# Activates the observers assigned. Examples:
#
Expand Down Expand Up @@ -41,6 +36,22 @@ def instantiate_observers
observers.each { |o| instantiate_observer(o) }
end

def add_observer(observer)

This comment has been minimized.

Copy link
@robertothais

robertothais Jun 25, 2010

What about delete_observers? Dynamically removing observers is useful in rake tasks and other contexts in which you don't want every observer firing.

This comment has been minimized.

Copy link
@mislav

mislav Jun 25, 2010

Author Member

I wasn't sure that it was ever used. It wasn't covered by tests, for instance. Feel free to make a patch to add delete_observers method, but first think about other ways to prevent observers from loading, such as creating a special environment for rake tasks and disabling observers in that environment (by means of environment configuration).

This comment has been minimized.

Copy link
@robertothais

robertothais Jun 25, 2010

Hi Mislav, thanks for the reply.
I stumbled upon a few articles on the web explaining how to disable observers, so it seems to me that quite a few people found use in this feature. In my particular case I want to disable observers only for one rake task, and not for the environment in general. I'll try coming up with a patch and some tests to cover this situation.

This comment has been minimized.

Copy link
@spovich

spovich Jun 25, 2010

Here is a use case that is real world: We disabled observers on a model with an observer that would send emails based on a model add/update. We didn't want observers when seeding the db in development or running tests.

+1 for adding back in an easy way to delete/disable observers.

unless observer.respond_to? :update
raise ArgumentError, "observer needs to respond to `update'"
end
@observer_instances ||= []
@observer_instances << observer
end

def notify_observers(*arg)
if defined? @observer_instances
for observer in @observer_instances
observer.update(*arg)
end
end
end

protected
def instantiate_observer(observer) #:nodoc:
# string/symbol
Expand All @@ -56,7 +67,6 @@ def instantiate_observer(observer) #:nodoc:
# Notify observers when the observed class is subclassed.
def inherited(subclass)
super
changed
notify_observers :observed_class_inherited, subclass
end
end
Expand All @@ -70,7 +80,6 @@ def inherited(subclass)
# notify_observers(:after_save)
# end
def notify_observers(method)
self.class.changed
self.class.notify_observers(method, self)
end
end
Expand Down
20 changes: 20 additions & 0 deletions activerecord/test/cases/lifecycle_test.rb
Expand Up @@ -7,6 +7,14 @@

class SpecialDeveloper < Developer; end

class SalaryChecker < ActiveRecord::Observer
observe :special_developer

def before_save(developer)
return developer.salary > 80000
end
end

class TopicaAuditor < ActiveRecord::Observer
observe :topic

Expand Down Expand Up @@ -159,4 +167,16 @@ def test_invalid_observer
assert_equal [ValidatedComment, ValidatedComment, ValidatedCommentObserver], callers,
"model callbacks did not fire before observers were notified"
end

test "able to save developer" do
SalaryChecker.instance # activate
developer = SpecialDeveloper.new :name => 'Roger', :salary => 100000
assert developer.save, "developer with normal salary failed to save"
end

test "unable to save developer with low salary" do
SalaryChecker.instance # activate
developer = SpecialDeveloper.new :name => 'Rookie', :salary => 50000
assert !developer.save, "allowed to save a developer with too low salary"
end
end

0 comments on commit c2ca73c

Please sign in to comment.