Naming view helper methods in Rails can be difficult. What do you think about the following helpers?
Since all helpers live under the same scope, they all need to be named differently to avoid name collision. This can become tedious as the number of models grow.
For me personally, these two issues always bugged me when creating a helper method:
- I don’t know where to put the helper method.
- I worry about name collision.
Reinventing the wheel
People thought of ways to organize these helper methods, which are now commonly called presenters, decorators or exhibiters.
Built-in helper methods all reside within view-context objects. So for presenter to work it needs access to the view-context object. However passing it for every helper call can be tedius. I liked how draper and active_decorator grabs that view_context for you, so you won’t have to keep passing it to presenter when calling it.
However I see that its effort in decorating the model object can easily leak. For example would you remember to also decorate the association objects?
I wanted something else instead, a presenter object which contains only the helper methods. I add a
presenter() method in the model object to access the presenter object. No decoration and less issues. The
presenter() object also takes care of caching the presenter object, so you only instantiate a presenter per model once.
So I made my changes on top the active_decorator, and called it lulalala_presenter (all the good names are taken already).
First install by putting this in the Gemfile:
Say you want to add a
title() helper method for auction, which takes care of truncation and html cleansing. I simply create a AuctionPresenter class. Remember to subclas it with
module AuctionPresenter < LulalalaPresenter::Base
h.truncate(model.body, length: 40)
And then in the view I call it like this
<%= @auction.presenter.title %>
I think it is good to start with less magic, but allow user to add magic if they want more convenience.
By default you use
h to access all the build-in helper methods, and
model to access the model object. If you are lazy you can choose to add stuff in your presenter so you can type less. For example you can delegate a few model attributes:
delegate :body, to: model
You can probably also automatically delegate everything to models using other delegation libraries.
At the end I quite like my results. For the top example, now I have
Having to type
.presenter when calling helpers isn’t so bad as I first thought. This makes it very obvious that the method belongs to presenter, so you won’t ever mix up a helper methods with model methods.
My two goals are also fulfilled. If I need an
og_image_tag helper for my auction, I immediately know I should put it in the
AuctionPresenter, and I can name it without worrying about prefixing model names.