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



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.