Haml 2.0
Haml 2.0 has been released. We’re calling it Friendly Fred because it aims to please. By the time you read this, it’ll probably have hit all the gem mirrors, so you can get it immediately.
So what’s new?
Friendliness
We named this release “Friendly Fred” because it’s much more friendly to users than previous versions. All sorts of Haml 1.8 had all sorts of bugs with handling and displaying errors; error messages weren’t useful, line numbers would be reported incorrectly, and sometimes the error message wouldn’t display at all.
That’s all been fixed. No more white screen of death. I’ve personally tested error handling, both compile-time and runtime, in Rails versions back to 1.2.6. I’ve added line-number tests for every error I could think up.
Sass errors didn’t have nearly as many problems as Haml errors,
but there were a few line-number-reporting bugs that are now fixed.
In addition, it was always kind of annoying to have to check the generated CSS file
for the actual error report.
Now when there’s a Sass error, in addition to reporting the error in the CSS file,
Sass will add the information to the content property of body:before,
so it’ll show up in the actual HTML page.
Error-handling wasn’t the only target of the bugfixes that went into 2.0. Among sundry boring fixes, we finally fixed the long-standing parser bug that confused attribute hashes with brackets within text content. This means that Haml 2.0 is actually the first version released with no known bugs.
Finally, we’re attempting to eliminate whitespace-preservation headaches as much as possible
by having literal pre and textarea tags – and Rails’ textarea-generating helpers -
properly deal with their contents.
You’ll still need to use ~ occasionally,
but no longer will you be subjected to the tyranny of
~ "<pre>#{h content}</pre>"Now you can just do
%pre= h content
or even
%pre&= content
Speed
The speed increase for 2.0 is nowhere near as impressive as 1.8, but it’s still interesting. According to my benchmarks1, Haml is now slightly faster than ERB, even when rendering via ActionView.
In addition, we’ve added an :ugly option,
thanks to Wincent Colaiuta.
This option forgoes pretty output formatting
in favor of speed increases,
which show up in particular when rendering deeply nested partials.
Dynamic Filters
Now, time for some real features! One of the most useful additions to 2.0 is the ability of filters to access the runtime template context. For most filters, this means you can do variable interpolation, like so:
:textile
You should all go to *#{h @place.name}*!For the filters that have Ruby code in them already, though,
such as :ruby and :erb,
this code is just run in the context of the template.
HTML Escaping
Haml 2.0 has very robust support for HTML escaping, thanks to Andre Arko. This includes both an option for escaping all dynamic input by default and syntactic support for escaping input even when it’s not the default.
To turn on HTML-escaping by default,
set the :escape_html option to true in whatever way your framework provides.
Then =, ==, and so forth will automatically escape any input they’re given.
In order to put in unescaped input,
you use != or !== instead.
If you don’t have :escape_html set,
then = and friends will work just like they always did.
But a new set of commands will be available:
&=, &==, and so on.
These work like = and friends
except they do escape their inputs.
To set options in Rails, use the Haml::Template.options hash in config/environment.rb (after Rails::Initializer.run):
Haml::Template.options[:escape_html] = true
To set them in Merb, use the Merb::Config[:haml] hash in init.rb instead:
Merb::Config[:haml][:escape_html] = true
Whitespace Munching
Getting rid of whitespace has traditionally been tricky with Haml. But thanks to much brainstorming by Evgeny Zislis and Sunny Ripert on my blog post about the subject; Nathan Sutton and Dustin Sallings on #haml on freenode; and various other people on the mailing list, we came up with a very nice syntax for it.
You can think of the syntax as alligators figuratively munching away at whitespace.
To get rid of all whitespace outside of a tag,
you add > to the end,
to munch away all the outer whitespace.
To get rid of whitespace within,
you add < instead.
Thus
%img
%img>
%img
%a{:href => "http://nex-3.com"}<
%p Foo!compiles to
<img /><img /><img /> <a href='http://nex-3.com'><p>Foo!</p></a>
Sass Mixins
The only major addition to Sass for 2.02
is the addition of support for mixins in Sass,
thanks to Garry Hill3.
This allows you to define a collection of attributes or even sub-rules
and include those in any rules you might want.
To define a mixin named clearfix,
you would write
=clearfix
display: inline-block
&:after
content: "."
height: 0
clear: both
visibility: hidden
* html &
height: 1pxand to include it in a rule, you would write
#sidebar +clearfix border: 1px solid black
Little Things
- There are three new predefined filters:
:javascript, which wraps the content in<script>andCDATAtags;:cdata, which wraps the content inCDATA(thanks to Yehuda Katz and:escaped, which HTML-escapes the content. - Haml can output HTML4 or HTML5, thanks to Mislav Marhonić.
This is set by setting the
:formatoption to:html4or:html5, respectively. - Boolean attributes conform to standards.
For XHTML,
%input{:selected => true}generates<input selected='selected' />. For HTML, it generates<input selected>. - Sass has a guarded-assignment
||=operator for constants that works just like the same operator in Ruby. css2sasstries to resolve parent-references (like&:hover), and can generate the alternate attribute syntax when the--alternateflag is passed.
Morning-after edit: Also, I forgot to mention, Haml 2.0 is fully compatible with Rails 2.1.
The Future
What’s on the horizon for Haml 2.2? I think the set of Haml features is levelling off. There may be minor additions, but I don’t plan to add any major new functionality.
What I do want to focus on is improving what we’ve already got, both in terms of performance and syntax. It’s possible that Haml will be more lenient in various ways that I’m not willing to specify yet by the time 2.2 rolls around.
Sass, being a younger language then Haml, still has a fair amount of room for new features. There are several that various people have asked to have in Sass, relating in particular to constants. It’s very likely that there will be considerable effort put into that.
1 You can run them for yourself by running rake benchmark in the Haml directory.
The benchmarks require Yehdua Katz’s benchwarmer gem.
2 There are more on the way for 2.2, though, don’t worry.
3 I couldn’t find a link to a blog or anything. Garry, if you read this and there’s somewhere you want me to link, let me know.
About Me
Feed
Haml Whitespace-Handling Sucks, Too



Tried to digg this, but digg failed epicly.
Anyway, so awesome! Can’t wait to wake up tomorrow and play with the new features! d-:
“To turn on HTML-escaping by default, set the :escape_html option to true in whatever way your framework provides.”
Might be useful to provide at least one example for how to do this. Rails for example. And the :ugly option too. I have no idea where to start looking for this.
I’m already loving Friendly Fred. Great job ! \o/
Great work by Nathan and all of the contributers for helping us hit this milestone in the haml community! I couldn’t ask for a more passionate and amazing community!
Great news, looking forward to trying this out!
This totally thrills me! SASS 2.0 will open up a bunch of doors for me. Looking forward to the progression of this language. Keep it up!
does it work with Rails edge?
Sass mixins are a fantastic feature! Thanks for the continued work to improve Haml and Sass!
I’m glad everyone’s so pleased :-).
Anonymous: I’ve added a bit about setting options to the HTML Escaping section.
Joe: Yes it does… for now. If Rails edge changes their template-handling mechanisms in the future, only the master and stable branches will be compatible until Haml 2.0.1 is released.
One thing to consider for the future is to promote HAML in different languages. Maybe HAML doesn’t have to just be a Ruby thing.
Thank you for the new features.
I have been reluctant to hack HAML (i.e. 1.8.2) but I eventually relented and added the following code after def surround in lib/haml/helpers.rb (I needed something more than surround). I’ve just discovered the new features that enable me to dispense with my hack, but here’s my hack (for discussion, there may still be a place for these):
Example:
or even:
Thanks also for the javascript filter. I want to create a slightly more efficient result when using it than from the following (i.e. replace repeated script containers with just one)
- unless @special_elements.blank? - @special_elements.each do |element| :javascript make_special('#{element.first}', #{element.last});which produces:
How can I achieve (note: lines 2 and 3 are swapped)
- unless @special_elements.blank? :javascript - @special_elements.each do |element| make_special('#{element.first}', #{element.last});to produce
I have a feeling you might say, “Do that in the controller”. The thing is that @special_elements is filled in by the various views that are generated (i.e. after the controller has run). If you could make this work, it would be pretty neat. I know it goes against the filter concept somewhat, but that could be a powerful concept if you could achieve such embedded control. Anyway, I look forward to your comments.
Thank you for HAML/SASS. I appreciate the contribution. It is a good philosophy to enforce MVC (well, at least to very strongly encourage MVC), particularly as neatness (elegance and conciseness) is a valuable result.
I hope you can tell that I am a fan! Sorry for the multiple comments in a row. Please note that I am holding back!
Anyway, can I suggest or request that
be equivalent to
because it follows the same pattern with # and . in that if the tag is omitted then it is assumed to be a div (i.e. Implicit Div Elements)?
Last one (for now).
I’m not sure if this is a bug, but it is at least a warning if you are doing something similar; otherwise, I’ve stuffed it up somehow. For a login form (i.e. if you use form_tag) in RubyOnRails, you have to put the end in.
... #login_panel - make_special('login_panel',...) .form = form_tag :action => :login do = text_field_tag :name, params[:name], :size => 30 = password_field_tag :password, params[:password], :size => 30 = submit_tag 'Login' - end <-- doesn't work if left off, HAML doesn't auto close this oneThe last end is necessary. This is a Rails thing and might be beyond HAML to account for it.
P.S. While I’ve got this code up there, can I make another feature request (just to make things a little DRYer)? Can you provide a variable which contains the id (if there is one) of the current container? This would be useful to me for many things, but this simple example will give you the idea.
... #login_panel - make_special('login_panel',...) .formwould become (or something similar, with some tasteful and appropriate escape sequence)
... #login_panel - make_special(@@@id,...) .formor a namespace polluting solution such as
... #login_panel - make_special(@view.current_dom_id,...) .formIf you think that is too yuck, then I understand. I think it is worth talking about. Is there a difference between being DRY and being lazy?
Cheers and thanks again.
Ryan: There are already numerous alternate implementations of Haml, including two in PHP (phpHaml and pHaml), one in Python, and one in C#. There may be more that I haven’t heard about yet. I’ve been thinking of 2.0 as a time to start working on unifying these alternate implementations, thinking about what it means to be “Haml,” and so forth.
Luis: I can see how those helpers would be useful. I’m not convinced that it’s not a better idea to do
Array#joinin a helper.For the
:javascriptstuff, filters are really meant to be used with primarily static text. I’d do that with- unless @special_elements.blank? %script{:type => "text/javascript"} - @special_elements.each do |element| &== make_special('#{element.first}', #{element.last});We may make
[]trigger the default-div behavior in the future. I’ve been able to getform_forto work without an extra end… I can’t imagine why it wouldn’t for you. And finally, I think adding support for class- and id-insertion would either cause too many performance issues or force us to run #gsub on some Ruby code, neither of which I want to do.Thank you, Nathan, for your replies.
I do agree to not overlook using a helper and
Array#join. I think it is easy for people to overlook them because when MVC is talked about, it is not presented as MVHC (H = Helper) and they kind of get left out until later.I am tempted to try to convince. I think there does exist a sensible use case, but it’s not going to be very common. I think the best shot at it is that
concatis conceptually betweensurroundandlist_of, and could/should be included for completeness.substitutecould allow for some interesting post-processing of HTML. Those uses maybe a bit pathological but could be useful. I would like a more general form oflist_of, but the answer is ultimately to use a helper. I do like that the resultant HTML is neatly indented. A helper can’t always make sure that indentation is correct, which is what is luring me out of a helper somewhat.One drawback with using
%script{:type => "text/javascript"}is that the CDATA is not included. I think you would need to do
%script{:type => "text/javascript"} !== //<![CDATA[ make_special('panel1', ...); make_special('panel2', ...); !== //]]>but then the Javascript code is not indented. :-( Is there a better solution?
The
form_foris OK. It’sform_tagthat’s the problem. I’ll have another look and see if I can post some useful information (hopefully it’s not that I’m a dummy!).That’s quite fair enough to avoid performance issues with adding support for class- and id-insertion. It’s great how fast HAML is. I think an efficient alternative to using a
gsubwould be to venture into namespace polluting which could be an optionor
Then when you go into the new scope do
and when you leave the new scope do
that way you could access the full containment of elements (even across partials), e.g.
... #login_panel - make_special(@dom_id_containment.last,...) - @domains.each do |domain| [@domain] - add_ajax_commands(@dom_id_containment,...) ...I hope this makes sense. Can you see where I am going? I like neatness. ;-) Particularly, when neatness also produces speed!
Yehaa! The Whitespace-War is over ;-)
When I was confronted with the whitespace issue the first time, I intuitively added an trailing backslash to the line – but that obviously didn’t work. Have you thought about this solution?
BTW, the URLs in your (atom) feed don’t include the domain :-(
Luis: Helpers have various tools available to them for properly indenting their output, including using
tab_up/tab_down,haml_tag,puts, or just returning a string that’s auto-indented by Haml. And in general, I’d be very nervous about just doing agsubon newlines… what if there were newlines where we didn’t expect them? It seems way too dangerous.In general, if you’re dynamically generating Javascript, you can HTML-escape it yourself (as I did in the example above), thus removing the need for CDATA. If you do need it, though, you could just write a simple helper.
The dom id detection isn’t as simple as that. Haml has distinct compilation and runtime phases, and the id may or may not be known in either of those. If it’s possible to implement this at all, it would require a huge amount of added complexity, and I just don’t think it’s worth it.
Dominik: Haml does have multiline syntax using
|, the pipe character. The syntax is intentionally awkward, though; see question three in the Haml FAQ.My feed has the
xml:baseattribute set, which provides the domain for domainless URLs. If your feed reader doesn’t process that, I suggest you submit a feature request ;-).Thanks Nathan. You are quite right. I think that answers the question about the difference between being DRY and being lazy. I think I was advocating laziness. The Javascript example is probably best done by:
- unless @special_elements.blank? = javascript_tag(@special_elements.collect{|element| "make_special('#{element.first}', #{element.last});"}.join("\n")which, with a helper, would become
All nice and neat. Yes, much better. Same with the dom id business. Thank you for spending the time responding.
Another counter to my argument would be to write a custom filter. But, I think you’re right: it’s just wrong. It’s best to focus on HAML being fast. Being faster than ERB is a good thing.
Also, I really like SASS errors coming up in the page. Great work!
I’ve been using the clearfix mixin trick for a while now (http://wonderfullyflawed.com/2008/05/21/clearfix-as-mixin/). Happy to see it make it off edge and into the wild!
I’d like to see escaped Ruby for use as a comment language, replacing my use of markdown.