Observers in Padrino

Written by | 8 minutes read | Tags padrino, ruby | Comments

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.

Further reading