Tuxedo: 150 LOC Presenter Logic

tuxedo-meme TL;DR Tuxedo a gem that implements Decorator/Presenter pattern in 150 LOC.

Presenters, Decorators, view helpers,... all names that try to do the same thing. Keep logic out of your views where it's difficult to test and promote reuse of helpers.

The earliest mention of Presenter came from Jay Fields in a blog post titled: Rails: Presenter Pattern created in 2007! Another great description is RailsConf 2014 Talk.

Time warp back to current DateTime and I must say we still didn't nail this one perfectly:

  • Draper: Most popular one but I feel it does to much black magic. It hides the true nature of the object. You are not working with @article which is an Article instance but rather with a ArticleDecorator instance. I also feel shameful when I have to initiate a Decorator in my controller. Why does my controller need to know how I'm going to decorate (read visualize) the @article anyway? And why do I need to define my associations again in my decorators? And why would I want to do that anyway? So many questions...
  • Cells: Tried it but for me this was going a step to far, for simply changing the background colour of a link, I don't need a new view framework on top of Rails. But I do admit it does sound good and the API looks great!
  • active_decorate: Automagically mixes decorator classes into the corresponding model.

Tuxedo

Dress up a boring looking instance with a Tuxedo suite and you have something nice to look at. Tuxedo is a simple gem that allows you to wrap any ruby object in a presenter. It attempts to use as little black magic as possible.

Under the hood Tuxedo uses Charlatan which turns any object into a proxy delegating to another object. The real good part is that it defines two helpers in ActionView: presenter_for and prac. Let's see those two in action, for more information on general usage head over to Tuxedo.

Presenter For

def presenter_for(model, klass = nil)  
  return if model.nil?
  klass ||= "#{model.class.name}Presenter".constantize
  presenter = klass.new(model, self)
  yield presenter if block_given?
  presenter      
end  

This method will try to lookup a Presenter class using the provided models class name. Given an instance of Car it will look for a CarPresenter. When a block is given it will yield the presenter else a new presenter instance is returned.

Let's assume we have defined the following presenter somewhere in our Rails application:

class CarPresenter  
  include Tuxedo

  def name_with_icon
    [to_car_icon(car.model), car.name].join(' ')
  end
end  

Using presenter_for in our views we can easily start using a presenter where we need one! For example when displaying a table, I always wrap my row in a presenter block.

# file: views/cars/_car.erb
<%= presenter_for(car) do |present| %>  
<tr>  
  <td><%= present.name_with_icon %></td>
  ...
</tr>  
<% end %>  

This shows clearly "hey, I'm wrapping my car in a presenter". I'm not really a big fan of assigning variables in my views but some people find this more declarative:

# file: views/cars/_car.erb
<% present = presenter_for(car) %>  
<tr>  
  <td><%= present.name_with_icon %></td>
  ...
</tr>  

PRAC (Present-and-Call)

Using the above example, and let's assume we only need to display the car's name with icon. Using prac we can easily call a presenter method for a provided instance. Using the same CarPresenter example:

# file: views/cars/_car.erb
<tr>  
  <td><%= prac(car, :name_with_icon) %></td>
  ...
</tr>  

PRAC basically translates to a presenter_for + call but it does extra nil checks behind the scenes:

def prac(object, method, *args)  
  return if object.nil? || method.nil?
  presenter_for(object).send(method.to_sym, *args)
end  

This is quite handy as it allows me to easily decorate associations without having to worry about nil's.

Bonus round

You can even use prac inside a presenter! This is really handy as it allows to decorate an association easily. Take the following two presenters:

class PartPresenter  
  include Tuxedo

  def part_icon
    to_part_icon(part)
  end
end

class CarPresenter  
  include Tuxedo

  def parts_with_icons
    car.parts.map do |part|
      parc(part, :part_icon)
    end.join('<br />').html_safe
  end
end  

Et voilà, calling presenter_for(car).parts_with_icons will give me back all the parts icons joined with a brake line. A decorated association without any black magic!

Final Thoughts

First I would like to thank my colleague @mcls github for refactoring and introducing Charlatan! He also came up with the awesome name Tuxedo. I find this pattern very useful since it allows to easily test my view helpers and simplify my view code. But when presenting associations it can easily become a mess of prac calls all over the place. It can also introduce a tighter coupling between different parts of your application.

Comments powered by Disqus