Contents

3. DRYML

With the hobo plugin installed, Dryml will be used for any view templates with a .dryml suffix.

Defining tags

def:

Definition

<def tag="my_tag">This is my tag</def>

Use

<my_tag/>

Output

This is my tag

Tags with attributes:

Definition

<def tag="my_name" attrs="first_name, last_name">
  <p>My name is <%= first_name %> <%= last_name %></p>
</def>

Use

<my_name first_name="Tom" last_name="Locke"/>

Output

<p>My name is Tom Locke</p>

The attributes become local variables inside the tag.

Passing arbitrary values

Definition

<def tag="my_age" attrs="age">My age is <%= age %></def>

Use

<my_age age="#3 * 4"/>   =>   My age is 12

The # at the start of the attribute value signifies an expression as opposed to a string.

Tag Libraries

Use <taglib> to load tags from another Dryml file

Note that app/views/hobolib/application.dryml is implicitly imported into every Dryml template.

Load a taglib from the same directory as the view

Use

<taglib src="tags"/>

Load a taglib from another directory within app/views

Use

<taglib src="shared/date_tags"/>

Load a taglib from a Ruby module

(see Tag Modules below for more)

Use

<taglib module="MyModule::MyNestedModule" />

Tags from Ruby

Tags are just methods, they can be called from ERB scriptlets and from helpers.

Simple tag call

Ruby

my_tag   =>   "This is my tag"

Tag call with parameters

Ruby

my_name :first_name => "Tom", :last_name => "Locke"

(tags only have keyword arguments, never positional arguments)

Tags with content

<tagbody>

Definition

<def tag="box" attrs="title">
  <div class="box">
    <h2><%= title %></h2>
	<tagbody/>
  </div>
</def>

Use

<box title="My Box"><p>I'm in a box</p></box>

Output

<div class="box">
  <h2>My Box</h2>
  <p>I'm in a box</p>
</div>

<tagbody> can occur any number of times.

Tricks with attributes

Accessing undeclared attributes:

Definition

<def tag="my_tag">
  <p onclick="<%= options[:clicker] %>">This is my tag</p>
</def>

Use

<my_tag clicker="alert('!')"/>

Output

<p onclick="alert('!')">This is my tag</p>

xattrs – setting multiple attributes at once

Definition

<def tag="my_tag">
  <p xattrs="# { :class => 'small', :style => 'color:red;' }">
    This is my tag
  </p>
</def>

Use

<my_tag/>

Output

<p class='small' style="color:red;">
  This is my tag
</p>

Forwarding tag options

It’s often useful to forward all the attributes to another tag:

Definition

<def tag="my_tag">
  <p xattrs="#options">This is my tag</p>
</def>

Use

<my_tag class="green" onclick="foo()"/>

Output

<p class="green" onclick="foo()">This is my tag</p>

Because xattrs="#options" is so common, there is a shorthand: xattrs=""

Parameter Tags

<: … > syntax

Definition

<def tag="my_name" attrs="first_name, last_name">
  <p>My name is <%= first_name %> <%= last_name %></p>
</def>

Use

<my_name first_name="Tom"><:last_name>Locke</:last_name></my_name>

Output

<p>My name is Tom Locke</p>

Just an alternate syntax for passing parameters for tags. These are useful when there is a need to pass a large block of content. In fact, when there is a need to pass more than one block of content - the tag body can be used if only one such block is needed.

<page> example

Definition

<def tag="page" attrs="sidebar, main">
  <html>
    <head>...</head>
    <body>
      <div class="side"><%= sidebar %></div>
      <div class="main"><%= main %></div>
    </body>
  </html>
</def>

Use

<page>
  <:sidebar> ... side content ... </:sidebar>
  <:main> ... main content ... </:main>
</page>

Implicit Context

Most pages display some object, and sub-sections of the page drill down into sub-objects. Implicit context makes this kind of page very succinct.

The initial context is set in the controller:

def show
  @this = BlogPost.find :first
end

Accessing the context

The context is available via the method this

<h1><%= this.title %></h1>

Output

<h1>My First Post -- Welcome to my blog</h1>

Changing the context

