Skip to content


Subversion checkout URL

You can clone with
Download ZIP
The unobtrusive presenter pattern for Rails
Fetching latest commit...
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.
.gitignore The unobtrusive presenter pattern for Rails.
NEWS Update the ChangeLog and NEWS with the new bug fixes.


A Rails presenter class.

Experienced devs will be interested in the NEWS, ChangeLog, and reference documentation.

How to Use Liaison

Add this to your Gemfile:

gem 'liaison'

Then run the bundler installer:


You instantiate Presenter classes in your controllers, setting them as instance variables so they can be passed to the views. The Presenter class takes the model name as a string (sign_up, for example) then a hash of options. Currently supported options are :fields, a list of attributes on the presenter ([:email, :password]); and :validator, a class that knows how to validate the data (SignUpValidator).

An instance of the Presenter object is Hash-like: it implements the Enumerable module, which means it has an #each method among many others; it also has a #[] method, which you can use to access values just like with the CGI params hash.

The business logic classes (SignUp in the below example) live under app/models and are tested as normal, except instead of requiring spec_helper they can likely require just rspec.

Validator classes (SignUpValidator in the below example) live under lib and must either descend from ActiveModel::Validator or implement the same interface (.kind, #kind, #validate that takes a record, and a constructor that takes a hash of options). They are also unit tested like normal and can likely get away with just requiring rspec instead of spec_helper. Sadly, in order to hook into the ActiveModel::Validations framework, you must pass the validator class itself instead of an object (SignUpValidator vs

Tutorial and Thought Process

A major idea of the presenter pattern is to break off the business logic from the view object, letting the view logic be a dumb instance that knows how to get, set, and validate values. The business logic can then query the presenter object for the values as needed.

Look, here's an example business object:

class SignUp
  attr_reader :user

  def initialize(presenter, account_builder = Account)
    @email        = presenter[:email]
    @password     = presenter[:password]
    @account_name = presenter[:account_name]

    @presenter       = presenter
    @account_builder = account_builder

  def save
    if presenter.valid?
      account = => account_name)
      @user = => email, :password => password) do |succeeded|
        presenter.add_errors(account.errors) unless succeeded


  attr_accessor :email, :password, :account_name, :account_builder, :presenter

It's just a class, which you can unit test as you please. A presenter object is passed in, then we pull the values out, make sure it's valid, and add errors to it as needed. This class does not deal directly with validations, state, or any of the ActiveModel nonsense.

Now you need to know how to use a Presenter object, so this is what the controller looks like:

class SignupsController < ApplicationController
  def new
    @sign_up = presenter

  def create
    @sign_up = presenter.with_params(params[:sign_up])
    db =

      redirect_to root_url
      render :new


  def presenter'sign_up',
                  :fields => [:email, :password, :account_name],
                  :validator => SignUpValidator)

In our new action we simply set the @sign_up i-var to an instance of the Presenter. In create we use that Presenter instance, adding CGI params in. Then we pass that to the SignUp class defined above and it's all boring from there.

The presenter method in the above example produces a new Presenter instance. This instance has a model name (sign_up), fields the form will handle (email, password, and account_name), and a validator (SignUpValidator). The validator is any instance of ActiveModel::Validator, for example:

class SignUpValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    record.errors.add(attribute, "can't be blank") if value.blank?

You, the author of the business logic class, are in charge of checking in on these validations and errors. For example, before saving any objects you should check Presenter#valid?. And after you've saved something to the database you should add any errors onto the presenter using Presenter#add_errors.


When writing your unit tests it'll be handy to have a mock presenter around, which is why we package a MockPresenter class for you to use. It gives you access to the #have_errors and #have_no_errors RSpec matchers.

describe SignUp, 'invalid' do
  let(:params) { { :email => '',
                   :password => 'bar',
                   :account_name => 'baz' } }
  let(:errors) { { :email => "can't be blank" } }
  let(:presenter) do => false,
                      :params => params,
                      :errors => errors)
  let(:account_builder) { => true) }

  subject {, account_builder) }

  it "does not save the account or user" do be_false

    presenter.should have_errors(errors)


Copyright 2011 Mike Burns. Distributed under the three-clause BSD license.

Please open a pull request on Github as needed. Be sure to update the ChangeLog and, if needed, the NEWS. We follow the GNU ChangeLog format.

Something went wrong with that request. Please try again.