Re-Introducing Herml

Posted: March 13th, 2009 | Author: kevin | Filed under: Erlang | View Comments

Months ago I blogged a bit about herml, a port of Haml to Erlang. I’ve gotten quite busy at work and haven’t had much time for adding features onto herml. Fortunately for me, my co-conspirator Sean has taken up my slack and added some kick-ass features like iteration and sub-template rendering with just a teensy-weensy bit o’ help yours truly.

Where possible Sean and I have tried to err on the side of choosing an Erlang-like syntax where Haml opts for Ruby-like constructs. This is easily seen in herml’s iteration syntax:

!!!
%html
  %body
    %table
      %tr
        - [{@Message}] <- @Messages
          %td
            @Message

We've taken Erlang's list comprehension syntax and bent it a bit. I'm pretty satisfied with the syntax since it strikes a good balance between "Erlanginess" and readability.

This template would be rendered by calling herml_manager:execute_template/3:

herml_manager:start_link(default, "/tmp").
herml_manager:render_template(default, "looping.herml", [{"Messages, ["Message1", "Message2"]}]).

herml_manager is the integration point you should use to embed herml rendering in your code. Depending on how the manager is started it will cache templates for faster execution and monitor templates for changes and automatically reload them.

The resulting output looks like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <body>
    <table>
      <tr>
        <td>Message1</td>
        <td>Message2</td>
      </tr>
    </table>
  </body>
</html>

herml can also render nested templates via herml:render/2:

%html
  %body
    @@herml:render('sub_main.herml')

"Normal" function calls and variables are prefixed with a single at (@) sign in herml. Prefixing a function call with two at signs (@@), as in the example above, causes herml to use the current environment when rendering the nested template. Practically this means functions which are called via @@ should, in addition to their regular argument list, take one additional argument to hold the environment. herml guarantees this will always be the last argument in the list.

I'm pretty proud of the progress we've made but there's still much more to do:

  • Use a more efficient representation for compiled templates. Erlang modules, maybe?
  • Add more helper functions
  • Streamline herml_manager's interface
  • Write more docs and examples
  • And on and on and on. . .

herml is certainly not for everyone in the same way that Haml isn't universally liked. If you do like the ideas behind Haml I hope you'll give herml a look. And, if you decide to check out herml, please take a look at herml's unit tests. herml's docs are woefully lacking but the unit tests contain many template examples.

To tempt you a bit more, I'll close with a few more examples of herml's current rendering capabilities:


!!!
%html
  %body
    %strong
      Hello world!

renders as:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <body>
    <strong>
      Hello, world!
    </strong>
  </body>
</html>

!!!
%html
  %body
    #message
      %strong
        Hello world!
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <body>
    <div id="message">
      <strong>
        Hello world!
      </strong>
    </div>
  </body>
</html>

!!!
%html
  %body
    %strong
      @Message

is rendered using:
herml_manager:execute_template(herml, "msg.herml", [{"Message", "Goodbye"}])
and looks like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <body>
    <strong>
      Goodbye
    </strong>
  </body>
</html>

  • Using erlang modules for compiled herml templates goes towards how erltl from erlyweb does templating. It's very powerful in that you can do anything in your template because it gets compiled to an erlang module (functions, pattern matching, guards, bifs, importing other functions, metadata).

    However doing multiple functions in a template, etc may go against the herml philosophy on the wiki page but at the same time doing what erltl does but with herml syntax would be insanely powerful and beautiful.
blog comments powered by Disqus