Nothing or not
Posted by Nicholas Wed, 12 Dec 2007 19:22:00 GMT
Everyone loves writing web apps. They’re just amazing at doing everything. Ever.
One of the more interesting aspects of writing web applications is how often you see yourself doing the same thing over and over again.
It’s a very common task, for instance, to grab some list of records and loop through and display them on a page. But what happens when no records are found?
There are oft used methods for doing this.
The first of these is to simply hide the entire block of code from displaying. Such as follows:
<% unless @results.blank? %>
<% @results.each do | result | %>
...
<% end %>
<% end %>
The second method takes slightly more work, and lends itself to much more duplication across your app. This approach is to actually display a message stating that nothing has been found, such as thus:
<% unless @results.blank? %>
<% @results.each do | result | %>
...
<% end %>
<% else %>
<p class="empty">
Nothing to display!
<p>
<% end %>
It would be great if you could do the second version just as simply as you would the first. The obvious answer would be to use a helper. But how? How would you create something to do this?
The answer comes from two wonderfully useful helper methods made available to you by Rails. These, of course, are concat and capture.
So now that we have the building blocks, how would we put them together to create a useful new construct that can be used as in an almost identical way to the first solution above? Maybe we want something that would look like this:
<% unless_blank( @results ) do %>
<% @results.each do | result | %>
...
<% end %>
<% end %>
Well that’s simple enough. We get the above functionality by sticking the following code into our application helper.
def unless_blank( obj, &block )
content = unless obj.blank?
capture( &block )
else
%{ <p class="empty">Nothing found.</p> }
end
concat( content, block.binding )
end
Hmm. That’s actually kind of neat, but what is really going on here?
This isn’t mysterious at all. At a glance you will note that we’re just assigning the captured block to the content variable if our object is not blank, and if it is then we’re just using some html with a little message to state so. Right now some curious readers may question, then, why we actually need to capture our block at all? Why can’t we just call the block if our object is not blank, setting content to the results directly.
The nutshell answer to this question is that without using ‘capture’ we’re going to be displaying 2 copies of the results of block later on when we concatenate the results to our binding. I invite you to try different variations of this code, with and without the capture segment, to see the difference between the two at a glance.
But what about concat? Well, you may have also noticed in the above example that we weren’t doing erb output anywhere. That’s where concat comes into play. The content variable is taken and appended onto the end of our current erb string. The result of this is that whenever we use concat to add content to the end of our binding, we’re effectively (for all intents and purposes) doing:<%= content %>
These give us a powerful way to create helpers that can handle some of our more common view patterns with little trouble. So grab a towel and dry up those sopping wet views!