When you’re developing your application, you often need to trigger some actions based on your object changes. For example, let’s suppose that you want to send a notification when your :start_date or :end_date attributes for your Project model are updated. Your first choice would probably be some ActiveRecord callback, like after_update:
1 2 3 4 5 6 7 8 9 10 11 |
class Project < ActiveRecord::Base after_update :check_date_changed def check_date_changed if self.start_date_changed? || self.end_date_changed? Notification.date_changed(self.star_date, self.end_date) end end end |
The code above works perfectly, but it violates a design principle from SOLID: the single responsibility principle. The Project model is responsible for its relationships, validations and methods; but not for notifying. To do that, we can delegate it to another class using Observer design pattern:
UML diagram for Observer design pattern
ActiveRecord removed the Observer class from its core since 4th Rails version. So, for Rails 4.0 or later, you need to include the gem on your Gemfile and run the bundle command:
1 |
gem 'rails-observers' |
To getting started, you should create a observers folder in some directory like app/observers. In the previous example, the after_update callback would be moved to the app/observers/project_observer.rb file:
1 2 3 4 5 6 7 8 9 |
class ProjectObserver < ActiveRecord::Observer def after_update(project) if project.start_date_changed? || project.end_date_changed? Notification.date_changed(project.start_date, project.end_date) end end end |
The method is named exactly as the corresponding callback and receive the observed object as param. It’s also important to notice the “ModelName|Observer” pattern to name the class, so the observer knows which model it should watch. If you choose not to follow the pattern, you should indicate which model should be watched (you can also use one observer for watching more than one model, like this):
1 2 3 |
class DateObserver < ActiveRecord::Observer observe :project, :task end |
After creating your class, you also need to register the observer on your application.rb (don’t forget to restart your server):
1 |
config.active_record.observers = :project_observer |
Done. That’s all you need for your observer to run. It’s important to keep in mind that observers don’t intervene on object create/update/destroy. So if you use some callback that cancel the object destroy on before_destroy callback for example, it should be placed on your model. The observer should be used only to watch object changes and it’s mostly used for notifying, sending emails and screen asynchronous updates. If you have any doubts or suggestions, please use the comment area or contact me.
4 Comments
Sannytet
December 11th, 2018 at 22:15Nice posts! 🙂
___
Sanny
John Goldman
December 27th, 2018 at 18:35thank you so much!
Gaurav Tinkhede
May 10th, 2020 at 09:26Hi ,
I am new to rails and want to use observer for auditing in my project.
I am facing below error :
method_missing': undefined method
observers=’ for ActiveRecord::Base:Class (NoMethodError)Regards,
Gaurav
Ronan Lopes
May 10th, 2020 at 15:45Hi, Gaurav! You probably missed something in the middle. Rails doesn’t have observers enabled by default lately, so you would have to add the gem ‘rails-observers’ and run the bundle command. Also, after add that config to your application.rb, you have to restart your server. Give those a try. Good luck there!