Dirty Views? Clean them up!

Posted on March 27, 2007

I feel the area where I need to improve most is in cleaning up my views. I feel like I’ve done a good job cleaning up my controllers and models, thanks to posts like Skinny Controller, Fat Model.

I have known about the content_for in rails views. Which basically allows you to have a layout like so:

<html>
    <head>
    </head>

    <body>
        <div id="wrapper">
            <div id="header">
                <% yield :header %>
            </div>

            <div id="content">
                <%= yield %>
            </div>

            <div id="sidebar">
                <% yield :sidebar %>
            </div>

            <div id="footer">
                <% yield :footer %>
            </div>
        </div>
    </body>
</html>

And in your view the following:


  <% content_for :header do %>
       This would appear for the yield :header call
  <% end %>

  <% content_for :sidebar do %>
      This is the sidebar content
   <% end %>

  <% content_for :footer do %>
     This is the footer content
  <% end %>

   <%# the rest would just be the content for the plain yield call %>
    This is would be my main content

This is nice, but I wanted to find a way to supply default content and this is what I came up with.

  def yield_with_default(yield_on, content=nil, &block)
    yield_content = eval "@content_for_#{yield_on} || ''" 
    if block_given?(&block)
      concat(yield_content.empty? ? capture(&block) : yield_content, block.binding)
    else
      Binding.of_caller do |binding|
        concat(yield_content.empty? ? content : yield_content, binding)
      end
    end
  end

I placed this in my application_helper.rb file. And now I can add something like this to my view.


    # with a block
    <% yield_with_default :header do %>
        This is my DEFAULT header content
    <% end %>

   # without a block
   <% yield_with_default :header, "This would be my default content" %>

This feels pretty clean to me. But if anybody has any other suggestions I would love to hear them.

I am also looking for more information on best practices with views in Rails. There doesn’t seem to be much information on the subject.

UPDATED: 04/01/2007 – forgot the do in the content_for statements

Rspec'ing Rails Views

Posted on February 12, 2007

I've been using rspec for testing rails apps for about a month now. However, I hadn't tried to spec out the views. I decided to starting specing out the views today, and ran into a small obstacle.

I am currently using restful authentication plugin and I have snippets in my view like:


    <% if logged_in? %>
         ....
    <% end %>

Everything is good, until I tried to write my first view test. Calling:

render "controller/template"

I received a missing method error on "logged_in?". The problem is that rspec views are tested in isolation from the controller. This means my include AuthenticatedSystem in the application controller doesn't get loaded and that's where the "logged_in?" method is mixed in.

I did find out that the application_helper.rb and <controller>_helper.rb (where <controller> is the current controller associated with the template being tested) get loaded, so I could define the "logged_in?" method in either of the two helper classes. But the problem with that is that I would have "logged_in?" defined in a couple of places just to run my rspecs.

Digging around in the rspec for rails code base, I found that you can get access to the controller via @controller and the template via @controller.template.

Then a light came on! I could stub out the call to logged_in? on the template like so:

   @controller.template.stub!(:logged_in?).and_return(true)

After doing that, everything worked fine. I also found it came in handy for stubbing out other template calls.