We build Web & Mobile Applications.
One of the good things about the high level of activity in the Rails world is that very often if you don’t like the default way of doing things there are usually several alternatives. A good example is the choice of templating language: if you don’t like ERb (and I don’t as it smells too much like PHP) then you can use something else. I’m personally a HAML fan but the project I’m currently working on - adding new features to an existing site that was developed by another firm - uses Markaby.
Markaby makes view templates look just like any other Ruby code and it is extremely intuitive: all I had to do was read this and I was good to go! I also like the way Markaby syntax can be used in helpers – Ryan Bates has produced another one of his fine RailsCasts on this very subject.
The only real downside I can see to using Markaby is its relatively slow speed especially when HAML is getting faster with each release.
Here are a few tips that I came up with for getting Markaby to generate XHTML the way I wanted it to.
In Markaby you can easily define class and id attributes by calling methods on the container elements like so:
div.error { 'error' }
p.intro! 'welcome'
This generates the following XHTML:
<div class="error">error</div>
<p id="intro">welcome</p>
But this syntax can’t be used when you want the class or id attribute to use hyphens (which is my personal preference), for example these won’t work:
div.error-message { 'error' }
p.intro-text! 'welcome'
Both of these examples cause syntax errors:
no such method 'message'
errorsyntax error, unexpected tIDENTIFIER, expecting kDO or { or (
errorThis is because Ruby evaluates the hyphen as the subtraction operator. This shouldn’t really come as a surprise given that Markaby is just Ruby code, but it caused me a few moments of head scratching when I first came across the error.
The solution is thankfully simple: classes and ids can also be specified by passing them as parameters to the container element. So the correct code is:
div :class => 'error-message' do 'error' end
p 'welcome', :id => 'intro-text'
Let’s say you want to generate something like this:
<div class="floated">content</div>
<div class="clear"></div>
<div>some more content</div>
You might think that this would do the job:
div.floated "content"
div.clear
div "some more content"
But you’d be wrong – this is what you’ll get:
<div class="floated">content</div>
<div class="clear">
<div>some more content</div>
</div>
As you can see the nesting of elements has gone wrong. Again this is an easy one to fix, just pass the container element an empty block like this:
div.clear {}
Let’s say you have a two column blog page layout: the left column will contain headings and excerpts of each blog entry and the right column will contain a list of entry headings. You could do this:
div :class => 'left-column' do
for entry in @entries do
h3 entry.title
p entry.excerpt
end
end
div :class => 'right-column' do
ul do
for entry in @entries do
li entry.title
end
end
end
This would do the trick, but if you’re like me then having two for loops iterating through the entries seems a bit unnecessary. It’d be nice to have one loop generating the content for both columns.
That’s where captures comes in. The capture method allows the XHTML generated by Markaby to be stored in an instance variable for use elsewhere in the template. Here’s the above example rewritten to use capture:
div :class => 'left-column' do
@entry_list = ''
for entry in @entries do
h3 entry.title
p entry.excerpt
@entry_list += capture { li entry.title }
end
end
div :class => 'right-column' do
ul { @entry_list }
end
This can be simplified further as the capture method is not really required:
div :class => 'left-column' do
@entry_list = ''
for entry in @entries do
h3 entry.title
p entry.excerpt
@entry_list += li entry.title
end
end
div :class => 'right-column' do
ul { @entry_list }
end
This is obviously a simple example, but it demonstrates how capturing output can be useful when optimising your templates.