lib/active_model/observing.rb in activemodel-3.0.pre vs lib/active_model/observing.rb in activemodel-3.0.0.rc
- old
+ new
@@ -1,20 +1,18 @@
-require 'observer'
require 'singleton'
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/module/aliasing'
+require 'active_support/core_ext/module/remove_method'
require 'active_support/core_ext/string/inflections'
module ActiveModel
module Observing
extend ActiveSupport::Concern
- included do
- extend Observable
- end
-
module ClassMethods
+ # == Active Model Observers Activation
+ #
# Activates the observers assigned. Examples:
#
# # Calls PersonObserver.instance
# ActiveRecord::Base.observers = :person_observer
#
@@ -22,12 +20,13 @@
# ActiveRecord::Base.observers = :cacher, :garbage_collector
#
# # Same as above, just using explicit class references
# ActiveRecord::Base.observers = Cacher, GarbageCollector
#
- # Note: Setting this does not instantiate the observers yet. +instantiate_observers+ is
- # called during startup, and before each development request.
+ # Note: Setting this does not instantiate the observers yet.
+ # +instantiate_observers+ is called during startup, and before
+ # each development request.
def observers=(*values)
@observers = values.flatten
end
# Gets the current observers.
@@ -38,10 +37,30 @@
# Instantiate the global Active Record observers.
def instantiate_observers
observers.each { |o| instantiate_observer(o) }
end
+ def add_observer(observer)
+ 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
+
+ def count_observers
+ @observer_instances.size
+ end
+
protected
def instantiate_observer(observer) #:nodoc:
# string/symbol
if observer.respond_to?(:to_sym)
observer = observer.to_s.camelize.constantize.instance
@@ -53,11 +72,10 @@
end
# Notify observers when the observed class is subclassed.
def inherited(subclass)
super
- changed
notify_observers :observed_class_inherited, subclass
end
end
private
@@ -67,15 +85,16 @@
# notify_observers(:before_save)
# ...
# notify_observers(:after_save)
# end
def notify_observers(method)
- self.class.changed
self.class.notify_observers(method, self)
end
end
+ # == Active Model Observers
+ #
# Observer classes respond to lifecycle callbacks to implement trigger-like
# behavior outside the original class. This is a great way to reduce the
# clutter that normally comes when the model class is burdened with
# functionality that doesn't pertain to the core responsibility of the
# class. Example:
@@ -100,52 +119,57 @@
#
# This Observer uses logger to log when specific callbacks are triggered.
#
# == Observing a class that can't be inferred
#
- # Observers will by default be mapped to the class with which they share a name. So CommentObserver will
- # be tied to observing Comment, ProductManagerObserver to ProductManager, and so on. If you want to name your observer
- # differently than the class you're interested in observing, you can use the Observer.observe class method which takes
- # either the concrete class (Product) or a symbol for that class (:product):
+ # Observers will by default be mapped to the class with which they share a
+ # name. So CommentObserver will be tied to observing Comment, ProductManagerObserver
+ # to ProductManager, and so on. If you want to name your observer differently than
+ # the class you're interested in observing, you can use the Observer.observe class
+ # method which takes either the concrete class (Product) or a symbol for that
+ # class (:product):
#
# class AuditObserver < ActiveModel::Observer
# observe :account
#
# def after_update(account)
# AuditTrail.new(account, "UPDATED")
# end
# end
#
- # If the audit observer needs to watch more than one kind of object, this can be specified with multiple arguments:
+ # If the audit observer needs to watch more than one kind of object, this can be
+ # specified with multiple arguments:
#
# class AuditObserver < ActiveModel::Observer
# observe :account, :balance
#
# def after_update(record)
# AuditTrail.new(record, "UPDATED")
# end
# end
#
- # The AuditObserver will now act on both updates to Account and Balance by treating them both as records.
+ # The AuditObserver will now act on both updates to Account and Balance by treating
+ # them both as records.
#
class Observer
include Singleton
class << self
# Attaches the observer to the supplied model classes.
def observe(*models)
models.flatten!
models.collect! { |model| model.respond_to?(:to_sym) ? model.to_s.camelize.constantize : model }
+ remove_possible_method(:observed_classes)
define_method(:observed_classes) { models }
end
# Returns an array of Classes to observe.
#
# You can override this instead of using the +observe+ helper.
#
# class AuditObserver < ActiveModel::Observer
# def self.observed_classes
- # [AccountObserver, BalanceObserver]
+ # [Account, Balance]
# end
# end
def observed_classes
Array.wrap(observed_class)
end