Friday, January 26, 2007

Conditional block-level helper to DRY up views

We're at RailsEdge in Reston and Bruce Williams and Marcel did a little talk on cleaning up views with block helpers, etc.

I wrote a helper of this flavor a while back to DRY up an article rendition issue, so I figured I would throw it up here.

Basically, we have a generic article view but have the need to swap out 'chunks' of the Article in different situations without creating a crazy cartesian product of rendition views.

View now looks like this:

<div class="article_module_head">
<% unless_partial @article.rendition, 'headline' do %>
<h2><%= @headline_override || @article.headline %></h2>
<% end %>
<% unless_partial @article.rendition, 'subhead' do %>
<% if @section %>
<h3><%= @section.title %></h3>
<% else %>
<h3><%= @article.subheadline %></h3>
<% end %>
<% end %>

Helper looks like this:

# Allows you to render a block of default code, but instead render a
# partial from a bucket of partials if it's there. Makes it so overriding
# a block of code in a template is as simple as creating a partial -- convention,
# etc.
# e.g.
# @article.rendition #=> 'foo'
# <% unless_partial @article.rendition, 'author' do %>
# <h2 class="article_module_attribution"><%= @article.time_string %></h2>
# <h2 class="article_module_attribution"><%= %></h2>
# <% end %>
# This block will first check for the _foo partial, then fall back to the block.
# Inline version (empty if no partial):
# <%= unless_partial @article.rendition, 'aftercopyright' %>
module ConditionalPartialHelper
def unless_partial(rendition, partial, &block)
output = render :partial => "#{controller.class.controller_path}/#{partial}/#{rendition}"
if block_given?
# block given, so they may not be using <%= on the helper call, so you can't just return it.
concat(output || '', block.binding)
# no block given, they used <%=
return output
rescue ActionView::ActionViewError # no overriding partial
yield if block_given?
# contingency for <%= on a with block--the block is doing the concatting
return ''

Hopefully the comment block explains it all.


codesujal said...


Not sure if you'll see this, but what's the performance impact of doing this check on a frequently traffic'd page? I realize that I could turn on caching, but I like to understand what's happening if caching were to fail/go away/otherwise suck.

I'm thinking about the cost for checking if a partial exists all the time. haven't had a chance to look into how it's implemented under the covers... thought maybe you guys may know.


Higgaion said...

I love it, but by catching that exception this helper got in the way of debugging my overriding partials. Is there another way? what I ended up with was a regex to match the missing template error message, and to raise all other exceptions again. this is less than ideal.