From 7c84bbf1607bf4059de04cc4c8ec84df2334574b Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 20 Jul 2009 23:57:01 -0500 Subject: Add wrap_with_notifications helper to AMo observing --- activemodel/lib/active_model/observing.rb | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'activemodel/lib/active_model/observing.rb') diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb index 7bad2397ae..707b1a0da6 100644 --- a/activemodel/lib/active_model/observing.rb +++ b/activemodel/lib/active_model/observing.rb @@ -1,7 +1,8 @@ require 'observer' require 'singleton' -require 'active_support/core_ext/string/inflections' require 'active_support/core_ext/array/wrap' +require 'active_support/core_ext/module/aliasing' +require 'active_support/core_ext/string/inflections' module ActiveModel module Observing @@ -39,6 +40,23 @@ module ActiveModel observers.each { |o| instantiate_observer(o) } end + # Wraps methods with before and after notifications. + # + # wrap_with_notifications :create, :save, :update, :destroy + def wrap_with_notifications(*methods) + methods.each do |method| + class_eval(<<-EOS, __FILE__, __LINE__ + 1) + def #{method}_with_notifications(*args, &block) + notify_observers(:before_#{method}) + result = #{method}_without_notifications(*args, &block) + notify_observers(:after_#{method}) + result + end + EOS + alias_method_chain(method, :notifications) + end + end + protected def instantiate_observer(observer) # string/symbol @@ -60,7 +78,7 @@ module ActiveModel end private - def notify(method) #:nodoc: + def notify_observers(method) self.class.changed self.class.notify_observers(method, self) end -- cgit v1.2.3 From 48bc39e03a077ed9a0684a181c1180c7f53b0cad Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 21 Jul 2009 00:11:26 -0500 Subject: Improve AMo observing docs --- activemodel/lib/active_model/observing.rb | 77 +++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 4 deletions(-) (limited to 'activemodel/lib/active_model/observing.rb') diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb index 707b1a0da6..3b230c43b9 100644 --- a/activemodel/lib/active_model/observing.rb +++ b/activemodel/lib/active_model/observing.rb @@ -42,7 +42,7 @@ module ActiveModel # Wraps methods with before and after notifications. # - # wrap_with_notifications :create, :save, :update, :destroy + # wrap_with_notifications :create, :save, :update, :destroy def wrap_with_notifications(*methods) methods.each do |method| class_eval(<<-EOS, __FILE__, __LINE__ + 1) @@ -58,7 +58,7 @@ module ActiveModel end protected - def instantiate_observer(observer) + def instantiate_observer(observer) #:nodoc: # string/symbol if observer.respond_to?(:to_sym) observer = observer.to_s.camelize.constantize.instance @@ -78,12 +78,72 @@ module ActiveModel end private + # Fires notifications to model's observers + # + # def save + # notify_observers(:before_save) + # ... + # notify_observers(:after_save) + # end def notify_observers(method) self.class.changed self.class.notify_observers(method, self) end end + # 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: + # + # class CommentObserver < ActiveModel::Observer + # def after_save(comment) + # Notifications.deliver_comment("admin@do.com", "New comment was posted", comment) + # end + # end + # + # This Observer sends an email when a Comment#save is finished. + # + # class ContactObserver < ActiveModel::Observer + # def after_create(contact) + # contact.logger.info('New contact added!') + # end + # + # def after_destroy(contact) + # contact.logger.warn("Contact with an id of #{contact.id} was destroyed!") + # end + # end + # + # 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): + # + # 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: + # + # 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. + # class Observer include Singleton @@ -95,6 +155,15 @@ module ActiveModel 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] + # end + # end def observed_classes Array.wrap(observed_class) end @@ -115,7 +184,7 @@ module ActiveModel observed_classes.each { |klass| add_observer!(klass) } end - def observed_classes + def observed_classes #:nodoc: self.class.observed_classes end @@ -132,7 +201,7 @@ module ActiveModel end protected - def add_observer!(klass) + def add_observer!(klass) #:nodoc: klass.add_observer(self) end end -- cgit v1.2.3