HAML gave us a great take on how views can also be done. It looks a little cryptic at first, but don’t let that shake you off. Once you internalize the meaning of %, #, and . it should be all good (and you already know most just from CSS)....Additionally, I can’t help but have respect for a Canadian who manages to swear more than I did during my vendoritis rant and drink beer at the same time. A perfect example of the diversity in the Rails community. Very much part of what makes us special.
—David talking about Haml and Hampton Catlin1
1. http://david.heinemeierhansson.com/arc/2006_09.html
Haml2 is a “whitespace-sensitive” HTML templating engine that uses indentation to determine the hierarchy of an HTML document. Haml was created because its creator, Hampton Catlin, was tired of having to type markup and wanted all his output code to be beautifully formatted. What he invented was a new templating engine that removed a lot of noisy boilerplate, such as angle brackets (from ERb), and did away with the need to close blocks and HTML tags.
We love Haml because it’s truly minimal, allowing a developer to focus simply on the structure of the page and not on the content. Today it’s common to keep view logic out of your templates, but that directive has been a guiding principle of Haml since its beginning. According to the 2012 Ruby Survey,3 36.96 percent of Rubyists prefer Haml over ERb, and 15.84 percent demand it in their projects. Haml is also the standard templating engine at various professional Ruby agencies, such as Hashrocket, Envy Labs, Remarkable Labs, and Astrails.
3. http://survey.hamptoncatlin.com/survey/stats
In this chapter, we’ll cover the fundamentals of Haml, from creating HTML elements to using filters to create other kinds of textual content embedded in your document.
To start using the Haml template language over ERb in your project, first add the haml-rails
gem to your Gemfile
and run bundle install
.
# Gemfile
gem 'haml-rails'
The benefit of using haml-rails
over simply the haml
gem is it adds support for Rails-specific features. For instance, when you use a controller or scaffold generator, haml-rails
will generate Haml views instead of using the Rails default of ERb. The haml-rails
gem also configures Haml templates to work with Rails 4 cache digests out of the box.
In this section, we’ll cover how to create HTML elements and attributes using Haml.
To create an HTML element in Haml, one simply needs to prefix the percent character (%)
to an element name. The element name can be any string, allowing you to use newly added HTML5 elements, such as header
.
Haml
%header content
HTML
<header>content</header>
Haml will automatically handle generating opening and closing tags for the element on compilation. Not only does this make templates more concise and clean, it also eliminates common errors such forgetting to not close an HTML tags.
Attributes in Haml are defined using two styles. The first style involves defining attributes between curly braces ({}
). These attribute “brackets” are really just Ruby hashes and are evaluated as such. Because of this, local variables and Ruby logic can be used when defining attributes.
%a{ title: @article.title, href: article_path(@article) } Title
The second style follows the more traditional way of defining HTML attributes using brackets. Note that attributes are separated by white space, not commas.
%a(title=@article.title href=article_path(@article)) Title
Multiline Attributes
Attribute hashes can be separated on multiple lines for readability. All new lines must be placed right after the comma:
1 %a{ title: @article.title,
2 href: article_path(@article) } Title
Introduced with HTML5, data attributes allow custom data to be embedded in any HTML element by prefixing an attribute with data-
. Instead of littering the attribute hash with multiple attribute keys prefixed with data-
, one can define all their data attributes in a nested hash associated with the key :data
, like this:
Haml
%article{ data: { author_id: 1 } } Lorem Ipsum...
HTML
<article data-author-id='123'>Lorem Ipsum...</article>
Note that underscores are automatically replaced with a hyphen. Not that you’d want to, but you can change this behavior by setting the Haml configuration option hyphenate_data_attrs
to false
. (Haml configuration options are covered in detail later in this chapter.)
It’s also possible to nest data hashes more than one level to reduce verbosity when attributes share common roots.
Haml
%article{ data: { author: {id: 1, name: "Kevin Wu" } } Lorem Ipsum...
<article data-author-id='123' data-author-name='Kevin Wu'>Lorem Ipsum...</article>
In HTML, there exists certain attributes that do not have a value associated with them, such as required
.
<input type="text" required>
These are referred to as boolean attributes in Haml, since their value does not matter—only that they’re present. To represent these attributes in using the hash-style attribute syntax, set the value of the attribute to true
.
%input{ type: 'text', required: true }
Otherwise, if you’re using the HTML attribute style syntax, a boolean value doesn’t have to be set at all.
%input(type="text" required)
XHTML
If the format of Haml is set to :xhtml
, boolean attributes will be set to their name. To illustrate, given the previous example, Haml would render the following HTML:
<input type="text" required="required" />
Haml was designed to promote the DRY principle (not repeating code unnecessarily). As such, it provides a shorthand syntax for adding id and class attributes to an element. The syntax is borrowed from CSS, where ids are represented by a pound (#
) and classes by a period (.
). Both of these signs must be placed immediately after the element and before an attributes hash.
Haml
1 #content
2 .entry.featured
3 %h3.title Haml
4 %p.body Lorem Ipsum...
1 <div id='content'>
2 <div class='entry featured'>
3 <h3 class='title'>Haml</h3>
4 <p class='body'>Lorem Ipsum...</p>
5 </div>
6 </div>
As the previous example shows, multiple class names can be specified similarly to CSS by chaining the class names together with periods. In a slightly more complicated scenario, the shortcut CSS style class and id syntax can be combined with longhand attributes. Both values are merged together when compiled down to HTML.
Haml
%article.featured{ class: @article.visibility }
HTML
<article class='feature visible'>...</article>
Haml has some serious tricks up its sleeves for dealing with complex id and class attributes. For instance, an array of class names will automatically be joined with a space.
Haml
%article{ class: [@article.visibility, @article.category] }
HTML
<article class='visible breakingnews'>...</article>
Arrays of id values will be joined with an underscore.
Haml
%article{ id: [@article.category, :article, @article.id] }
HTML
<article id='sports_article_1234'>...</article>
Note that the array is flattened and any elements that evaluate to false or nil will be dropped automatically. This lets you do some pretty clever tricks at the possible expense of readability and maintainability.
1 %article{ class: [@article.visibility,
2 @article.published_at < 4.hours.ago && 'breakingnews'] }
In the example, if the article was published less than four hours ago, then breakingnews
will be added a one of the CSS classes of the element. While we’re on the subject, remember that it is advisable to migrate this kind of logic into your Ruby classes. In this particular example, we might give the Article
class (or one of its presenters or decorator classes) a breakingnews?
method and use it instead of inlining the business logic.
1 def breaking?
2 published_at < 4.hours.ago
3 end
%article{ class: [@article.visibility, @article.breaking? && 'breakingnews'] }
If breaking?
returns false, then the Ruby expression short-circuits to false, and Haml ignores that particular class name.
The default elements of Haml are divs. Since they are used so often in markup, one can simply define a div with a class or id using .
or #
respectively.
Haml
1 #container
2 .content Lorem Ipsum...
HTML
1 <div id="container">
2 <div class="content">
3 Lorem Ipsum...
4 </div>
5 </div>
Implicit Div Creation
Not having to specify div tags explicitly helps your markup be more semantic from the start, placing focus on the intention of the div instead of treating it as just another markup container. It’s also one of the main reasons that we recommend Haml over ERb. We believe that Haml templates lessen mental burden by communicating the structure of your DOM in way that maps cleanly to the CSS that will be applied to the document.
In HTML, there are certain elements that don’t require a closing tag, such as br
.4 By default, Haml will not add a closing tag for the following tags:
4. For a definitive explanation of why some HTML elements like <br/>
close themselves, while others like <script>
need a closing tag, read http://www.colorglare.com/2014/02/03/to-close-or-not-to-close.html
• area
• base
• br
• col
• hr
• img
• input
• link
• meta
• param
To illustrate, consider the following example:
%hr
would render HTML
<hr>
or XHTML
<hr />
Adding a forward slash character (/
) at the end of a tag definition causes Haml to treat it as being an empty element. The list of empty tags Haml uses can be overridden using the autoclose
configuration setting. Haml configuration options are covered in detail later in this chapter.
A doctype must be the first item in any HTML document. By including the characters !!!
at the beginning of a template, Haml will automatically generate a doctype based on the configuration option :format
, set to :html5
by default. Adding !!!
to a template would result in the following HTML:
<!DOCTYPE html>
Haml also allows the specifying of a specific doctype after !!!
. A complete listing of supported doctypes can be found on Haml’s reference website.5
5. http://haml.info/docs/yardoc/file.REFERENCE.html#doctype_
There are two types of comments in Haml: those that appear in rendered HTML and those that don’t.
To leave a comment that will be rendered by Haml, place a forward slash (/
) at the beginning of the line you want commented. Anything nested under that line will also be commented out.
/ Some comment
<!-- Some comment -->
You can use this feature to produce Internet Explorer conditional comments by suffixing the condition in square brackets like this:
/[if lt IE 9]
Besides conditional comments for targeting Internet Explorer, comments left in your markup are meant to communicate a message to other developers working with the template. These messages should not be rendered to the browser, as they are specific to your team. In Haml, starting a line with -#
ensures any text following the pound sign isn’t rendered at all.
Haml
-# Some important comment...
%h1 The Rails 4 Way
<h1>The Rails 4 Way</h1>
If any text is nested beneath this kind of silent comment, it will also be omitted from the resulting output.
Somewhat similar to ERb, using the equals character (=)
results in Haml evaluating Ruby code following the character and outputting the result into the document.
Haml
%p= %w(foo bar).join(' ')
HTML
<p>foo bar</p>
Alternatively, using the hyphen character (-)
evaluates Ruby code but doesn’t insert its output into the resulting document. This is commonly used in combination with if/else statements and loops.
- if flash.notice
.notice= flash.notice
Note that Ruby blocks don’t need to be explicitly closed in Haml. As seen in the previous example, any indentation beneath a Ruby evaluation command indicates a block.
Kevin Says ...
Do not use -
to set variables. If you find yourself doing so, this is an indication that you need to create some form of view object, such as a presenter or decorator.
Lines of Ruby code can be broken up over multiple lines as long as each line except the last ends with a comma.
= image_tag post.mage_url,
class: 'featured-image'
Ruby code can be interpolated in two ways in Haml: inline with plain text using #{}
or using string interpolation in combination with =
. To illustrate, the following two lines of Haml code samples are equivalent:
%p By: #{post.author_name}
%p= "By: #{post.author_name}"
To match the default Rails XSS protection scheme, Haml will sanitize any HTML-sensitive characters from the output of =
. This results in any =
call to behave like &=
.
Haml
&= "Cookies & Cream"
HTML
Cookies & Cream
Alternatively, to unescape HTML with Haml, simply use !=
instead of =
. If the Haml configuration option escape_html
is set to false
, then any call to =
will behave like !=
. (You probably will never want to do that.)
Haml
!= "Remember the awful <blink> tag?"
HTML
Remember the awful <blink> tag?
On rare occasion, you might want to start a line of your template with a character such as =
that would normally be interpreted. You may escape the first character of a line using a backslash.
Haml
%p
= equality for all =
<p>
= equality for all =
</p>
Haml is meant to be used for layout and design. Although one can technically write multiline declarations within a template, the creators of Haml made this intentionally awkward to discourage people from doing so.
If you for some reason do need declarations that span multiple lines in a template, you can do so by adding multiline operator (|
) to the end of each line.
1 #content
2 %p= h( |
3 "While possible to write" + |
4 "multiline Ruby code, " + |
5 "it is not the Haml way," + |
6 "as you should eliminate as much Ruby" + |
7 "in your views as possible.") |
We highly recommend extracting multiline Ruby code into helpers, decorators, or presenters.
Haml provides a variety of helpers that are useful for day-to-day development, such as creating list items for each item in a collection and setting CSS ids and classes based on a model or controller.
Given an object, such as an Active Record instance, Haml can output an HTML element with the id
and class
attributes set by that object via the []
operator. For instance, assuming @post
is an instance of a Post
class with an id
value of 1
, then the template code
1 %li[@post]
2 %h4= @post.title
3 = @post.excerpt
renders
<li class='post' id='post_1'>...</li>
This is similar to using Rails helpers div_for
and content_tag_for
, covered in Chapter 11, “All about Helpers.”
Returns the name of the current controller and action to be used with the class
attribute of an HTML element. This is commonly used with the body
element to allow for easy style targeting based on a particular controller or action. To illustrate, assuming the current controller is PostsController
and action index
,
%body{ class: page_class }
renders
<body class='posts index'>
Given an Enumerable
object and a block, the list_of
method will iterate and yield the results of the block into sequential <li>
elements.
Haml
1 %ul
2 = list_of [1, 2, 3] do |item|
3 Number #{item}
HTML
1 <ul>
2 <li>Number 1</li>
3 <li>Number 2</li>
4 <li>Number 3</li>
5 </ul>
Haml ships with a collection of filters that allows you to pass arbitrary blocks of text content as input to another processor, with the resulting output inserted into the document. The syntax for using a filter is a colon followed by the name of the filter. For example, to use the markdown filter,
1 :markdown
2 # The Rails 4 Way
3
4 Some awesome **Rails**-related content.
1 <h1>The Rails 4 Way</h1>
2
3 <p>Some awesome <strong>Rails</strong>-related content.</p>
Here is a table of all the filters that Haml supports by default:
Some filters require external gems to be added to your Gemfile
in order to work. For instance, the :markdown
filter requires a markdown gem, such as redcarpet
.
In Chris Eppstein’s blog post “Haml Sucks for Content,”6 he stated his opinions on why one shouldn’t use Haml to build content:
6. http://chriseppstein.github.io/blog/2010/02/08/haml-sucks-for-content
Haml’s use of CSS syntax for IDs and class names should make it very clear: The markup you write in Haml is intended to be styled by your stylesheets. Conversely, content does not usually have specific styling—it is styled by tags.
Essentially, what Chris was trying to convey is to not use native Haml syntax for creating anything other than skeletal (or structural) HTML markup. Use of filters to inline reader content, such as in this example using the :markdown
filter,
1 %p
2 Do
3 %strong not
4 use
5 %a{ href: "http://haml.info" } Haml
6 for content.
is equivalent to the following markdown within a filter:
1 :markdown
2 Do **not* use [Haml](http://haml.info) for content.
We like this idea but admit that your mileage may vary. It really depends on the type of project you’re working on and the capabilities of the person that will be maintaining the Haml template source files.
Haml provides various configuration options to control exactly how markup is rendered. Options can be set by setting the Haml::Template.options
hash in a Rails initializer.
# config/initializers/haml.rb
Haml::Template.options[:format] = :html5
The autoclose
option accepts an array of all tags that Haml should self-close if no content is present. Defaults to ['meta', 'img', 'link', 'br', 'hr', 'input', 'area', 'param', 'col', 'base']
.
Determines if Haml will include CDATA sections around JavaScript and CSS blocks when using the :javascript
and :css
filters, respectively.
When format
is set to html
, it defaults to false. If the format
is xhtml
, cdata
will always be set to true and cannot be overridden.
This option also affects the filters * :sass
, * :scss
, * :less
, and * :coffeescript
.
The compiler class to use when compiling Haml to HTML. Defaults to Haml::Compiler
.
The default encoding for HTML output is Encoding.default_internal
. If that is not set, the default is the encoding of the Haml template.
The encoding option can be set to either a string or an Encoding
object.
If set to true
(default), will escape all HTML-sensitive characters in attributes.
When Haml is used with a Rails project, the escape_html
option is automatically set to true
to match Rails’ XSS protection scheme. This causes =
to behave like &=
in Haml templates.
Specifies the output format of a Haml template. By default, it’s set to :html5
.
Other options include the following:
• :html4
• :xhtml
. Will cause Haml to automatically generate self-closing tags and wrap the output of JavaScript and CSS filters inside CDATA.
Haml converts all underscores in all data attributes to hyphens by default. To disable this functionality, set hyphenate_data_attrs
to false
.
The MIME type that rendered Haml templates are servered with. If this is set to text/xml, then the format will be overridden to :xhtml
, even if it has been set to :html4
or :html5
.
The parser class to use. Defaults to Haml::Parser
.
The preserve
option accepts that an array of all tags should have their newlines preserved using the preserve
helper. Defaults to ['textarea', 'pre']
.
Setting to true
causes all tags to be treated as if whitespace removal Haml operators are present. Defaults to false
.
Haml does not attempt to format or indent the output HTML of a rendered template. By default, ugly
is set to false
in every Rails environment except production
. This enables you to view the rendered HTML in a pleasing format when you’re in development but yields higher performance in production.
In this chapter, we learned how Haml helps developers create clear, well-indented markup in your Rails applications. In the following chapter, we will cover how to manage sessions with Active Record, Memcached, and cookies.
18.118.186.202