Sunday, September 16, 2007

Syntactic sugar for dealing with empty containers

In any web application, we're often just reading a collection of rows from the database and displaying it in the browser. Often times, we'll have code that looks like this:
<% unless @friends.empty? -%>
<% @friends.each do |friend| -%>
<li><%= h friend.username %></li>
<% end -%>
<% else -%>
No friends yet
<% end -%>
I don't know why, but this kinda gets to me, and doesn't look all that neat. I probably attribute it to having to upgrade and maintain a piece of C server code that was nested 8 or 9 layers deep all in one huge main(). It might be counter-productive, but I tried to see if I could do better.
<% if @friends.each do |friend| -%>
<li><%= h friend.username %></li>
<% end.empty? -%>
No friends yet
<% end -%>
Well, this is kinda nice in a way that it's only one hierarchy deep. When I look at it, one section is for what to display when there are elements in the list, and one is for when there isn't. I suppose your mileage may vary. However, I didn't like the "if" in front. It obscures the intent of displaying the list. So, in the pursuit of more counter-productivity and perhaps in the spirit of pseudo-altering the language, I tried this out:
<% @friends.each do |friend| -%>
<li><%= h friend.username %></li>
<% end.empty do -%>
No friends yet
<% end -%>
Well, that worked. I kinda like it. Since the message was so simple, I had wanted empty() to take a message, and just display it, but because it's a "%" and not a "%=", the message won't get displayed, so I had to do it in a block. In a way, it's almost like being able to write my own "else" statement. If I had used curly braces instead of "do/end", it might look pretty close. Here's the code for empty:
class Array
def empty(message = "")
if self.empty?
return block_given? ? (yield message) : message
end
end
end
Like it? Hate it? Tip!

2 comments:

  1. I like it, and I can definitely see situations in existing code where I could use it.

    ReplyDelete
  2. In the javascript world, both the Rhino book and the guy maintain JQuery give reasons why you shouldn't manipulate native objects in javascript. It's mainly for compatibility issues, since javascript runs in an environment where it shares the runtime with other scripts. However, that doesn't stop the guys at the Prototype library to add nice iterators like each, map, and inject into Array.

    In ruby however, this seems to be done--but with reservations. Rails itself has quite a bit of core extensions added on to basic classes, such as Array and Time.

    For now, it's nice to be able to have the syntactic sugar, since no one else has to deal with empty(). But I think if the code was an API or something in a shared environment, it would present lots of problems.

    ReplyDelete