Disclaimer
ActiveRecord::Observer will be extracted to a separated gem when Rails 4 is released, so if you are planning to upgrade to the latest Rails version and want to use ActiveRecord::Observer, you’ll need to add the rails-observer gem to your Gemfile.
ActiveRecord::Observer
Using ActiveRecord::Observer is a good practice, instead of bloating your model with a lot of callbacks, moving this behavior to another class with only this responsibility sounds great, but ActiveRecord::Observer has some “limitations”, I quoted that because they seem like limitations, but the problem is that ActiveRecord::Observer’s capabilities aren’t fully documented or aren’t visible, you have to dig in the source code and figure out how it works.
By default, ActiveRecord::Observer watched a limited set of callbacks defined by the ActiveRecord::Callbacks::CALLBACKS constant, which are:
CALLBACKS = [
:after_initialize, :after_find, :after_touch, :before_validation, :after_validation,
:before_save, :around_save, :after_save, :before_create, :around_create,
:after_create, :before_update, :around_update, :after_update,
:before_destroy, :around_destroy, :after_destroy, :after_commit, :after_rollback
]
But what if you want to add you own callbacks?, is there a way to do that? There surely is one.
Adding your own callbacks
ActiveRecord::Observer functionality is fairly easy to understand, there is a notify_observers method in the ActiveModel::Observing module that we can use to define our own callbacks with something like this:
module Notifications
def notify_completed
notify_observers :after_complete
end
end
class Action < ActiveRecord::Base
include Notifications
def complete!
...
notify_completed
end
end
class ActionObserver < ActiveRecord::Observer
def after_complete(action)
# your callback logic here
...
end
end
And that’s it, you’ll have the after_complete callback available in your observer class, this method will receive the model instance where the callback is triggered as an argument.
But, what if I want to know more about the event that triggered the callback, not just the instance where it ocurred? Maybe I want to find out more about the user that triggered the action, is there a way to do that?, well, try this.
Sending more arguments in the callback
If you look in the notify_observers source, you’ll see that it only receives one argument at a time, but you can see that this method uses a class method with the same name but this one accepts many arguments.
In the original notify_observers they always use self as the second argument, we are going to change that with another object, I’ll use a hash, but you can wrap your arguments into a class, the important thing here is that the second argument needs to be an object.
module Notifications
def notify_completed(user)
self.class.notify_observers :after_complete, { action: self, user: user }
end
end
class Action < ActiveRecord::Base
include Notifications
def complete!(user)
...
notify_completed(user)
end
end
class YourModelObserver < ActiveRecord::Observer
def after_complete(event)
# your callback logic here
# The event argument will be a Hash with action and user keys.
...
end
end
And that’s it.
There are many things that Rails can do for you, but you’ll need to go beyond the docs, diving deep in the source is the best way to find those things.
Hope you find this useful, thanks for reading!