Haml—Great Alternative to Rhtml

Written by steve on 03-22-2007 at 01:22 PM

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:

  1. It’s very concise
  2. It autocloses tags!
  3. It autocloses do/end
  4. It follows the DOM, as you’ve specified it in your CSS

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 &copy; 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.

The svn repository is at svn://hamptoncatlin.com/haml/trunk (for those of you who like the latest and greatest) and svn://hamptoncatlin.com/haml/tags/stable.

If you prefer, you can install the plugin using:

        
          script/plugin install svn://hamptoncatlin.com/haml/trunk haml
        
      

You’ll find complete information and documentation at: hamptoncatlin.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]
        
      

Permalink

 

0 comments

name (opt.)
email (req.)
comment
how many hands do most people have?

blog home