@awead

Technical notes and explications of code

(Yet Another Post About) Presenters in Rails

YAPAPIR! Lots of virtual ink has been spilled over presenters in Rails. My take on them that follows came about after our team decided to arrive at an adoption strategy. We’d been using them in other projects, and were starting to go down a road of implementing them different ways, when we though we should pick one way and stick to it.

Here’s a summary of what I’ve found are the different ways you can do presenters and I’ll conclude with the way I think they ought to be done.

They’re Really Decorators

If you read about presenters, you’ll probably see them side-by-side with references to decorators. According to wikipedia: [1]

In object-oriented programming, the decorator pattern (also known as Wrapper, an alternative naming shared with the Adapter pattern) is a design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class.

Presenters are going to do something with your model, usually an ActiveRecord object, and do something with it that will modify it, yet leave it intact in its original state. The best use case would be display logic such as someone’s name.

1
2
3
4
5
6
7
class Person < ActiveRecord::Base
  attr_acessor :first_name, :last_name, :salutation

  def full_name
    [salutation, first_name, last_name].join(" ")
  end
end

full_name doesn’t really have any bearing on the Person data model, it’s purely for display. A while back, I probably would have put this in a helper. However, the helper doesn’t really know anything about Person, and you’d have to make some assumptions about how your controller might be instantiating Person:

1
2
3
4
5
module PersonHelper
  def full_name
    [@person.salutation, @person.first_name, @person.last_name].join(" ")
  end
end

What if @person is nil? What if salutation is nil? So a simple display method can quickly morph into a complex logic problem. Enter the presenter: put all that logic and code in another class separate from the model.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class PersonPresenter
  delegate :first_name, :last_name, to: :model

  attr_reader :model

  def initialize(model)
    @model = model
  end

  def full_name
    [salutation, first_name, last_name].join(" ")
  end

  def salutation
    model.salutation || inferred_salutation
  end

  private

    # Let's assume that ::gender is a required attribute
    def inferred_salutation
      if model.gender == "male"
        "Mr."
      else
        "Ms."
      end
    end
end

In that brief example above, I’ve already made a bunch of assumptions about how I’m going to build my presenters, but there are many ways I could have implemented the above solution. Let’s look into them.

The Approaches

The Model Presenter

  • instantiated by the controller, helper, or view
  • operates on only one model
  • initialized with an instance of the model it’s presenting

The Assembler

  • instantiated by the controller
  • assembles access to multiple models within one controller

The Templative Presenter

  • uses either of above strategies
  • adds the current ActionView::Context as a second parameter

Guidelines

Requirements

I’ll start with Sandi Metz’s rules which cover more than just presenters, except the last one:

  • Classes can be no longer than one hundred lines of code.
  • Methods can be no longer than five lines of code.
  • Pass no more than four parameters into a method. Hash options are parameters.
  • Controllers can instantiate only one object.

So basically, if you’re using presenters, your controllers can only do one thing: instantiate a presenter. This might seem a bit draconian, but there’s an advantage here. Have you ever dealt with a controller that’s doing so much stuff you don’t know where what’s getting done where? Well, if your controller can only do one thing, then it kinda solves that problem for you.

You can break these rules if you absolutely have to, but the idea is that because these limits are there it informs you as to the complexity of your methods and classes. In other words, if you have a class with more that 100 lines, maybe there’s underlying cause that should be addressed instead of just plowing ahead with a really big class. On the other hand, if you’ve thought it through and can make a well-thought case for a long class, you’re free to have one.

Very Strong Recommendations

Use POROs

Ruby has a enough built-in features to cover the use cases, which makes additional tooling, while neat, magical, and cool, ultimately superfluous. That isn’t to say you can’t use them. However, there’s enough in the stock Ruby toolbag to fix any problem

  • object-oriented design through composition or inheritance
  • modules
  • delegation methods
  • judicious use of monkeypatching as a last resort

Use POR (plain old Rails)

