Encapsulate common functionality with blocks and ERB

You might find yourself adopting certain layout standards throughout your application. In an application I'm working on, I display a lot of tabular data in styled boxex; usually at least two per page. I found myself repeating this common pattern:

<table class="list">
  <tr>
    <th colspan="3">
      <%= image_tag "item", :size => "16×16" %>
      Items: <%= @items.size %>
    </th>
  </tr>
  <%= render :partial => "item", :collection => @items %>
</table>

Using a helper method, you can encapsulate most of this functionality into something more manageable:

# In your ApplicationHelper
def show_table(title, options = {}, &block)
  image = options[:image] ? image_tag(options[:image], :size => "16×16") || ""

  content = capture(&block)
  concat(tag(:table, { :class => "list" }, true), block.binding)
  concat(content_tag(:tr, content_tag(:th, image + title, :colspan => options[:colspan] || 1)), block.binding)
  concat(content, block.binding)
  concat("</table>", block.binding)
end

<!– In your view –>
<% show_table "Items: #{@items.size}", :image => "item", :colspan => 3 do %>
  <%= render :partial => "item", :collection => @items %>
<% end %>

Obviously, this can still be cleaned up a bit if you're only ever going to display a table for list data (in my case, I'm not), but you get the idea. Blocks in ERB make things much cleaner and more maintainable in the long run.

Leave a Reply