HAML: RoR's new templating engine

Hampton Catlin is presenting a new template engine called HAML at RailsConf tomorrow.

It combines structured XHTML creation with inline Ruby, but also borrows the same language used to define CSS. You have to see it to know what I mean.

I just learned about the project a couple of days ago, and I think it has a really bright future. My colleagues and I at Unspace prepared an introductory article on it here:

     unspace.ca

If you like the article, please help spread the word with a Digg:

http://digg.com/programming/Introducing_a_new_templating_engine_for_Rails

Also feel free to work on the Wikipedia entry:

     Haml - Wikipedia

To hear more you'll have to see Hampton at RailsConf tomorrow.

John

Looks really awesome..

Interesting.. No offense, I'm sure it's a good system in its own right, but I've been trying very hard to get away from whitespace-significant programming recently. :slight_smile:

-Pawel

This does look very nice. One thing I ran into instantly and that wasn't mentioned in the docs was how to do mutli line iterators. Like when you want to iterate over a collection and output elements. I couldn't figure out how that would work. Can you give a clue?

  Other then that, way cool. I'll definitely give it a go if it can do multi line statements.

-Ezra

That was one of the first questions I had for Hampton when I saw HAML. But you can see how the syntax is so much more succinct without it, because you don't need closing braces/tags, and then you are not much better off than using regular XHTML.

I haven't used it much at all myself, that's why the article is so basic... but from what I understand you would use regular Ruby control structures.

The best thing to do would be to hear from Hampton himself. If you'll be at RailsConf don't miss his presentation tomorrow.

Also, I'm sure after his presentation there will be a lot of interest to get HAML documented and so forth.

Checked it out. Looks great, but several questions in my mind would be:

1. I didn't see any support in the code for block-controlled view constructs such as:

<% for foo in @foos do %> <%= foo.name %> <% end %>

I.e., how do you stick raw Ruby in without the results being inserted in the stream?

2. What is the ~ character for? I saw it in the tests and the plugin code, but not sure what it does.

3. How would designers work with this?

Thanks

John Philip Green wrote:

3. How would designers work with this?

Frankly, it's a godsend :wink:

I am a designer at Unspace and have been working in HAML since Hampton created it, and I find it much easier to use than traditional markup. It makes complex, HTML heavy pages very readable and compact. I also think since class and id names have direct correlation with CSS it just makes sense if you know what I mean. I picked up HAML pretty much right away and haven't looked back.

I think if your designers are comforatble working with CSS/XHTML they shouldn't have any problem with it.

- Anthony Watts

Checked it out. Looks great, but several questions in my mind would be:

  1. I didn’t see any support in the code for block-controlled view constructs such as:

<% for foo in @foos do %> <%= foo.name %> <% end %>

That was a difficult early decision to make. At first, it was something that I planned to add. But, then as we began to use it on our projects (several of them so far) we found that not having if, loops, and blocks is actually a good thing.

Our code ends up being more readable, and it forces us to make hard-but-good decisions on whether the bit of wanted functionality should be a partial, a helper, or a one-line-loop. Generally, this means that the code is far more readable

You’d say this

= print_foo_names(@foos)

and go put that in the helpers.

I.e., how do you stick raw Ruby in without the results being inserted in the stream?

Well, in general, that code should be in the view, assuming that it doesn’t return a result.

However, if you want a hacky way…

= action_with_no_result && nil

Any line that returns a nil will not print… that’s including tags.

#wontprint= nil

It can be helpful when you want a wrapping div and you’d like to supress the output.

  1. What is the ~ character for? I saw it in the tests and the plugin code,

but not sure what it does.

Well, the biggest problem with auto-indenting code are those pesky

, , and  tags. In all of HTML, they are the only ones who don’t want to be indented or you get wonky results.

I spent more time on this issue than on any other in building HAML. I could fill a book with my thoughts on it and my struggles. However, after talking to some very smart people and doing lots of consideration, this was the result.

