Ruby on Rails tip: Simplify your templates with helper methods

Do you find yourself writing the same code in several templates? For example, in one template you have:

<h2 class='page_title'>Contacts</h2>

and in another template, you have:

<h2 class='page_title'>Goals and Action Items</h2>

Simplify your code by writing helper methods. In the case above, you can have this method:

def page_title(title)
  content_tag('h2', title, :class =&gt; 'page_title') if title
end

Then, in your template, your code now becomes:

<%= page_title 'Contacts' %>

Now, let’s say aside from the ‘class’ attribute, you want to have the ability to add additional html options like an ‘id’ or ‘style’. Let’s modify our method and add a hash of options.

def page_title(title, options = {})
  content_tag('h2', title, {:class =&gt; 'page_title'}.merge(options)) if title
end

In your template, you can now do this:

<%= page_title 'Contacts', :id => 'id_of_contact' %>

and the generated HTML becomes:

<h2 class="page_title">Contacts</h2>

The ‘content_tag’ approach works prettly neat if you have simple text. Sometimes, you may need to display several ‘p’ and ‘h2’ tags. For example, a warning message can be written like:

<div class='warning'>
  <h2>Not contacts found</h2>
  <p>If your search has returned no results, please check your criteria
    for misspellings.</p>
  <p></p>
</div>

You can try to use ‘content_tag’ but that would be messy and uncool🙂

Instead, use Ruby’s code block. Not only it simplifies your code but you’ll learn how to use Ruby’s powerful ‘yield’ method. In your helper file, your ‘warning’ method becomes:

def warning(brief, &amp;block)
  concat("<div class='warning'>", block.binding)
  concat(content_tag('h2', brief), block.binding)
  yield # now invoke the code in the 'block'
  concat('</div>', block.binding)
end

In your template, you’re warning becomes:

  <p>If your search has returned no results, please check your criteria
    for misspellings.</p>

Note this time, you use ‘<% %>’, without ‘=’.

OK, I know there’s not much action here. So let’s try another example. Let’s say you want to write a row of names, and each row will have a URL to an edit page. The typical way to do this is:

      <ul>
        <% for person in @people -%>
          <li><%= link_to person.name, :action => 'edit', :id => person %></li> 
        <% end -%>
      </ul>

(A variation of the above would be to use a partial.)

Let’s create another helper method to write our code above:

def list_items(objects, &amp;block)
  concat('<ul>', block.binding)
  for object in objects
    concat('<li>', block.binding)
    yield(object) # you pass 'object' to the 'block'
    concat('</li>', block.binding)
  end
  concat('</ul>', block.binding)
end

In your template, you can now do this:

<% list_items @people do |person| -%>
  <%= link_to person.name, :action => 'edit', :id => person %>
<% end -%>

Should you decide to use ‘table’ instead of ‘ul’, then all you need to modify is your ‘list_items’ method. You can also improve ‘list_items’ by displaying a message if the array is empty. Or, include CSS attributes and extra ‘div’ to beautify the list.

If you use generator (or scaffold), every controller will have a corresponding helper file. If you have an ‘account_controller.rb’, you will also have ‘account_helper.rb’ under the ‘helpers’ folder. If you have some methods that you want available in all templates, you can put them in ‘application_helper.rb’ or you can specify the helper in your application controller (application.rb)

helper :my_helper

Now, start refactoring those templates!!!

5 thoughts on “Ruby on Rails tip: Simplify your templates with helper methods

  1. Thanks for this Jason. The resulting code with capture is very clean indeed. Can’t wait to clean-up my code.

  2. alex band has a new five-track EP
    vote for ONLY ONE and REST OF OUR LIVES
    please visit alexmaxband.com
    plus, alex band has an upcoming album in 2009 entitled WE’VE ALL BEEN THERE

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s