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:

HTML:
  1. <h2 class='page_title'>Contacts</h2>

and in another template, you have:

HTML:
  1. <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:

RUBY:
  1. def page_title(title)
  2.   content_tag('h2', title, :class => 'page_title') if title
  3. end

Then, in your template, your code now becomes:

RUBY:
  1. <%= 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.

RUBY:
  1. def page_title(title, options = {})
  2.   content_tag('h2', title, {:class => 'page_title'}.merge(options)) if title
  3. end

In your template, you can now do this:

RUBY:
  1. <%= page_title 'Contacts', :id => 'id_of_contact' %>

and the generated HTML becomes:

HTML:
  1. <h2 class="page_title" id="id_of_contact">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:

RUBY:
  1. <div class='warning'>
  2.   <h2>Not contacts found</h2>
  3.   <p>If your search has returned no results, please check your criteria
  4.     for misspellings.</p>
  5.   <p><%= link_to 'Click here to do another search', search_url %></p>
  6. </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:

RUBY:
  1. def warning(brief, &block)
  2.   concat("<div class='warning'>", block.binding)
  3.   concat(content_tag('h2', brief), block.binding)
  4.   yield # now invoke the code in the 'block'
  5.   concat('</div>', block.binding)
  6. end

In your template, you're warning becomes:

RUBY:
  1. <% warning 'Not contacts found' do -%>
  2.   <p>If your search has returned no results, please check your criteria
  3.     for misspellings.</p>
  4.   <p><%= link_to 'Click here to do another search', search_url %></p>
  5. <% end -%>

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:

RUBY:
  1. <ul>
  2.   <% for person in @people -%>
  3.     <li><%= link_to person.name, :action => 'edit', :id => person %></li> 
  4.   <% end -%>
  5. </ul>

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

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

RUBY:
  1. def list_items(objects, &block)
  2.   concat('<ul>', block.binding)
  3.   for object in objects
  4.     concat('<li>', block.binding)
  5.     yield(object) # you pass 'object' to the 'block'
  6.     concat('</li>', block.binding)
  7.   end
  8.   concat('</ul>', block.binding)
  9. end

In your template, you can now do this:

RUBY:
  1. <% list_items @people do |person| -%>
  2.   <%= link_to person.name, :action => 'edit', :id => person %>
  3. <% 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)

RUBY:
  1. helper :my_helper

Now, start refactoring those templates!!!


Did you enjoy this post? Why not leave a comment below and continue the conversation, or subscribe to my feed and get articles like this delivered automatically each day to your feed reader. If you don't have a feed reader, you can always have these articles delivered to your email inbox every day. Click here to sign up.

Trackbacks & Pingbacks

No trackbacks/pingbacks yet.

Comments

Wow! This is helpful. Thanks!

What does the call to block.binding return?

Ryan Bates covered some of this in a recent podcast called “Blocks in View”. In it he shows how to avoid using the concat with HTML strings. It’s pretty clean. His podcast site is http://railscasts.com - specifically the podcast I mentioned is at http://railscasts.com/episodes/40.

Thanks for the post, by the way.

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

Leave a comment

Line and paragraph breaks automatic, e-mail address never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>


*
To prove you're a person (not a spam script), type the security word shown in the picture.
Anti-Spam Image