Written by: steve ross on March 22nd 2007

I've become a true Haml convert. What does that mean? First, what is Haml? Haml is a markup language for Rails that drops in as a plugin and can be used as a substitute for rhtml. Let's jump right in with a quick example.

An Example

rhtml version

<% for post in @posts %>
  <p><span class="labels">title:</span> <span class="titles"><%= post.title %></span></p>
<% end %>

Haml version


- for post in @posts
  %p
    %span.labels title:
    %span.titles= post.title

A couple of things to note about Haml:

What you may not see is that the generated HTML is beautiful, with perfect indentation and can't-fail W3C compliance. Well, ok, nothing's guaranteed, but I've never written pure Haml that failed a W3C validation. Why is this cool? Because I spend way less time wrestling with my markup and more time on my Ruby code. I confess, I am not an HTML monkey. Never was, never will be. I'm just a programmer. That's why Haml is such an incredible gift to me.

Your application.haml Layout

Tired of typing a correct (X)HTML header and getting it right? Try this:


!!!
%html
  %head
    = stylesheet_link_tag 'application'
  %body
    = yield

That is a relatively complete layouts/application.haml. The !!! says "spit out a valid (X)HTML header, complete with DOCTYPE." Pretty cool, huh?

Extending Haml's Goodness to CSS: Sass

Haml is great for markup, but what about CSS? It turns out that Sass is to CSS as Haml is to rhtml. Sass is a natural way to write styles, free of some of the syntactic stuff that makes CSS so ... um ... interesting to write. Ok, maybe it's not a challenge to you, but I'm just a programmer. I'm not a Web monkey. Sass helps me get my CSS right the first time and it encourages me to follow better practices, such as using descendent selectors.

Here's an example from this site:


!background_color   = #5d3526
!content_background = #1B1718
!text_color         = white
!bold_hilight       = #e1deae
!link_color         = #e1deae
!menu_background    = white
!menu_text_color    = black
!menu_text_over     = #5d3526
!row_alt_color      = !content_background + #111111

.buzzword
  :color= !bold_hilight
  :font-size= !buzzword_font_size
  :font-weight bold

Whoa! What's that stuff at the top? Sass allows for named values, and if I want to change the background color everywhere in my CSS, it's one change in one place. You can see how I've used a few of these constants in the buzzword class definition. Just as with Haml, using the equal-sign (=) indicates some code-type stuff is going on, and that's how you need to write your expressions to take advantage of the named constants and the arithmetic (as you can see in row_alt_color).

Sass files are named public/stylesheets/sass/filename.sass, where filename is whatever you want your CSS file to be. The Sass engine compiles the Sass into a CSS file of the same name in the public/stylesheets directory, so typical usage is this (again, stealing from this site):


!!!
%html
  / File: app/views/layouts/application.haml (note that a single slash generates an HTML comment)
  %head
    %meta{"http-equiv"=>"Content-Type", :content=>"text/html; charset=utf-8"}/
    = stylesheet_link_tag('application', :media => 'all')
    = stylesheet_link_tag('ie', :media => 'screen') if request.user_agent =~ /msie/i
    = javascript_include_tag(:defaults)
    %title= yield :title
  %body
    #banner= image_tag('banner_l.gif', :size => '646x136')
    #logo= image_tag('logo.gif', :size => '159x138', :title => 'calico web development', :alt => 'calico web development')
    #menu
      #menu-container
        = render :partial => '/layouts/shared/nav_links'
    #content
      = yield
    #footer
      %p
        copyright © 1998 -
        = "#{Date.today.year}."
        all rights reserved

Here's a more interesting Sassification from this site's stylesheet:


/ File: public/stylesheets/sass/application.sass
#menu
  :background= $menu_background
  :font-size= $menu_font_size
  :max-height 40px
  #menu-container
    :padding 1px 0 12px 164px
    a, a:link, a:visited
      :color= $menu_text_color
      :font-weight bold
      :font-size= $menu_font_size
    a:hover
      :color= $menu_text_over
      :font-weight bold
      :text-decoration none
    ul
      :list-style-type none
      li
        :float left
        :padding 0 23px 0 13px
        :text-indent 0
        :background url(/images/menu_bar.gif) 100% 66% no-repeat

Installation of Haml

This is the part you've all been waiting for. Where to get it.

gem install haml

You'll find complete information and documentation at: http://haml-lang.com and lots of good discussion at the Google group.

Performance

This seems to be a big concern to some. Not me. I snipped some log entries from a site that uses Haml. As you might expect, the list action (first entry) actually hits the database, yet I'm still serving up some 52 req/sec. Because the time to process the entire request is less that .02 seconds, the margin of error can be quite high by falling on one side or the other of a timer tick. In any case, ~50 req/sec is over 4 million requests/day. That's on a single Red Hat Linux box with Apache proxying directly to one instance of Mongrel.

The second log entry shows creating a new form. This uses Rails' form_for helper and renders just a bit slower. Not clear why it's slower, but again, these are anecdotal and the throughput is quite adequate for the site. Adding Mongrels would scale this up if (all of a sudden) lots of people fell in love with my site.


  Processing PostsController#list (for 24.18.43.53 at 2007-03-22 12:33:27) [GET]
    Session ID: db208605296b2fa0eb06cc499aa2dd1c
    Parameters: {"action"=>"list", "controller"=>"posts"}
  Rendering  within layouts/application
  Rendering posts/list
  Completed in 0.01899 (52 reqs/sec) | Rendering: 0.01664 (87%) | DB: 0.00179 (9%) | 200 OK [http://calicowebdev.com/blog/list]

  Processing PostsController#new (for 24.18.43.53 at 2007-03-22 12:33:37) [GET]
    Session ID: db208605296b2fa0eb06cc499aa2dd1c
    Parameters: {"action"=>"new", "controller"=>"posts"}
  Rendering  within layouts/application
  Rendering posts/new
  Completed in 0.02401 (41 reqs/sec) | Rendering: 0.02299 (95%) | DB: 0.00028 (1%) | 200 OK [http://calicowebdev.com/blog/new]