In Praise of Haml

Tagged:  •    •  

Our big celebrity charity news site runs on Rails, and I've just switched all the view code from erb to haml. I cut the total number of lines of view code from 2370 to 1788, which makes for a saving of 25%.

Note that I didn't add any helper methods in the process, so the comparison should be quite fair. If I exclude blank lines from the comparison, the ratio is about the same, and it's quite impressive given that a few of the views have significant text content inside them.

It took about half an hour to switch my brain over to thinking in Haml, and then it was plain sailing. To my eyes, the views are no less readable now, and are certainly more concise.

Deleting all those <% end %> tags was enormously satisfying, and almost made me miss Python syntax.

Here's an example of a partial from the site before and after its conversion:

<div class="section">
<% if (with_birthday_today = Celebrity.all_alive_with_birthday_today).any? %>
<h3>Today's birthdays</h3>
<ul>
  <% for celebrity in with_birthday_today %>
<li><%= link_to_item celebrity %> is <%= celebrity.age_in_years_today %> years old</li>
  <% end %>
</ul>
<% elsif (upcoming_birthdays = Celebrity.birthdays_in_next_month(3)).any? %>
<h3>Upcoming birthdays</h3>
<ul>
  <% for celebrity in upcoming_birthdays %>
<li><%= link_to_item celebrity %> on <%= celebrity.birth_date.to_s(:short) %></li>
  <% end %>
</ul>
<% end %>
</div>

and in HAML:

.section
  - if (with_birthday_today = Celebrity.all_alive_with_birthday_today).any?
    %h3 Today's birthdays
    %ul
      - for celebrity in with_birthday_today
        %li== #{link_to_item celebrity} is #{celebrity.age_in_years_today} years old
  - elsif (upcoming_birthdays = Celebrity.birthdays_in_next_month(3)).any?
    %h3 Upcoming birthdays
    %ul
      - for celebrity in upcoming_birthdays
        %li== #{link_to_item celebrity} on #{celebrity.birth_date.to_s(:short)}

Haml's just seen its 2.0 release, it's approximately as fast as Erb, and the error messages are really pretty good now. (Occasionally I left a trailing '%>', or forgot to indent after an "- if ...", and these produced slightly obscure error messages.)

The new release has also added handy filters for embedding Javascript, Erb and the like inside Haml templates. I'm glad I've finally jumped on the bandwagon.

Hi Steve,

Funny, I should come across your recent post with the text upcoming_birthdays in it. We're trying to implement a similar functionality in our model which contains a date of birth. It's not as easy as i thought to write this method, especially since i want to consider leap years n all. Do you mind sharing how you went about implementing your upcoming_birthdays method? I assume you can pass it a method on what defines the upcoming period.

Cheers,
Aditya

Aditya - for steves case i guess you could do :conditions => ['month(birthdate)=month(now())+1']. Would that work?

Here's what I actually do to figure out the upcoming birthdays:

    SELECT *, extract(days FROM age(birth_date)) AS days_hence FROM
celebrities
     WHERE still_alive = true AND extract(months FROM age(birth_date)) = 11
     ORDER BY extract(months FROM birth_date) ASC,
              extract(days FROM birth_date) ASC LIMIT ?

This gives you a list of celebrities whose age is N years, 11 months and M days, in decreasing order of M. It therefore gives you the people who are closest to reaching their next birthday. I'm not sure how to generalise this such that it would also show people who are more than 1 month from their next birthday.

Syndicate content