A Quick Guide to DRYML

Posted by Tom on 2006-11-10

UPDATE: This stuff is very out of date now, you might prefer to look here

Gosh - there’s quite a lot in here - where to start? Guess I’ll just dive right in.

To follow along, create yourself a new app, install Hobo, and throw in a demo controller. (for help with that, see Hello World)

Tags can be defined inside your views, making them local to that view. Useful for quick prototyping:

app/views/demo/my_page.dryml

<def tag="time"><%= Time.now %></def>

<p>The time is <time/></p>

More commonly you’d define tags in app/views/hobolib/application.dryml, making them available to your whole app, but for the purposes of exploring we’ll stick with local definitions.

Tags can have attributes. They are available as local variables inside the definition:

<def tag="time" attrs='format'>
  <%= Time.now.strftime(format) %>
</def>

<p>Today is <time format='%A'/></p>

Tags can call other tags, as you’d expect:

<def tag="time" attrs='format'>
  <%= Time.now.strftime(format) %>
</def>

<def tag='today'><time format='%A'/></def>

<p>Today is <today/></p>

Tags can have a body. Use DRYML’s <tagbody/> to insert the body where you need it:

<def tag="flower_box">
  <div class='flower_box'>
    <img src='flower.gif'/><tagbody/>
  </div>
</def>

<flower_box>Nice flower eh?</flower_box>

(Aside: yes, you could have done something similar with CSS, but there plenty you can’t do with CSS, like adding drop shadows in a way that copes with resizing. The drop-shadow technique I prefer needs 8 nested DIVs. Boy was I ever glad to wrap that in a tag)

Watch out for the XHTML compliant image tag (<img ... />). DRYML templates must be valid XML, except for two relaxations: they may contain ERB scriplets (in content or attribute values), and they need not have a single root tag.

OK let’s define a tag that lays out a whole page – yes you can use DRYML tags instead of layouts. This has the advantage of letting the choice of page-layout be made in the view, where it belongs (regular Rails layouts are selected in the controller). It also makes it easier for the page to pass bits-and pieces to the layout, such as a title and an onload event.

Here’s a simple page layout that separates the header and footer with a horizontal rule no less! Notice how we define multiple attributes using commas in attrs attribute.

<def tag='page' attrs='title,onload,header,footer'>
  <html>
    <head><title><%= title %></title></head>
    <body onload="<%= onload %>">

      <div class='header'>
        <%= header %>
      </div>
      <hr/>

      <tagbody/>

      <hr/>
      <div class='footer'>
        <%= footer %>
      </div>

    </body> 
  </html>
</def>


<page title="My Page"
      onload="alert('NO PLEASE! Not an onload alert!')">
  My wonderful page
</page>

Note the absence of a header and footer in that case. We’d rather not define those in attributes, as they’ll probably contain mark-up. So we’ll use an alternate syntax for supplying parameters to tags. Lets quickly get rid of that horrible alert too - if we don’t supply the onload, the result in the rendered page will simply be a blank onload on the <body> tag.

<def tag='page' attrs='title,onload,header,footer'>
  <html>
    <head><title><%= title %></title></head>
    <body onload="<%= onload %>">

      <div class='header'>
        <%= header %>
      </div>
      <hr/>

      <tagbody/>

      <hr/>
      <div class='footer'>
        <%= footer %>
      </div>

    </body> 
  </html>
</def>


<page title="My Page">
  <:header>
    Welcome to my site
  </:header>

  My wonderful page

  <:footer>
    My site. &copy; Me. Some Rights Reserved
  </:footer>
</page>

Those tags that start with a colon, e.g. <:footer>, are not defined tags. They are simply parameters to the <page> tag. They can appear anywhere directly inside the call (i.e. the <page> tag in this case). They could even have been given the other way around, the result would be the same.

Obviously the <page> tag isn’t much use unless it’s defined somewhere where it can be used by multiple pages. The simplest thing is to move it into app/views/hobolib/application.dryml. That library – or taglib as we call them – is implicitly imported by every page in your app. Alternatively here’s how you could put it into your own taglib instead. Move just the <def> into a new file e.g.:

app/views/shared/my_tags.dryml

<def tag='page' attrs='title,onload,header,footer'>
  <html>
    <head><title><%= title %></title></head>
    <body onload="<%= onload %>">

      <div class='header'>
        <%= header %>
      </div>
      <hr/>

      <tagbody/>

      <hr/>
      <div class='footer'>
        <%= footer %>
      </div>

    </body> 
  </html>
</def>

In the page, instead of the full definition of <page>, you now just need to import that taglib using, um, <taglib>. Like this:

<taglib src="shared/my_tags"/>

<page title="My Page">
  <:header>
    <h1>Welcome to my site</h1>
  </:header>
  <:footer>
    <i>My site. &copy; Me. Some Rights Reserved</i>
  </:footer>

  My wonderful page

</page>

If you define your tags in app/views/hobolib/application.dryml, you don’t need to use <taglib> – this is the global taglib and is always imported. Convention over configuration, man!



(edit)