To change to an attribute named x of the current context, use attr="x". To change to an unrelated object y, use obj="#y" (note the #). Every tag accepts these attributes.

attr example: (the core tag <show> displays the current context)

Use

<h1><show attr="title"/></h1>

You can think of this as this = this.title (although you can’t actually do that–there is no this= method)

obj example:

Use

<div class='flash'><show obj="#flash[:notice]"/></div>

Note – <show> could be implemented:

Definition

<def tag="show"><%= this %></show>

(Although the built in <show> from the core library has a few extra features)

Tags can change the context

The core tag <repeat> repeat changes the context as it iterates over a collection:

Use

<!-- context is a BlogPost, which has_many :comments -->
<repeat attr="comments">
  <div class="comment">
	<div class="date">Posted at <show attr="created_at"/></div
    <div class="body"/><show attr="body"/></div>
  </div>
<repeat/>

To achieve this, use attr or obj on <tagbody/>

Definition

<def tag="repeat">
  <% this.each do |x| %>
    <tagbody obj="#x"/>
  <% end %>
</def>

(again, the core tag <repeat> is smarter than this)

Aliasing tags

Add ”!” to the output of every <show>

Definition

<def tag="boring_show" alias_of="show"/>

<def tag="show"><boring_show xattrs=""/>!</show>

Shorthand for same:

Definition

<def tag="show" alias_current="boring_show"><boring_show xattrs=""/>!</def>

Inner Tags

A convenient mechanism to allow tweaking of the tag inside the tag. Inner tags are created with the content_option and replace_option attributes.

content_option example:

Definition

<def tag="categories">
  <div class="categories">
	<h2 content_option="heading">Categories</h2>
	<ul>
	  <repeat>
	    <li><show attr="name"/></li>
	  </repeat>
	</ul>
  </div>
</def>

Changing the content:

(using <categories> definition–see above)

Use

<categories attr="categories" heading="Post Categories"/>

Output

<div class="categories">
  <h2>Post Categories</h2>
  <ul>
    <li>General</li>
    <li>Documentation</li>
  </ul>
</div>

Or use a parameter tag:

(using <categories> definition–see above)

Use

<categories attr="categories">
  <:heading>post<b>CATEGORIES</b></:heading>
</categories>

Output

<div class="categories">
  <h2>post<b>CATEGORIES</b></h2>
  <ul>
    <li>General</li>
    <li>Documentation</li>
  </ul>
</div>

Adding attributes:

(using <categories> definition–see above)

Use

<categories attr="categories"
            heading="Post Categories" heading.class="small" />

Output

<div class="categories">
  <h2 class="small">Post Categories</h2>
  <ul>
    <li>General</li>
    <li>Documentation</li>
  </ul>
</div>

With a parameter tag

(using <categories> definition–see above)

Use

<categories attr="categories">
  <:heading class="small">post<b>CATEGORIES</b></:heading>
</categories>

Output

<div class="categories">
  <h2 class="small">post<b>CATEGORIES</b></h2>
  <ul>
    <li>General</li>
    <li>Documentation</li>
  </ul>
</div>

replace_option example

With replace_option the entire inner tag is replaced:

Definition

<def tag="categories">
  <div class="categories">
	<h2 replace_option="heading">Categories</h2>
	<ul>
	  <repeat>
	    <li><show attr="categories"/></li>
	  </repeat>
	</ul>
  </div>
</def>

Use

<categories attr="categories">
  <:heading><h3>Categories</h3></:heading>
</categories>

Output

<div class="categories">
  <h3>Categories</h3>
  <ul>
    <li>General</li>
    <li>Documentation</li>
  </ul>
</div>

Nesting inner tags

Definition

<def tag="post">
  <div class="post">
	<h1><show attr="title"/></h1>
	<div class="body"><show attr="body"/></div>
	<categories attr="categories" replace_option="categories"/>
  </div>
</def>

Use

<post categories.heading.class="small">

Output

<div class="post">
  <h1>My First Post</h1>
  <div class="body">Nobody reads your first post anyway do they?</div>
  <h3 class="small">Categories</h3>
  <ul>
    <li>General</li>
    <li>Documentation</li>
  </ul>
</div>

Nested inner tag with attributes and content

(using <post> definition above, and <categories> definition with content_option)

Use

<post>
  <:categories.heading class="small">Post Categories</:categories>
</post>

Output

<div class="post">
  <h1>My First Post</h1>
  <div class="body">Nobody reads your first post anyway do they?</div>
  <h3 class="small">Post Categories</h3>
  <ul>
    <li>General</li>
    <li>Documentation</li>
  </ul>
</div>

Dynamic Inner Tags

The attributes content_option and replace_option can be given dynamic values ("# ... ").

Definition

<def tag="show_fields" attrs="fields">
  <ul>
  <% for field in fields %>
    <li content_option="#field"><%= field.titleize %>: <show attr="#field"/></li>
  <% end %>
  <ul>
</def>

Use

<show_fields fields="name, rank, number" number.class="highlighted" />

Output

<ul>
  <li>Name: Fred</li>
  <li>Rank: Dogsbody</li>
  <li class="highlighted">Number: 12345</li>
</ul>

Tag Modules

Helper Modules

The easiest way to define tags in Ruby is to put them in helper modules. All the techniques described in this section will work in helper modules, but you will not need the <taglib> declarations. Helper modules are automatically included using normal Rails rules.

Importing tags from a Ruby module

(not required for helper modules - they are imported automatically)

Use

<taglib module="MyTags"/>

<taglib module="Can::Be::Nested"/>

You would normally put these modules anywhere in the Rails load path, e.g.

Defining module tags

Tags can be defined in Ruby modules, without using DRYML. Any method that only takes keyword parameters can be used as a tag.

Ruby

module MyTags
  def time_now(options)
	Time.now.to_s(options[:format].to_sym || :short)
  end	
end

Use

<time_now format="long"/>

The tag-body from Ruby

If the tag is called with a body, the method will receive a block. Never yield to the block directly.

Ruby - INCORRECT

def uppercase
  body = yield # WRONG
  body.upcase
end

Instead use the new_context method:

Ruby - CORRECT

def uppercase
  body = new_context { yield } # Good
  body.upcase
end

Using def_tag

The def_tag macro provides some benefits over using a normal method def.

Ruby

def_tag :link_to_all, :join do
  this.map {|x| link_to(tagbody.call(:obj => x),
	                    object_url(x), options) }.join(join)
end

Use

<link_to_all attr="categories" join=", " class="category_link">
  <show attr="name"/>
</link_to_all>

Output

<a href="/categories/3" class="category_link">News</a>, 
<a href="/categories/9" class="category_link">General</a>

Advantages of def_tag:

* Automatically support obj and attr to change context * tagbody is available as a proc which can be called directly (no need for new_contenxt), and takes obj and attr paramters * Parameters can be listed (join in this example). They become available as methods, and are automatically stripped from options, making it convenient to pass options on as extra html attributes (e.g. setting the css class as in this example).

def_tag gotcha - parameters are not locals

In the above example, join looks like a local, but is actually a method. Be careful not to create a local called join.

Ruby (Wrong!)

def_tag :link_to_all, :join do
  join ||= ", "    # creates a local that hides the join method
  this.map {|x| link_to(tagbody.call(:obj => x),
	                    object_url(x), options) }.join(join)
end

(in this case you should do: join2 = join || ", ")

Note this gotcha does not apply to tags created in DRYML. In DRYML the attributes become proper local variables.

Tag Predicates

Defining a tag with a predicate

Definition

<def tag="nav" if="logged_in? && !current_user.super_user?"> ... </def>

<def tag="nav" if="logged_in? && current_user.super_user?"> ... </def>

<def tag="nav"> ... </def>

There are now three definitions of nav. Each time the tag is used, the correct definition will be used according to the predicate.

Accessing attributes and options

Definition

<def tag="nav" if="logged_in && options[:show_private_]"> ... </def>

Use

<nav show_private="#true"/>

Note that unlike inside the definition, you can’t access attributes directly by name even if you list them in attrs="...". You have to use get them from the options hash.

Tag predicates with def_tag

Give the predicate as the second argument, after the name and before the attributes:

Ruby

def_tag :nav, (proc {logged_in?}) do 
  ...
end

To access the options, give the proc an argument

def_tag :nav, (proc {|opts| logged_in? && opts[:show_private]}) do 
  ...
end

Hobo::PredicateDispatch

Tag predicates are implemented using Hobo’s generic predicate dispatch mechanism for Ruby.

Ruby

def MyModule

  include Hobo::PredicateDispatch

  defp :foo (proc {|opts| current_user.guest? && opts[:x] > 10}) do |opts, block|
    ...
  end

end