How to use Observers in Rails
Observer
pattern provides a simple mechanism for one object to inform a set of interested objects when its state changes.
When to use it:
Observer pattern is used when there is one to many relationship between objects such as if one object is modified, then its dependent objects are to be notified and updated automatically.
Let’s take a simple example where we would like to send notification emails to user based on his/her activity status.
class User < Activerecord::Base
after_save :send_email_notification
private
def send_email_notification
if status_changed?
UserNotificationMailer.account_open_email(self).deliver if status=='created'
UserNotificationMailer.account_activated_email(self).deliver if status == 'active'
UserNotificationMailer.account_deactivation_email(self).deliver if status =='deactive'
end
end
end
This above code works absolutely fine as expected. But What is the problem here? Yes, our model job is not to send emails. This violates the Single Responsibility Principle
. So best solution is to use observer pattern here.
Create a UserObserver class inside model which will inherit from ActiveRecord::Observer
.
class UserObserver < ActiveRecord::Observer
def after_save(record)
if record.status_changed?
UserNotificationMailer.account_open_email(record).deliver if record.status=='created'
UserNotificationMailer.account_activated_email(record).deliver if record.status == 'active'
UserNotificationMailer.account_deactivation_email(record).deliver if record.status =='deactive'
end
end
end
How to use single observer for multiple models?
Let’s suppose we have two entities where we need to trigger user email if any of the entity state changes
class Notifier < ActiveRecord::Observer
observe User, Post
def after_save(record)
return unless record.status_changed?
case record.class.name
when 'User'
UserNotificationMailer.account_open_notification(record).deliver if record.status=='created'
UserNotificationMailer.account_activation_notification(record).deliver if record.status == 'active'
UserNotificationMailer.account_deactivation_notification(record).deliver if record.status =='deactive'
when 'Post'
UserNotificationMailer.post_approval_notification(record).deliver if record.status=='approved'
end
end
end
The convention is to name observers after the class they observe. If we want to use one observer for several classes, we need to use observe :mode_1, :model_2
Disclaimer:
Active record observers have been removed from rails core and moved to rails-observers gem after Rails 3.2 .
To use this in later versions include rails-observers
gem in Gemfile and configure your observers in config/application.rb