As your Ruby on Rails application grows, maintaining clean and manageable code becomes increasingly important. One of the powerful yet often underutilized features in Rails is the Observer pattern. Observers allow you to keep your models lean by extracting responsibilities that don't necessarily belong in the model itself. In this article, we'll explore what observers are, when to use them, and how to implement them in your Rails application.
What are Observers?
Observers in Rails provide a way to respond to lifecycle callbacks outside of your models. They allow you to encapsulate the callback logic in a separate class, promoting cleaner and more modular code. Observers listen for changes to an object and respond to those changes without the object needing to explicitly notify the observer.
When to Use Observers
Observers are ideal for situations where you want to decouple the callback logic from your models. Here are a few common use cases:
- Logging and Analytics: Track changes or actions performed on a model without cluttering the model code.
- Notifications: Send emails, Slack messages, or other notifications when certain model events occur.
- Asynchronous Jobs: Enqueue background jobs in response to model changes.
- Complex Business Logic: Implement business rules that should be applied after certain model changes.
Implementing Observers in Rails
Rails observers are not included in Rails by default anymore, starting from Rails 4. However, you can still use them by including the rails-observers
gem in your Gemfile:
gem 'rails-observers'
After adding the gem, run bundle install
to install it.
Creating an Observer
Let’s walk through an example of creating an observer. Imagine you have a User
model and you want to send a welcome email whenever a new user is created.
- Generate the Observer:
First, generate the observer file:
rails generate observer User
This will create a file app/models/user_observer.rb
.
- Define the Observer:
Open the generated user_observer.rb
file and define the callback methods:
class UserObserver < ActiveRecord::Observer
def after_create(user)
UserMailer.welcome_email(user).deliver_later
end
end
Here, after_create
is a callback method that will be triggered after a new user is created. The UserMailer.welcome_email(user).deliver_later
line enqueues the welcome email to be sent asynchronously.
- Register the Observer:
To make Rails aware of the observer, you need to register it. Open config/application.rb
and add the following line inside the class Application < Rails::Application
block:
config.active_record.observers = :user_observer
- Create the Mailer:
If you haven't already, generate the mailer:
rails generate mailer UserMailer
Define the welcome_email
method in app/mailers/user_mailer.rb
:
class UserMailer < ApplicationMailer
def welcome_email(user)
@user = user
mail(to: @user.email, subject: 'Welcome to My Awesome Site')
end
end
Also, create a view template for the email in app/views/user_mailer/welcome_email.html.erb
.
Benefits of Using Observers
- Separation of Concerns: By moving callback logic to observers, your models remain focused on their primary responsibility: data persistence and validation.
- Reusability: Observers can be reused across different models, promoting DRY (Don't Repeat Yourself) principles.
- Maintainability: With a clean separation of callback logic, your codebase becomes easier to maintain and understand.
Conclusion
Observers are a powerful tool in the Rails ecosystem for maintaining clean and modular code. By decoupling callback logic from your models, you can achieve a more maintainable and scalable codebase. While Rails no longer includes observers by default, the rails-observers
gem makes it easy to integrate this pattern into your application. Use observers to handle logging, notifications, complex business logic, and more, ensuring your models remain lean and focused on their primary responsibilities.
Happy coding!
Top comments (3)
Hi Afar, so I think this might be little controversial. The reason why the observers were taken out from Rails 4 was that it made code hard to read/debug. I remember using them but the app become messy overtime. Maybe that's reason why the gem itself is not maintained since 2017? I do like the idea but there are other ways how to achieve the functionality without making code less readable.
Thanks for your valuable feedback, yes you are right we are using callbacks now.
Hi Petr,
I never used Observers so the idea of using them looked interesting. But I understand that it adds a layer of complexity (in term of readability).
What are the others ways to unclutter business logic you mentionned, without relying on observers?