tag:www.rhnh.net,2008:/haml Haml - Xavier Shay's Blog 2007-07-17T11:41:13Z Enki Xavier Shay notreal@rhnh.net tag:www.rhnh.net,2008:Post/118 2006-09-16T11:00:00Z 2007-07-17T11:41:13Z HAML Tutorial <p><a href="http://dev.hamptoncatlin.com/haml"><span class="caps">HAML</span></a> is, and is an acronym for, an <span class="caps">HTML</span> Abstraction Markup Language. It is a replacement for the <span class="caps">RHTML</span> templates we are so used to in rails applications. If you are interested in <em>why</em> one would need such a thing, please read John Philip Green&#8217;s excellent <a href="http://unspace.ca/discover/haml"><span class="caps">HAML</span> introduction</a>. If you are more interested in <em>how</em> one would use such a thing, read on!</p> <h3>Table of Contents</h3> <ol> <li><a href="#installation">Installation</a></li> <li><a href="#fundamentals">Fundamentals</a></li> <li><a href="#xhtml_techniques"><span class="caps">XHTML</span> techniques</a></li> <li><a href="#ruby_techniques">Ruby techniques</a></li> <li><a href="#conclusion">Conclusion</a></li> </ol> <p>h3.#installation). Installation</p> <p>First things first, install the plugin:</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">./script/plugin install -x svn://hamptoncatlin.com/haml/trunk haml<tt> </tt></pre></td> </tr></table> <p>This gives you a library to parse <span class="caps">HAML</span> templates, and also registers the .haml extension with rails. What this means is that to start using <span class="caps">HAML</span> you only need to rename your template from &#8216;index.rhtml&#8217; to &#8216;index.haml&#8217;. Do that now (in a new test app, an existing app, whatever), as we are about to get our first taste of ham &#8230; (l).</p> <p>h3.#fundamentals). Fundamentals</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt>3<tt> </tt>4<tt> </tt>5<tt> </tt>6<tt> </tt>7<tt> </tt>8<tt> </tt>9<tt> </tt><strong>10</strong><tt> </tt>11<tt> </tt>12<tt> </tt>13<tt> </tt>14<tt> </tt>15<tt> </tt>16<tt> </tt>17<tt> </tt>18<tt> </tt>19<tt> </tt><strong>20</strong><tt> </tt>21<tt> </tt>22<tt> </tt>23<tt> </tt>24<tt> </tt>25<tt> </tt>26<tt> </tt>27<tt> </tt>28<tt> </tt>29<tt> </tt><strong>30</strong><tt> </tt>31<tt> </tt>32<tt> </tt>33<tt> </tt>34<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">%h1 HAML Example<tt> </tt>%div<tt> </tt> %blockquote <tt> </tt> Farewell, Emily. It was fun, but you were a robot. <tt> </tt> You had no heart. <tt> </tt>--- <tt> </tt><tt> </tt>In the same vein as YAML and Python, *indentation matters* in HAML. It allows the parser to cleverly close our tags without being explicitly told to do so. Equals less typing for us lazy sloths. 2 spaces per indent is the rule. The first non-whitespace character of each line is what is used to decide how to parse the line. As may be evident, the % character indicates an XHTML tag. There are only 5 others, which we will cover in due course. Lines that do not begin with a special character are treated as normal text.<tt> </tt><tt> </tt>h3.#xhtml_techniques). XHTML techniques<tt> </tt><tt> </tt>Being a prime requirement of a templating language, outputting XHTML is as simple as you would expect. I'm not even going to write a full paragraph, this annotated listing should suffice:<tt> </tt>--- haml<tt> </tt>/ The slash character specifies an XHTML comment,<tt> </tt>/ but if after a tag name it self closes that tag<tt> </tt>%br/<tt> </tt><tt> </tt>/ Attributes are specified by a hash provided directly after <tt> </tt>/ the tag name. There is NO SPACE between the tag and the hash<tt> </tt>%a{&quot;name&quot; =&gt; &quot;top&quot;}<tt> </tt><tt> </tt>/ &quot;class&quot; is such a common attribute that it has a shortcut syntax<tt> </tt>%span.important Tada!<tt> </tt><tt> </tt>/ Combine the two to impress you friends<tt> </tt>%span.extra{&quot;style&quot;=&gt;&quot;color: red&quot;} Tada! Tada!<tt> </tt><tt> </tt>/ A div with id is also common, so it too has a shortcut syntax<tt> </tt>#content<tt> </tt> This is a div with id &quot;content&quot;<tt> </tt><tt> </tt>/ As does a div with class<tt> </tt>.fancy<tt> </tt> This is a div with class &quot;fancy&quot;<tt> </tt></pre></td> </tr></table> <p>The one curly aspect of generating <span class="caps">XHTML</span> you only need to deal with once &#8211; the doctype. You can use three exclamation marks on the first line of a template (hopefully a layout template) to output a doctype declaration. The problem is that it makes your document <span class="caps">XHTML 1</span>.0 transitional. Always. It also forgets to give you an <span class="caps">XML</span> prolog, so for now I specify these without using <span class="caps">HAML</span>, which brings up another point &#8211; you can mix normal <span class="caps">XHTML</span> tags and <span class="caps">HAML</span> code (although why you would want to outside of this fix eludes me).</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt>3<tt> </tt>4<tt> </tt>5<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">!!!<tt> </tt>%html{&quot;xmlns&quot;=&gt;&quot;http://www.w3.org/1999/xhtml&quot;, &quot;xml:lang&quot;=&gt;&quot;en&quot;}<tt> </tt> %head<tt> </tt> %title Layout Example<tt> </tt> %body= @content_for_layout<tt> </tt></pre></td> </tr></table> <table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt>3<tt> </tt>4<tt> </tt>5<tt> </tt>6<tt> </tt>7<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;<tt> </tt>&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot;<tt> </tt> &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&gt;<tt> </tt>%html{&quot;xmlns&quot;=&gt;&quot;http://www.w3.org/1999/xhtml&quot;, &quot;xml:lang&quot;=&gt;&quot;en&quot;}<tt> </tt> %head<tt> </tt> %title Layout Example<tt> </tt> %body= @content_for_layout<tt> </tt></pre></td> </tr></table> <p>Those on the edge may want to keep an eye on <a href="http://dev.hamptoncatlin.com/haml/ticket/13">this ticket</a> , which proposes a fix.</p> <p>h3.#ruby_techniques). Ruby techniques</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt>3<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">= link_to :controller =&gt; 'home'<tt> </tt>= 1 + 2 # =&gt; 3<tt> </tt>%span= 1 + 2<tt> </tt></pre></td> </tr></table> <p>Text after an equals sign is evaluated as ruby code. It is roughly equivalent to <code>&lt;%= 1 + 2 # =&gt; 3 %&gt;</code>, but with one fairly major caveat: Each is evaluated independent of the rest of the template. Meaning the follow <strong>will not</strong> work, because the first line is evaluated as an entire ruby snippet, and does not find the <code>end</code> it requires to be valid.</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt>3<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">= for i in (1..10)<tt> </tt>= i<tt> </tt>= end<tt> </tt></pre></td> </tr></table> <p>There is currently no way around this. There is a <a href="http://dev.hamptoncatlin.com/haml/ticket/16">ticket</a> on the <span class="caps">HAML</span> trac with a proposed fix, but at the time of authoring the patch has not been attached. This is not as shocking as it may first appear. Ask yourself why you are using a loop or an if block in your code. If it cannot be reduced to a one liner, maybe it should be moved it out into a partial.</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt>2<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">= (1..10).inject('') { |buffer, i| buffer + i.to_s }<tt> </tt>= render :partial =&gt; 'secret', :collection =&gt; @secrets if cia?<tt> </tt></pre></td> </tr></table> <p>An alternative way to evaluate ruby code is to use a tilde instead of equals. This has the effect of searching in the evaluated string and replacing all newlines found in <code>pre</code>, <code>code</code> or <code>textarea</code> tags with an <span class="caps">XHTML</span> entity (<code>&amp;#000A;</code>). This allows you to create neat markup even when displaying large chunks of preformatted text.</p><table class="CodeRay"><tr> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt> </tt></pre></td> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }"> ~ &quot;&lt;textarea&gt;\n\n\n\n\n\nYo&lt;/textarea&gt;&quot;<tt> </tt></pre></td> </tr></table> <p>Keep in mind that your ruby expression must not span more than one line &#8211; only the first line will be parsed and the rest will be treated as plain text. There is a <a href="http://dev.hamptoncatlin.com/haml/ticket/6">proposed fix</a> (that makes 3! I want a pony) on the <span class="caps">HAML</span> Trac, if you are in to that sort of thing.</p> <p>h3.#conclusion). Conclusion</p> <p><span class="caps">HAML</span> may not be quite as powerful as <span class="caps">RHTML</span> yet, but it drastically reduces the size of your views while greatly increasing readability and the quality of the markup. The best part is you can mix and match &#8211; you can start writing <span class="caps">HAML</span> templates in your existing project right now and keep all your old <span class="caps">RHTML</span> code hanging around.</p>