Rails has views to render HTML. Use them for that. Partials can subdivide this HTML layer into logical units that fit your use case or design rules. Rails has helpers, and no, they’re not evil. Use them, appropriately, particularly for specific cases that cut across multiple presenters and do not depend on a data conditional in your model.

A view should only use one presenter

If you have a view that needs more that one presenter, you have a presenter problem. You can build another presenter. If you have a lot of presenters, think about strategies like inheritance or composition in order to DRY up your presenters. If you’re finding that it’s difficult to construct a single presenter for a view, maybe that’s an indication that the view itself is too complicated and needs some refactoring.

Meh

Only instantiate your presenters in controllers/views/helpers

While I feel awkward when I instantiate anything in view or helper, I can’t really find a coherent argument as to why it’s a bad idea. So, feel free to instantiate your presenters anywhere, but I still think there should be one presenter per view, or view partial.

Presenters should not contain HTML

Probably, yes, but there could be reasons for it such as sharing a display behavior across presenters, but helpers are a better fit there perhaps?

Views should not contain logic

Ideally, maybe. Practically, though, a small amount of logic doesn’t seem to disrupt things:

1
2
3
<h1>User Information</h1>
<p>Hello, <%= presenter.first_name %></p>
<%= render "activity" if presenter.has_activity? %>

Iterators present their own problems, but shouldn’t be too disruptive either:

1
2
3
4
5
6
7
<h2>Recent Activity</h2>
<table>
<tr><th>Date</th><th>Action</th></tr>
<% presenter.user_activity.each do |activity| %>
  <tr><td><%= activity.date %></td><td><%= activity.action %></td></tr>
<% end %>
</table>

Or if you want to unify all your table displays, consider some helpers:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module TabularHelpers

  def header_row(*cols)
    content_tag :tr do
      cols.map |col| {  |col| content_tag :th, col, class: "my-header-class" }
    end
  end

  def table_row(*cols)
    content_tag :tr do
      cols.map |col| {  |col| content_tag :td, col, class: "my-row-class" }
    end
  end
end

Then your table views become more reusable:

1
2
3
4
5
<h2>Recent Activity</h2>
<table>
  <%= header_row("Date", "Action")
  <%= presenter.user_activity.map { |activity| table_row(activity.date, activity.action) } %>
</table>

Tools

Draper

Gem on github. Instantiates a decorator based on your ActiveRecord model. There’s some good discussion about its pros and cons

Curly

Gem on github Rails meets handlebars.js

Notes

[1] https://en.wikipedia.org/wiki/Decorator_pattern

References

Corbel, Guirec, “When I use Helpers, Partials, Presenters and Decorators,” July 2013, https://coderwall.com/p/jx9tca/when-i-use-helpers-partials-presenters-and-decorators

Fields, Jay, “Rails: Presenter Pattern,” 9/16/2007, http://blog.jayfields.com/2007/03/rails-presenter-pattern.html

Fowler, Martin, “Presentation Model,” 7/19/2004, http://martinfowler.com/eaaDev/PresentationModel.html

Goswami, Mainak, “Gang of Four – Decorate with Decorator Design Pattern,” https://dzone.com/articles/gang-four-%E2%80%93-decorate-decorator

Hock-Isaza, “Mixing Presenters and Helper,” http://blog.nhocki.com/2012/05/08/mixing-presenters-and-helpers/

Murray, Robert, “Decorators, Presenters, Delegators and Rails,” 1/23/2014, https://robertomurray.co.uk/blog/2014/decorators-presenters-delegators-rails/

Seitz, Jeremy, “Thoughts about Rails Presenters,” https://gist.github.com/somebox/5a7ebf56e3236372eec4

Vlasveld, Roemer, “http://www.inspire.nl/blog/rails-presenters-filling-the-model-view-controller-gap/

Wang, Eugene, “Presenting the Rails Presenter Pattern,” 9/26/2013, http://eewang.github.io/blog/2013/09/26/presenting-the-rails-presenter-pattern/