First, ~ simply says “hey, watch out, you might be given some whitespace sensitive stuff coming into you from the evaluation on the right”. But, what it does is causes some processing that changes “\n” into the UTF-8 entity for endlines. Basically, it puts the whole tag on one line, but puts in an endline character that the browser can respect, but won’t mess up your output.

It keeps the output beautiful and doesn’t hurt anything. Also, it saves some processing to only have to specify where it might happen.

  1. How would designers work with this?

Our designer loves it. They don’t really care about HTML, they care about understanding the structure of the page. Also, they go crazy for the CSS style syntax.

Funny story, Anthony, the guy who posted below, he wouldn’t work the other day because we didn’t install haml on a project. He now refuses to work on any projects that don’t use HAML. Simply because he likes the syntax and the way it helps him think about the pages structure. Honestly, I am super surprized by this outcome, but it speaks a lot to what our problems have been so far.

Thanks

Sincerely, Hampton Catlin.

Hello John,

So, assume you want to make a list.

.ul   = print_foo_names(@foos)

...

def print_foo_names(foos)   foos.each do |foo|   content_tag('li', foos.name) end

Is this the general idea?

Hampton wrote:

Francois Beausoleil wrote:

%table   %tbody     %tr= render(:partial => 'foo', :collections => @foos)

Looks good to me Francois.

You can make helpers.. but I'd argue that in your example, you'd want to use a partial.

Down with code inside views!

What I'm grappling with is the case where you have a relatively large number of records returned from a query. There are times where

render :partial => 'foo_partial', :collection => @foos

works great, but then there are times where custom iteration is more effective. Do you wrap every conditional piece in a partial, as:

= render :partial => 'admin_menu' if admin?

There's just not a lot of information out there yet.

I wouldn't be spending so many cycles on this if I didn't think HAML had a lot of potential. There's just something I'm not seeing...

Ryan McMinn wrote:

I agree. I think this looks really promising and would love to be able to use it. unfortunately I don't think its realistic to now allow multi line iterations. I may have a look and patch this in though. Was it left out because it was hard to implement or because the authors really don't think its needed? I really like the looks of these templates but no multi line loops is a show stopper for me.

  I mean, what about form_for block? How do you do those? do you have to render a partial that has normal erb in it? I just don't think it makes sense to not allow multi line loops. I would love to see this added.

Cheers- -Ezra

Not allowing multi-line iterators is a pretty big limb to cut off, but I'd like to see it explored more before people demand familiarity. I'm pretty interested in this, and plan to try to contribute some patches in the coming days. One obvious change I feel is that elements like img, br, hr and link shouldn't require explicit closing by the coder.

I am also curious to analyze the source more and see if there's a good reason not to use the ActionView tag helper lib for basic tag creation. It does escaping and the basic task it is charged with pretty well already. Perhaps the authors could elaborate on why they avoided that?

Ezra Zygmuntowicz wrote:

Yes. That's exactly how you'd do it, actually.

/Jeff

It's a paradigm shift all right. For example,

---------hello.haml-------- !!! %html   %head     %title this is the title   %body     hello world     %ul= render :partial => 'list_item', :collection => @members

Can you characterize "bad" (as in, "that would be bad")? I'm finding it pretty easy to convert templates, although I ran up against one that had a file-column in it that required some messing around:

    = @catalog_item = catalog_item; link_to(image_tag(url_for_file_column("catalog_item", "image", "thumb")), {:action => 'enlarge', :id => catalog_item}, :popup => ['new window'])

Joe-95 wrote:

Heads up: run the tabs to spaces command in textmate, even if you are using soft-tabs. Maybe I misunderstand that term, but it led me to a while of frustration 'til I realized what was up. Yay whitespace lovin'.

So, I'm thinking it shouldn't need me to declare which *html* elements are self-terminating. But we can leave in the option for self-termination at will, of course. I was also thinking % with no identifer could be considered as %p.

What about an inline syntax:

% Hi. Um.. I {em really}, {strong really} like you. Okay.

Also, why not use ActionView::Helpers::TagHelper? It already handles the basic tag creation in a simple way, and does escaping and such.