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 %>
...
</div>
...


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"><%= @article.author %></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)
begin
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)
else
# no block given, they used <%=
return output
end
rescue ActionView::ActionViewError # no overriding partial
yield if block_given?
end
# contingency for <%= on a with block--the block is doing the concatting
return ''
end
end


Hopefully the comment block explains it all.

2 comments:

codesujal said...

Hey,

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.

Sujal

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.