Decorators and the Draper Gem

What is a decorator?

A decorator (or presenter) is a class that is responsible for presenting the model logic to the view. The presenter is usually initialized in the controller and usually expects to receive a model object. The presenter can then be used in the view. For example:

# app/models/user.rb
class User < ApplicationRecord; end

# app/presenters/user_presenter.rb
class UserPresenter
  def full_name
    "#{user.first_name} #{user.last_name}"
  end
end

# app/controllers/users_controller.rb
class UsersController < ApplicationController
  def show
    user = User.find(params[:id])
    @presenter = UserPresenter.new(user)
  end
end

# app/views/users/show.html.erb
<h1><%= @presenter.full_name %>

In this example, full_name does not handle any system logic. The method is only concerned with formatting logic in a human readable way, for the user. Therefore, this method is only a presentation concern so it makes sense to pull it out of the model and put it into the UserPresenter.

Why should you separate presentation logic?

Many Rails applications will mix business logic and presentation logic inside their models. So why is it advantageous to separate business logic and presentation logic:

  • You may need multiple presentation options. For example, you may want to give the user an option to view their account on screen or to download it as a PDF. In this case you will need methods for formatting to HTML and methods for formatting to PDF. If these methods are all kept inside the model it will result in a lot of duplication. With presenters you can create multiple presenter classes to handle different formats.
  • You may want to re-write the presentation layer without touching the model layer. For example, you may decide that you want to re-write your Rails views using a front-end JavaScript framework. Having nicely separated presenters will make this task a lot easier.
  • It makes your system easier to understand. Large models make your system difficult to understand. When I open up a new class I don’t want to have to sift through methods that are just formatting HTML. I only want to know what the model is responsible for. Presenters allow developers to understand the system more easily.

Isn’t that what helpers are for?

Helpers are build into Rails. They allow you to define functions that can be used in your views. For example, you could definite a helper method to output a patients full name:

# app/helpers/patient_helper.rb
module PatientHelper
  def full_name(patient)
    "#{patient.full_name} #{patient.last_name}"
  end
end

All helpers are included into each of your Rails views, which makes them very convenient. However, helpers can become unwieldy when used on large projects.

Presenters solve this problem because they are defined on specific classes. This means that presentation methods are nicely contained within objects and only the relevant objects are initialized for relevant views.

Draper gem

Draper essentially makes it easier for you to use presenters in your Rails app. Here are some of the things that Draper handles for you:

  • Generates a decorator class every time you use rails to scaffold a new model
  • Provides the Draper::LazyHelpers module so that you can easily mixin all of the helper methods
  • Gives you an easy way to access and use decorators. To access the decorator for a user, with Draper, simply run: user.decorate.

Lets re-write the example above, using Draper:

# app/models/user.rb
class User < ApplicationRecord; end

# app/decorators/user_decorator.rb
class UserDecorator < Draper::Decorator
  def full_name
    "#{object.first_name} #{object.last_name}"
  end
end

# app/controllers/users_controller.rb
class UsersController < ApplicationController
  def show
    @user = User.find(params[:id]).decorate
  end
end

# app/views/users/show.html.erb
<h1><%= @user.full_name %>

Drawback of using presenters

One of the drawbacks of using presenters is that it can be hard to track which presenter is being used in a view template, especially if you have lots of partials. Look at this example:

<h1><%= @presenter.name %></h1>

From looking at this file it is hard to know which presenter is being used. If you were using a helper method directly then it would be much easier to search for the method by name. In this case, it’s difficult to find where the name method is defined.

The only way to mitigate this problem is to give your instance variables good names, when you set them in the controller.

Summary

A decorator/presenter is responsible for presenting the model logic to the view.

The reasons for separating out presentation logic are: it allows for multiple presentation options, it allows you to re-write the presentation layer without touching the model layer, it makes your system easier to understand.

Draper makes it easier to use presenters in Rails.

Be sure to give your presenters descriptive names so that it is easy to find the corresponding presenter class from the reference in the view template.