Observers in Padrino
This article is part of an ongoing series about my exploration of the Padrino web framework. If you want to read more about it please check out padrinobook.com. You sniff around in the sources of the book under GitHub.
Learn more about Padrino Book? Sign Up!
You may come to the point, where you to have certain elements that are bound to your model but don't belong either there nor in the controller. This article will explain how you can use Observers in your application:
Define observers
Let's create the observer with the name user_observer in the models folder:
# app/models/user_observer.rb
class UserObserver < ActiveRecord::Observer
def before_save(user)
if user.new_record?
encrypt_confirmation_code(user)
JobVacancy::App.deliver(:registration, :registration_email, user.name, user.email)
end
end
def after_save(user)
JobVacancy::App.deliver(:confirmation, :confirmation_email, user.name, user.email, user.id)
end
private
def encrypt_confirmation_code(user)
user.confirmation_code = set_confirmation_code(user)
end
def set_confirmation_code(user)
require 'bcrypt'
salt = BCrypt::Engine.generate_salt
confirmation_code = BCrypt::Engine.hash_secret(user.password, salt)
normalize_confirmation_code(confirmation_code)
end
def normalize_confirmation_code(confirmation_code)
confirmation_code.gsub("/", "")
end
end
We are defining our user observer with extends from the ActiveRecord::Observer. Inside this class we can define any callbacks for each action we want to use. The most commons ones are before_<action> and after_<action> where <action> is the ActiveRecord trigger method like save, update, delete, show, or get.
Register the observer
Since the observer is created we need to register it:
# app/app.rb.
module JobVacancy
class App < Padrino::Application
...
# Activating the user_observer
ActiveRecord::Base.add_observer UserObserver.instance
...
end
end
and disable the observer in our specs to make writing more easier:
# spec/spec_helper.rb
RSpec.configure do |conf|
...
conf.before do
User.observers.disable :all # <-- turn of user observers for testing reasons
end
...
end
Thoughts about Observers
Observers are a design pattern where an object has a list of its dependents called observers, and notifies them automatically if its state has changed by calling one of their methods. Observers means to be decoupling responsibility. They can serve as a connection point between your models and some other functionality of another subsystem. Observers "lives" longer in your application and can be attached/detached at any time.
Callbacks life shorter - you pass it to a function to be called only once. Rule of the thumb: When you use callbacks with code that isn't directly related to your model, you better put this into an observer.
The Observer pattern decouples event producers from event consumers but tightly couples models to them - and that make it hard to test them and you always have to take them with you.
Besides they add a kind of hidden magic to your code, you may forget when you that they are always around you. Better way is to make those calls explicit in your controller.