Content_for is the new GOTO
I don't like content_for. Your view code jumps around up and down files and makes it hard to work out what's going on. It smells a lot like GOTO. When was the last time you saw someone recommend you use a GOTO?
content_for :javascript and content_for :css
Use of content_for can be easily avoided, at least for including CSS and Javascript files. Include the controller name and action name in the body tag in your layout and properly qualify your CSS declarations.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title><%= page_title %></title>
<meta http-equiv="Content-Language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="/stylesheets/simple.css" media="screen" />
</head>
<body id="<%= "#{controller.controller_name.tableize.singularize}_#{controller.action_name}" %> class="<%= "#{controller.controller_name.tableize.singularize} #{controller.action_name}" %>">
<%= yield %>
</body>
</html>
Say you were looking at the Posts views in your app, you can now style these using something like this.
.post.index .article .title {
font-size: 1.25em;
}
.post.show .article .title {
font-size: 0.9em;
}
Or, in case you need to support browsers that don't let you specify two classes as a selector for a single element, you can write it like this.
#post_index .article .title {
font-size: 1.25em;
}
#post_show .article .title {
font-size: 0.9em;
}
Since all your Javascript is unobtrusive anyway (right?), it should be pretty easy to qualify the selectors used there with the same CSS selectors shown above.
As an added bonus, by specifying your Javascript / CSS like the above you can package it all in one Javascript or CSS file on deployment to your production environment and save yourself a bunch of HTTP requests.
Leave feedback...
Commenting is closed for this article.

I hear you on the body id / class stuff, which I use on almost every project on which I work.
content_for is useful when your layout isn’t a straightforward header – content – footer.
e.g.
<header_gubbins />
<div id=“some stuff”>
<%= yield :the_stuff_i_want_to_change_for_different_pages %>
</div>
<ul id=“navigation”>
<li/>
</ul>
<%= yield :content %>
<footer_gubbins />
…otherwise you end up having to repeat yourself calling partials or helpers in your views.
My guess for the “usefulness” of this comes when one page on your site needs a big ‘ol JavaScript library, and you only want to load that on the pages that need it (so you minimise the footprint of the front page). The ability to inject header-level loading from a partial is kinda handy for ensuring that you only burden the visitor with what they need as they need it, but I’d agree that `content_for`isn’t the best place for this.
Perhaps delegating this out to a helper that checks for some flags being set (@load_jquery == true) and writes out the header link and script tags appropriately?
Or is that just as bad?
With haml, you can use the page_id and page_class helper: %body { :id => page_id, :class => page_class }
which basically does the same you did above.
Though I still like to use content_for, e.g. to add stuff like keywords or rss autodiscovery tags to the head section based on the displayed content (e.g. an autodiscovery tag for a comment feed of the currently viewed article in a blog, but only if comments are allowed).
@Andrew Why not call something like this?
<%= render :partial => “#{controller.controller_name.tableize}/sidebar” %>
I can’t think of a use case for using content_for that isn’t better served by using something that’s less confusing. I’d love to be proved wrong though – can you provide a use case?
@Jonathan I think that a Javascript-heavy page would call for a separate Javascript-enabled layout. Also, with proper caching and a bit of planning you might not need to worry so much about the large size of a Javascript library: why not have the homepage load the mammoth file asynchronously and cache it? I’m not sure if this approach would work, but I think I might check it out.
@Andreas For stuff like keywords you can simple set an instance variable in your view.
Layout: <%= @keywords %>
View / partial: <% @keywords = “foo, bar, baz, quux” -%>
For the auto-discovery tag I think I’d either use a different layout for a resource that had an atom representation, or I’d render a partial in the layout something like:
<%= render :partial => “#{controller.controller_name.tableize}/metadata” if File.exists?(”#{RAILS_ROOT}/app/views/#{controller.controller_name.tableize}/metadata”) %>I really don’t get it: You say I could use something like this:
Layout: <%= @keywords %>
View / partial: <% @keywords = “foo, bar, baz, quux” -%>
How is content_for worse than this? How is this better than content_for? I could use this as a content_for substitute, too.
I believe content_for (being a closure) is still superior to rendering stuff into a string and displaying it somewhere else.
With
<%= render :partial => “#{controller.controller_name.tableize}/sidebar” %>
I’d have to have a sidebar template for every controller – why should I want that? When I need something in the sidebar that is in the context of one action (related articles, friends of this user, for example), content_for is my best choice. Messing around with too many partial can clutter up the views directories very fast.
But in the end, I assume, it’s simply a matter of taste.
So, I don’t share your opinion at all, but thanks for making me think :-)
@Chris You’re right, I’m confusing the issue here. My bug-bear with content_for is that when used with Javascript and CSS files it makes it hard for me to package the assets into just a few files. For something like meta keywords it may be appropriate, although I still think using an instance variable is more readable (as an aside, content_for renders the closure to a string which is captured in a @content_for_xxx instance variable).
I’m still not convinced about using content_for but I think that’s because I’d be reluctant to have some context-specific content placed outside the central page context. It probably is a matter of taste so I’ll accept that I may not be right in all cases, but for the majority of the time I’m going to try to avoid it.
Also, because content_for returns to the calling line after it completes it’s more of a GOSUB than GOTO.
I’ll now sit in a corner for making ruby sound like Applesoft basic.