A Quick Guide to DRYML
Posted by
Tom | November 10, 2006 18 Responses comments

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!

Reader Comments Add your comment »

I am not convinced. From what I understand, these custom tags are like functions - you define them, reuse them and they can take parameters. We had helpers all this time for that! Why reinvent the wheel just to replace Ruby methods with XML-like markup?

Syntactic sugar and code expressiveness. I like it a lot. I suppose <%= Time.stuff %> would be much more readable in format <time/>.

I find it really clean for something like admin only links. I really dislike having to call a helper. Maybe it’s just me.

But yes, same old wheel, but with shinier rims =P

Here’s a bigger example. I’ve imagined a couple of helpers to try and make it a fair comparison. IMO the advantage of the XML syntax speaks for itself. The implicit context also helps make things a lot clearer:

RHTML

<% panel do %>
  <h1>Categories</h1>
  <% ul_for(@categories) do |item| %>
    <%= object_link "#{display_name(item)} <i>(#{count(item.adverts)})</i>",
                    item %>
  <% end %>
<% end %>

DRYML

<panel>
  <h1>Categories</h1>
  <ul_for>
    <object_link>
      <display_name/> <i>(<count attr="adverts"/>)</i>
    </object_link>
  </ul_for>
</panel>

Why the choice of underscores instead of dashes, then? (”ul-for”, “object-link” …)

Yeah I did consider dashes. In the end I just figured it’s better to have one convention everywhere, even though in markup I do slightly prefer dashes. I assume you prefer dashes. It will be interesting to see how many others do.

I am really impressed by this new templating system. Very, very nice. I would love a combination or Liquid and DRYML.

That was a “combination of Liquid and DRYML”. Being able to restrict ERB usage, and only allow liquid filters, variables, etc.

Would also be amazing to see some sort of include tags, and cascading tag access. So if you have an application.dryml, and include a header.dryml you have access to all the tags defined in application.dryml within the header.dryml.

Thanks Nathaniel :-) Can you expand a bit on what Liquid has that DRYML is lacking? I confess I don’t have a deep knowledge of Liquid.

Ha ha - we both posted at the same time :-)

Yes - restricting ERB usage is something I’ve thought about, although
if Why’s sandbox stands up, maybe that’s an even nicer approach.

Inclusion of tag libraries is all in there. You can say

<taglib src="header" />

(The .dryml suffix is not needed). Or

<taglib module="MyModule"/>

In your example, header.dryml would automatically have access to
application.dryml tags, as application.dryml is implicitly imported in
every dryml file.

A designer’s point of view

This DRML approach promises two excellent benefits. By separating form and function I can control presentation without breaking the code. Being far simpler to read also means that it is easier for me to maintain and explain to my customers.

Looking forward to trying out this on our own rails project where the views have caused no end of friction and time wasting between myself and the rails developer.

Hey,
I love what you’e doing!
Don’t ever change and best of luck.

Raymon W.

I really do love what you’re doing. I was pleasantly surprised to hear a British accent in the screencast.

Is there a transcript available for the screencast? I wouldn’t mind going through the screencast nice and slowly to see what you actually did.

Regards,
Steven

Steven - sorry no transcript. You’ll have to make do with frequent use of the pause button!

[...] “Exploring Very Rapid Web Development Techniques with Hobo” by Tom Locke was definitely cool. Hobo is “an Open Source extension to Ruby on Rails which helps you build full blown web applications incredibly quickly and easily.” Dr. Nic created the MyConfPlan site using Hobo. Filling the gap between an “auto admin” tool and hand coding an entire application, you can get an entire application up and running in a few minutes to a few hours, complete with AJAX, model level security, controller generation and the DRYML tag library. A few of the “smaller features” are automatic routing, a migration generator, authorization and sign-up, search, “kinda” theme support, and many more. Not yet at version 1.0, the Hobo team is working on documentation in the form of a comprehensive screencast series, API stability, and performance. Looking beyond 1.0, they want to add plugins, themes, and user created tag libraries. [...]

[...] “Exploring Very Rapid Web Development Techniques with Hobo” by Tom Locke was definitely cool. Hobo is “an Open Source extension to Ruby on Rails which helps you build full blown web applications incredibly quickly and easily.” Dr. Nic created the MyConfPlan site using Hobo. Filling the gap between an “auto admin” tool and hand coding an entire application, you can get an entire application up and running in a few minutes to a few hours, complete with AJAX, model level security, controller generation and the DRYML tag library. A few of the “smaller features” are automatic routing, a migration generator, authorization and sign-up, search, “kinda” theme support, and many more. Not yet at version 1.0, the Hobo team is working on documentation in the form of a comprehensive screencast series, API stability, and performance. Looking beyond 1.0, they want to add plugins, themes, and user created tag libraries. [...]

Hi Tom,

I get follwing error when I use tag in a dryml page.

MESSAGE ===

NoMethodError in Hello#hello_world

Showing hello/hello_world.dryml where line #29 raised:

undefined method `tagbody’ for #

Extracted source (around line #29):

26:
27:
28:
29:
30:
31:

===
What am I doing incorrectly?

when I use a tag name “flower_box” it gives following error

MESSAGE ===
Hobo::Dryml::DrymlException in Hello#hello_world

Showing hello/hello_world.dryml where line #27 raised:

invalid tag=”flowerbox” attribute on — at app/views/hello/helloworld.dryml:27

===

Regards,
HAK

Hi! Today
completely changed system and all links which were in bookmarks were lost. Including the link on
hobocentral.net ! 4 hours searched for a site on the Internet also has just now found through http://google.com/
X-rum, I can not find your number ICQ write to me please!!!

Clearly. Thanks!
:))


Write a Comment

Comments are formatted using markdown. To include code, either quote it in `backticks` or indent a code-block by 4 spaces.