Eric Radman : a Journal

Programming Documents with Haml

Haml is an templating language for writing HTML. Over the past decade the capabilities of HTML+CSS have become good enough for nearly everything: letters, my resume, pages on this site, even printed forms.

Haml combines a set of valuable features:

  1. A notation that mirrors HTML
  2. Blocks of text can be interpreted using custom filters
  3. Ad-hoc scripting

Mirroring HTML

Notwithstanding the name, Haml is not very abstract. It is mostly a 1:1 representation of the HTML it generates

    %title A Letter
    %link{:href=>"main.css", :rel=>"stylesheet", :type=>"text/css"}/
      - require 'date'
      = "Last updated on " +"%B %d, %Y")

Setting a class and id properties has a nice syntax

/ class="first"
/ id="content"

Ruby code can be run directly, and Haml::Engine can be used directly to process another template.


In Haml, Ruby itself is also not abstracted, which gives us the ability to invent new mechanisms. One simple scheme I have used is to read extra arguments on the command line


This means Makefile can easily communicate a source file to read

.SUFFIXES: .haml .html

SOURCES != ls posts/*.haml | awk 'sub("\.haml$$", ".html")'

    haml25 template.haml $@ $<


Perhaps the most important feature of template language is the ability to process and transform blocks of text

module Haml::Filters::Link
  include Haml::Filters::Base

  def render(text)
    %q|<a href="#{text}">#{text}</a>|

This can be incorporated thusly

  Visit my home page at

And to compile

haml25 -I $PWD -r link my.haml

Built-in Tests

I filed this post under the category TDD. Indeed built-in self tests are an excellent means of writing filters. Haml filters are simple Ruby modules, so we can build simple self-tests without having to instantiate an object

require 'haml'

module Haml::Filters::Link
    def render(text)
        # ...

if __FILE__ == $0
    include Haml::Filters::Link

    out = render("")
    expected = %q|<a href=""></a>|

    if expected == out
        puts "PASS"
        puts <<~MESSAGE
          expected: #{expected}
          got     : #{out}

Stumbling Blocks

There are some features of Haml which cause difficulty. The first is that control over whitespace is alkward. Often the most strait-forward method is to use the special functions surround and suceed. In this example I want a period immediately following the hyperlink

Send comments to
= succeed '.' do

Another technique for controlling whitespace is to append > to trim whitespace and then add non-breaking spaces where needed

  %span.Prompt> $&nbsp;
  ls *.css *.html | entr reload-browser Firefox

Sometimes it's easier to simply generate and/or write HTML. This is no problem—Haml doesn't prevent you, and sometimes this provides a readable and correct solution to formatting.

Last updated on October 18, 2018