Using Helpers

Earlier we said that it’s OK to put code in templates. Now we’re going to modify that statement. It’s perfectly acceptable to put some code in templates, because that’s what makes them dynamic. However, it’s poor style to put too much code in templates. Three main reasons for this stand out. First, the more code we put in the view side of our application, the easier it is to let discipline slip and start adding application-level functionality to the template code. This is definitely poor form, because we want to put application components in the controller and model layers so that it is available everywhere. This will pay off when we add new ways of viewing the application.

The second reason is that html.erb is basically HTML. When we edit it, we’re editing an HTML file. If we have the luxury of having professional designers create our layouts, they’ll want to work with HTML. Putting a bunch of Ruby code in there just makes it hard to work with.

The final reason is that code embedded in views is hard to test, whereas code split out into helper modules can be isolated and tested as individual units. Rails provides a nice compromise in the form of helpers. A helper is simply a module containing methods that assist a view. Helper methods are output-centric and exist to generate HTML or XML, or JavaScript, which are helpers that extend the behavior of a template.

Your own helpers

By default, each controller gets its own helper module. Additionally, there is an application-wide helper named application_helper.rb. Rails makes certain assumptions to help link the helpers into the controller and its views. While all view helpers are available to all controllers, it often is good practice to organize helpers. Helpers that are unique to the views associated with the ProductController tend to be placed in a helper module called ProductHelper in the file product_helper.rb in the app/helpers directory. You don’t have to remember all these details. The rails generate controller script creates a stub helper module automatically.

In Hiding an Empty Cart with a Custom Helper, we created one such helper method named hidden_div_if(), which enabled us to hide the cart under specified conditions. We can use the same technique to clean up the application layout a bit. Currently, we have the following:

<h3><%= @page_title || "Pragmatic Store" %></h3>

Let’s move the code that works out the page title into a helper method. Because we’re in the store controller, we edit the store_helper.rb file in app/helpers:

module StoreHelper
  def page_title
    @page_title || "Pragmatic Store" 
  end
end

Now the view code simply calls the helper method:

<h3><%= page_title %></h3>

Helpers for formatting and linking

Rails comes with a bunch of built-in helper methods that are available to all views. We’ll only touch on the highlights here, but you’ll probably want to look at the Action View RDoc for the specifics. There’s a lot of functionality in there.

Aside from the general convenience these helpers provide, many of them also handle internationalization and localization. In Internationalization, we translated much of the application. Many of the helpers we used handled that for us, such as number_to_currency(). It’s always a good practice to use Rails helpers where they are appropriate, even if it seems just as easy to hard-code the output we want.

Formating helpers

One set of helper methods deals with dates, numbers, and text:

<%= distance_of_time_in_words(Time.now, Time.local(2016, 12, 25)) %>

4 months

<%= distance_of_time_in_words(Time.now, Time.now + 33, include_seconds: false) %>

1 minute

<%= distance_of_time_in_words(Time.now, Time.now + 33, include_seconds: true) %>

Half a minute

<%= time_ago_in_words(Time.local(2012, 12, 25)) %>

7 months

<%= number_to_currency(123.45) %>

$123.45

<%= number_to_currency(234.56, unit: "CAN$", precision: 0) %>

CAN$235

<%= number_to_human_size(123_456) %>

120.6 KB

<%= number_to_percentage(66.66666) %>

66.667%

<%= number_to_percentage(66.66666, precision: 1) %>

66.7%

<%= number_to_phone(2125551212) %>

212-555-1212

<%= number_to_phone(2125551212, area_code: true, delimiter: " ") %>

(212) 555 1212

<%= number_with_delimiter(12345678) %>

12,345,678

<%= number_with_delimiter(12345678, delimiter: "_") %>

12_345_678

<%= number_with_precision(50.0/3, precision: 2) %>

16.67

The debug() method dumps out its parameter using YAML and escapes the result so it can be displayed in an HTML page. This can help when trying to look at the values in model objects or request parameters:

<%= debug(params) %>
--- !ruby/hash:HashWithIndifferentAccess
name: Dave 
language: Ruby 
action: objects 
controller: test

Yet another set of helpers deals with text. There are methods to truncate strings and highlight words in a string:

  • <%= simple_format(@trees) %>

    Formats a string, honoring line and paragraph breaks. We could give it the plain text of the Joyce Kilmer poem Trees,8 and it would add the HTML to format it as follows:

    I think that I shall never see
    A poem lovely as a tree.

    A tree whose hungry mouth is prest
    Against the sweet earth’s flowing breast;

  • <%= excerpt(@trees, "lovely", 8) %>

    …A poem lovely as a tree…

  • <%= highlight(@trees, "tree") %>

    I think that I shall never see A poem lovely as a tree. A tree whose hungry mouth is prest Against the sweet earth’s flowing breast

  • <%= truncate(@trees, length: 20) %>

    I think that I sh… There’s a method to pluralize nouns:

  • <%= pluralize(1, "person") %> but <%= pluralize(2, "person") %>

    1 person but 2 people

If we’d like to be like fancy websites and automatically hyperlink URLs and email addresses, there are helpers to do that. Another one strips hyperlinks from the text.

Back in Making Prettier Listings, we saw how the cycle() helper can be used to return the successive values from a sequence each time it’s called, repeating the sequence as necessary. This is often used to create alternating styles for the rows in a table or list. The current_cycle() and reset_cycle() methods are also available.

Finally, if we’re writing something like a blog site or allowing users to add comments to our store, we could offer them the ability to create their text in Markdown (BlueCloth) or Textile (RedCloth) format. These are formatters that take text written in human-friendly markup and convert it into HTML.

Linking to other pages and resources

The ActionView::Helpers::AssetTagHelper and ActionView::Helpers::UrlHelper modules contain a number of methods that let us reference resources external to the current template. Of these, the most commonly used is link_to(), which creates a hyperlink to another action in our application:

<%= link_to "Add Comment", new_comments_path %>

The first parameter to link_to() is the text displayed for the link. The next is a string or hash specifying the link’s target.

An optional third parameter provides HTML attributes for the generated link:

<%= link_to "Delete", product_path(@product),
{ class: "dangerous", method: 'delete' } %>

This third parameter also supports two additional options that modify the behavior of the link. Each requires JavaScript to be enabled in the browser.

The :method option is a hack that allows us to make the link look to the application as if the request were created by a POST, PUT, PATCH, or DELETE, rather than the normal GET method. This is done by creating a chunk of JavaScript that submits the request when the link is clicked. If JavaScript is disabled in the browser, a GET will be generated.

The :data parameter allows us to set custom data attributes. The most commonly used one is the :confirm option, which takes a short message. If present, an unobtrusive JavaScript driver will display the message and get the user’s confirmation before the link is followed:

<%= link_to "Delete", product_path(@product), method: :delete,
data: { confirm: 'Are you sure?' }
%>

The button_to() method works the same as link_to() but generates a button in a self-contained form rather than a straight hyperlink. This is the preferred method of linking to actions that have side effects. However, these buttons live in their own forms, which imposes a couple of restrictions. They cannot appear inline, and they cannot appear inside other forms.

Rails has conditional linking methods that generate hyperlinks if some condition is met or just return the link text otherwise. The link_to_if() and link_to_unless() methods take a condition parameter, followed by the regular parameters to link_to. If the condition is true for link_to_if, or false for link_to_unless, a regular link will be created using the remaining parameters. If not, the name will be added as plain text with no hyperlink.

The link_to_unless_current() helper creates menus in sidebars where the current page name is shown as plain text and the other entries are hyperlinks:

Get hands-on with 1200+ tech skills courses.