Providing default content in Rails layouts [04092007]
Another quick tip for Rails view developers. We commonly employ the following snippet in our layouts:
<%= (out=yield :sidebar) ? out : render(:partial => 'sidebar') %>
This thorny little one-liner says, if the view we're rendering for this layout has content_for :sidebar, show that content. Otherwise show the default content (which we've tucked away in the 'sidebar' partial).
It's a handy pattern because it lets your views opt to modify some aspect of the layout, without having to — they can go with the default sidebar content if they don't care.
However. Firstly, it's a bit unsightly. Secondly, you must put the default content in a partial (unless it's short enough to just be a string — an approach that is arguably uglier). In most cases, the default content for a block in a layout is best described in that layout, so that you don't have to go hunting down other files to get a picture of what the layout displays. Here's a better way:
<% default_content_for :sidebar do %> <p>This is the default sidebar content! (et cetera)</p> <% end %>
So now, if the view specifies some content_for :sidebar, the contents of the block above (ie, the <p> tag) will not be rendered. If the view doesn't do that, then the layout will render the contents of the block.
If you'd prefer this idiom in your views, drop the following method into application_helper.rb:
def default_content_for(name, &block)
name = name.kind_of?(Symbol) ? ":#{name}" : name
out = eval("yield #{name}", block.binding)
concat(out || capture(&block), block.binding)
end
This method is full of bindings and captures, which you can read up on at your leisure, or feel free to ask questions in the comments.
Thanks, the 'defaultcontentfor' is exactly what I was looking for. Keeping the default text in the layout really helps prevent spaghetti templates.
Great tip, thanks.
Thanks for the tips, here - just found these pretty helpful in cleaning up the application layout of project I'm working on.
- Scott
There’s an easier way of course….
This is ruby after all…
<%= yield :sidebar || render(:partial => ‘sidebar’) %>
Will do the trick nicely :)
That definitely works, Keith. Is it as declarative as saying “Here’s the default content, right in this block”?
Up to you. Arguing over code aesthetics is a mug’s game.
Incidentally, the default_content_for method can be made a bit simpler now that concat no longer requires a binding.
Elegant! It keeps the default content where it naturally belongs and makes the source immediately understandable.