Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 10616 lines (7937 sloc) 160 KB
#LyX 2.0 created this file. For more info see http://www.lyx.org/
\lyxformat 413
\begin_document
\begin_header
\textclass hobo
\use_default_options true
\master hobo.lyx
\begin_modules
logicalmkup
\end_modules
\maintain_unincluded_children false
\language english
\language_package default
\inputencoding auto
\fontencoding global
\font_roman default
\font_sans default
\font_typewriter default
\font_default_family default
\use_non_tex_fonts false
\font_sc false
\font_osf false
\font_sf_scale 100
\font_tt_scale 100
\graphics default
\default_output_format default
\output_sync 0
\bibtex_command default
\index_command default
\paperfontsize default
\spacing single
\use_hyperref false
\papersize default
\use_geometry false
\use_amsmath 1
\use_esint 1
\use_mhchem 1
\use_mathdots 1
\cite_engine basic
\use_bibtopic false
\use_indices false
\paperorientation portrait
\suppress_date false
\use_refstyle 1
\boxbgcolor #e6e6e6
\index Index
\shortcut idx
\color #008000
\end_index
\secnumdepth 3
\tocdepth 3
\paragraph_separation skip
\defskip medskip
\quotes_language english
\papercolumns 1
\papersides 1
\paperpagestyle default
\tracking_changes false
\output_changes false
\html_math_output 0
\html_css_as_file 0
\html_be_strict false
\end_header
\begin_body
\begin_layout Chapter
Chapter 13
\begin_inset Newline newline
\end_inset
THE HOBO DRYML GUIDE
\end_layout
\begin_layout Section
What is DRYML?
\end_layout
\begin_layout Standard
DRYML is a template language for Ruby on Rails that you can use in place
of Rails’ built-in ERB templates.
It is part of the larger Hobo project, but will eventually be made available
as a separate plugin.
\end_layout
\begin_layout Standard
DRYML was created in response to the observation that the vast majority
of Rails development time seems to be spent in the view-layer.
Rails’ models are beautifully declarative, the controllers can be made
so pretty easily (witness the many and various “result controller” plugins),
but the views, ah the views…
\end_layout
\begin_layout Standard
Given that so much of the user interaction we encounter on the web is so
similar from one website to another, surely we don’t have to code all this
stuff up from low-level primitives over and over again? Please, no!
\end_layout
\begin_layout Standard
Of course what we want is a nice library of ready-to-go user interface component
s, or widgets, which can be quickly added to our project, and easily tailored
to the specifics of our application.
\end_layout
\begin_layout Standard
If you’ve been at this game for a while you’re probably frowning about now.
Re-use is a very, very thorny problem.
It’s one of those things that sounds straight-forward and obvious in principle,
but turns out to be horribly difficult in practice.
When you come to re-use something, you very often find that your new needs
differ from the original ones in a way that wasn’t foreseen or catered
for in the design of the component.
The more complex the component, the more likely it is that bending the
thing to your needs will be harder than starting again from scratch.
\end_layout
\begin_layout Standard
So the challenge is not in being able to re-use code, it is:
\end_layout
\begin_layout Standard
\begin_inset Flex Strong
status collapsed
\begin_layout Plain Layout
\begin_inset Flex Emph
status collapsed
\begin_layout Plain Layout
Being able to re-use code in ways that were not foreseen.
\end_layout
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Standard
The reason we created DRYML was to see if this kind of flexibility could
be built into the language itself.
DRYML is a tag-based language that makes it trivially easy to give the
defined tags a great deal of flexibility.
\end_layout
\begin_layout Standard
So DRYML is just a means to an end.
The real goal is to create a library of reusable user-interface components
that actually succeed in making it very quick and easy to create the view
layer of a web application.
\end_layout
\begin_layout Standard
That library is also part of Hobo – the
\begin_inset Flex Emph
status collapsed
\begin_layout Plain Layout
Rapid
\end_layout
\end_inset
tag library.
You will visit this library later on in the book.
Here we will see how DRYML provides the tools and raw materials that make
a library like Rapid possible.
\end_layout
\begin_layout Standard
Discussing DRYML before Rapid means that many of the examples are
\begin_inset Flex Emph
status collapsed
\begin_layout Plain Layout
not
\end_layout
\end_inset
good advice for use of DRYML in a full Hobo app.
For example, you might see
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<%= h this.name %>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Which in an app that used Rapid would be better written
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<view:name/>
\end_layout
\end_inset
or even just
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<name/>
\end_layout
\end_inset
(that’s a tag by the way, called
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
name
\end_layout
\end_inset
, not some metaprogramming trick that lets you use field names as tags).
Bear that in mind while you’re reading this chapter.
The examples are chosen to illustrate the point at hand, they are not necessari
ly something you want to paste right into your application.
\end_layout
\begin_layout Section
Simple page templates and ERB
\end_layout
\begin_layout Standard
In its most basic usage, DRYML can be indistinguishable from a normal Rails
template.
That’s because DRYML is (almost) an extension of ERB, so you can still
insert Ruby snippets using the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<% ...
%>
\end_layout
\end_inset
notation.
For example, a show-page for a blog post might look like this:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<html>
\end_layout
\begin_layout LyX-Code
<head>
\end_layout
\begin_layout LyX-Code
<title>My Blog</title>
\end_layout
\begin_layout LyX-Code
</head>
\end_layout
\begin_layout LyX-Code
<body>
\end_layout
\begin_layout LyX-Code
<h1>My Famous Blog!</h1>
\end_layout
\begin_layout LyX-Code
<h2><%= @post.title %></h2>
\end_layout
\begin_layout LyX-Code
<div class="post-body">
\end_layout
\begin_layout LyX-Code
<%= @post.body %>
\end_layout
\begin_layout LyX-Code
</div>
\end_layout
\begin_layout LyX-Code
</body>
\end_layout
\begin_layout LyX-Code
</html>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
\begin_inset Flex Strong
status collapsed
\begin_layout Plain Layout
No ERB inside tags
\end_layout
\end_inset
\end_layout
\begin_layout Standard
DRYML’s support for ERB is not quite the same as true ERB templates.
The one thing you can’t do is use ERB snippets inside a tag.
To have the value of an attribute generated dynamically in ERB, you could
do:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<a href="<%= my_url %>">
\end_layout
\end_inset
\end_layout
\begin_layout Standard
In DRYML you would do:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<a href="#{my_url}">
\end_layout
\end_inset
\end_layout
\begin_layout Standard
In rare cases, you might use an ERB snippet to output one or more entire
attributes:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<form <%= my_attributes %>>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
We’re jumping ahead here, so just skip this if it doesn’t make sense, but
to do the equivalent in DRYML, you would need your attributes to be in
a hash (rather than a string), and do:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<form merge-attrs="&my_attributes">
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Finally, in a rare case you could even use an ERB snippet to generate the
tag name itself:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<<%= my_tag_name %>> ...
</<%= my_tag_name %>>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
To achieve that in DRYML, you could put the angle brackets in the snippet
too:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<%= "<#{my_tag_name}>" %> ...
<%= "</#{my_tag_name}>" %>
\end_layout
\end_inset
\end_layout
\begin_layout Section
Where are the layouts?
\end_layout
\begin_layout Standard
Going back to the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<page>
\end_layout
\end_inset
tag at the start of this section, from a “normal Rails” perspective, you
might be wondering why the boilerplate stuff like
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<html>
\end_layout
\end_inset
,
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<head>
\end_layout
\end_inset
and
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<body>
\end_layout
\end_inset
are there.
What happened to layouts? You don’t tend to use layouts with DRYML, instead
you would define your own tag, typically
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<page>
\end_layout
\end_inset
, and call that.
Using tags for layouts is much more flexible, and it moves the choice of
layout out of the controller and into the view layer, where it should be.
\end_layout
\begin_layout Standard
We’ll see how to define a
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<page>
\end_layout
\end_inset
tag in the next section.
\end_layout
\begin_layout Section
Defining simple tags
\end_layout
\begin_layout Standard
One of the strengths of DRYML is that defining tags is done right in the
template (or in an imported tag library) using the same XML-like syntax.
This means that if you’ve got markup you want to re-use, you can simply
cut-and-paste it into a tag definition.
\end_layout
\begin_layout Standard
Here’s the page from the previous section, defined as a
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<page>
\end_layout
\end_inset
tag simply by wrapping the markup in a
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<def>
\end_layout
\end_inset
tag:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<def tag="page">
\end_layout
\begin_layout LyX-Code
<html>
\end_layout
\begin_layout LyX-Code
<head>
\end_layout
\begin_layout LyX-Code
<title>My Blog</title>
\end_layout
\begin_layout LyX-Code
</head>
\end_layout
\begin_layout LyX-Code
<body>
\end_layout
\begin_layout LyX-Code
<h1>My Famous Blog!</h1>
\end_layout
\begin_layout LyX-Code
<h2><%= @post.title %></h2>
\end_layout
\begin_layout LyX-Code
<div class="post-body">
\end_layout
\begin_layout LyX-Code
<%= @post.body %>
\end_layout
\begin_layout LyX-Code
</div>
\end_layout
\begin_layout LyX-Code
</body>
\end_layout
\begin_layout LyX-Code
</html>
\end_layout
\begin_layout LyX-Code
</def>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Now we can call that tag just as we would call any other:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<page/>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
If you’d like an analogy to “normal” programming, you can think of the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<def>...</def>
\end_layout
\end_inset
as defining a method called
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
page
\end_layout
\end_inset
, and
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<page/>
\end_layout
\end_inset
as a call to that method.
\end_layout
\begin_layout Standard
\begin_inset Flex Strong
status collapsed
\begin_layout Plain Layout
\begin_inset Flex Emph
status collapsed
\begin_layout Plain Layout
In fact, DRYML is implemented by compiling to Ruby, and that is exactly
what is happening.
\end_layout
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Section
Parameters
\end_layout
\begin_layout Standard
We’ve illustrated the most basic usage of
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<def>
\end_layout
\end_inset
, but our
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<page>
\end_layout
\end_inset
tag is not very useful.
Let’s take it a step further to make it into the equivalent of a layout.
First of all, we clearly need the body of the page to be different each
time we call it.
\end_layout
\begin_layout Standard
In DRYML we achieve this by adding
\begin_inset Flex Emph
status collapsed
\begin_layout Plain Layout
parameters
\end_layout
\end_inset
to the definition, which is accomplished with the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
param
\end_layout
\end_inset
attribute.
Here’s the new definition:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<def tag="page">
\end_layout
\begin_layout LyX-Code
<html>
\end_layout
\begin_layout LyX-Code
<head>
\end_layout
\begin_layout LyX-Code
<title>My Blog</title>
\end_layout
\begin_layout LyX-Code
</head>
\end_layout
\begin_layout LyX-Code
<body param/>
\end_layout
\begin_layout LyX-Code
</html>
\end_layout
\begin_layout LyX-Code
</def>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Now we can call the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<page>
\end_layout
\end_inset
tag and provide our own body:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<page>
\end_layout
\begin_layout LyX-Code
<body:>
\end_layout
\begin_layout LyX-Code
<h1>My Famous Blog!</h1>
\end_layout
\begin_layout LyX-Code
<h2><%= @post.title %></h2>
\end_layout
\begin_layout LyX-Code
<div class="post-body">
\end_layout
\begin_layout LyX-Code
<%= @post.body %>
\end_layout
\begin_layout LyX-Code
</div>
\end_layout
\begin_layout LyX-Code
</body:>
\end_layout
\begin_layout LyX-Code
</page><def tag="page">
\end_layout
\begin_layout LyX-Code
<html>
\end_layout
\begin_layout LyX-Code
<head>
\end_layout
\begin_layout LyX-Code
<title>My Blog</title>
\end_layout
\begin_layout LyX-Code
</head>
\end_layout
\begin_layout LyX-Code
<body param/>
\end_layout
\begin_layout LyX-Code
</html>
\end_layout
\begin_layout LyX-Code
</def>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
See how easy that was? We just added
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
param
\end_layout
\end_inset
to the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<body>
\end_layout
\end_inset
tag, which means our page tag now has a parameter called
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
body
\end_layout
\end_inset
.
In the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<page>
\end_layout
\end_inset
call we provide some content for that parameter.
\end_layout
\begin_layout Standard
It’s very important to read that call to
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<page>
\end_layout
\end_inset
properly.
In particular, the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<body:>
\end_layout
\end_inset
(note the trailing ’:’) is
\begin_inset Flex Emph
status collapsed
\begin_layout Plain Layout
not
\end_layout
\end_inset
a call to a tag, it is providing a named parameter to the call to
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<page>
\end_layout
\end_inset
.
We call
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<body:>
\end_layout
\end_inset
a
\begin_inset Flex Emph
status collapsed
\begin_layout Plain Layout
parameter tag
\end_layout
\end_inset
.
In Ruby terms you could think of the call like this:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
page(:body => "...my body content...")
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Note that is not actually what the compiled Ruby looks like in this case,
but it illustrates the important point that
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<page>
\end_layout
\end_inset
is a call to a defined tag, whereas
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<body:>
\end_layout
\end_inset
is providing a parameter to that call.
\end_layout
\begin_layout Section
Changing Parameter Names
\end_layout
\begin_layout Standard
To give the parameter a different name, we can provide a value to the param
attribute:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<def tag="page">
\end_layout
\begin_layout LyX-Code
<html>
\end_layout
\begin_layout LyX-Code
<head>
\end_layout
\begin_layout LyX-Code
<title>My Blog</title>
\end_layout
\begin_layout LyX-Code
</head>
\end_layout
\begin_layout LyX-Code
<body param="content"/>
\end_layout
\begin_layout LyX-Code
</html>
\end_layout
\begin_layout LyX-Code
</def>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
We would now call the tag like this:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<page>
\end_layout
\begin_layout LyX-Code
<content:>
\end_layout
\begin_layout LyX-Code
...body content goes here...
\end_layout
\begin_layout LyX-Code
</content:>
\end_layout
\begin_layout LyX-Code
</page>
\end_layout
\end_inset
\end_layout
\begin_layout Section
Multiple Parameters
\end_layout
\begin_layout Standard
As you would expect, we can define many parameters in a single tag.
For example, here’s a page with a side-bar:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<def tag="page">
\end_layout
\begin_layout LyX-Code
<html>
\end_layout
\begin_layout LyX-Code
<head>
\end_layout
\begin_layout LyX-Code
<title>My Blog</title>
\end_layout
\begin_layout LyX-Code
</head>
\end_layout
\begin_layout LyX-Code
<body>
\end_layout
\begin_layout LyX-Code
<div param="content"/>
\end_layout
\begin_layout LyX-Code
<div param="aside" />
\end_layout
\begin_layout LyX-Code
</body>
\end_layout
\begin_layout LyX-Code
</html>
\end_layout
\begin_layout LyX-Code
</def>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Which we could call like this:
\end_layout
\begin_layout LyX-Code
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<page>
\end_layout
\begin_layout LyX-Code
<content:> ...
main content here ...
</content:>
\end_layout
\begin_layout LyX-Code
<aside:> ...
aside content here ...
</aside:>
\end_layout
\begin_layout LyX-Code
</page>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
\begin_inset Box Shaded
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout Plain Layout
Note that when you name a parameter, DRYML automatically adds a CSS class
of the same name to the output, so the two
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<div>
\end_layout
\end_inset
tags above will be output as
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<div class="content">
\end_layout
\end_inset
and
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<div class="aside">
\end_layout
\end_inset
respectively.
\end_layout
\end_inset
\end_layout
\begin_layout Section
Default Parameter Content
\end_layout
\begin_layout Standard
In the examples we’ve seen so far, we’ve only put the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
param
\end_layout
\end_inset
attribute on empty tags.
That’s not required though.
If you declare a non-empty tag as a parameter, the content of that tag
becomes the default when the call does not provide that parameter.
This means you can easily add a parameter to any part of the template that
you think the caller might want to be able to change:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<def tag="page">
\end_layout
\begin_layout LyX-Code
<html>
\end_layout
\begin_layout LyX-Code
<head>
\end_layout
\begin_layout LyX-Code
<title param>My Blog</title>
\end_layout
\begin_layout LyX-Code
</head>
\end_layout
\begin_layout LyX-Code
<body param>
\end_layout
\begin_layout LyX-Code
</html>
\end_layout
\begin_layout LyX-Code
</def>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
We’ve made the page title parameterized.
All existing calls to
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<page/>
\end_layout
\end_inset
will continue to work unchanged, but we’ve now got the ability to change
the title on a per-page basis:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<page>
\end_layout
\begin_layout LyX-Code
<title:>My VERY EXCITING Blog</title:>
\end_layout
\begin_layout LyX-Code
<body:>
\end_layout
\begin_layout LyX-Code
...
body content
\end_layout
\begin_layout LyX-Code
</body:>
\end_layout
\begin_layout LyX-Code
</page>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
This is a very nice feature of DRYML - whenever you’re writing a tag, and
you see a part that might be useful to change in some situations, just
throw the param attribute at it and you’re done.
\end_layout
\begin_layout Subsection*
Nested
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
param
\end_layout
\end_inset
Declarations
\end_layout
\begin_layout Standard
You can nest
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
param
\end_layout
\end_inset
declarations inside other tags that have
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
param
\end_layout
\end_inset
on them.
For example, there’s no need to choose between a
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<page>
\end_layout
\end_inset
tag that provides a single content section and one that provides an aside
section as well – a single definition can serve both purposes:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<def tag="page">
\end_layout
\begin_layout LyX-Code
<html>
\end_layout
\begin_layout LyX-Code
<head>
\end_layout
\begin_layout LyX-Code
<title>My Blog</title>
\end_layout
\begin_layout LyX-Code
</head>
\end_layout
\begin_layout LyX-Code
<body param>
\end_layout
\begin_layout LyX-Code
<div param="content"/>
\end_layout
\begin_layout LyX-Code
<div param="aside" />
\end_layout
\begin_layout LyX-Code
</body>
\end_layout
\begin_layout LyX-Code
</html>
\end_layout
\begin_layout LyX-Code
</def>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Here the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<body>
\end_layout
\end_inset
tag is a param, and so are the two
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<div>
\end_layout
\end_inset
tags inside it.
The
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<page>
\end_layout
\end_inset
tag can be called either like this:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<page>
\end_layout
\begin_layout LyX-Code
<body:> ...
page content goes here ...
</body:>
\end_layout
\begin_layout LyX-Code
</page>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Or like this:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<page>
\end_layout
\begin_layout LyX-Code
<content:> ...
main content here ...
</content:>
\end_layout
\begin_layout LyX-Code
<aside:> ...
aside content here ...
</aside:>
\end_layout
\begin_layout LyX-Code
</page>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
An interesting question is, what happens if you give both a
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<body:>
\end_layout
\end_inset
parameter and say,
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<content:>
\end_layout
\end_inset
.
By providing the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<body:>
\end_layout
\end_inset
parameter, you have replaced everything inside the body section, including
those two parameterized
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<div>
\end_layout
\end_inset
tags, so the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<body:>
\end_layout
\end_inset
you have provided will appear as normal, but the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<content:>
\end_layout
\end_inset
parameter will be silently ignored.
\end_layout
\begin_layout Section
The Default Parameter
\end_layout
\begin_layout Standard
In the situation where a tag will usually be given a single parameter when
called, you can give your tag a more compact XML-like syntax by using the
special parameter name
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
default
\end_layout
\end_inset
:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<def tag="page">
\end_layout
\begin_layout LyX-Code
<html>
\end_layout
\begin_layout LyX-Code
<head>
\end_layout
\begin_layout LyX-Code
<title>My Blog</title>
\end_layout
\begin_layout LyX-Code
</head>
\end_layout
\begin_layout LyX-Code
<body param="default"/>
\end_layout
\begin_layout LyX-Code
</html
\end_layout
\begin_layout LyX-Code
</def>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Now there is no need to give a parameter tag in the call at all - the content
directly inside the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<page>
\end_layout
\end_inset
tag becomes the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
default
\end_layout
\end_inset
parameter:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<page>
\end_layout
\begin_layout LyX-Code
...
body content goes here --
\end_layout
\begin_layout LyX-Code
no need for a parameter tag ...
\end_layout
\begin_layout LyX-Code
</page>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
You might notice that the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<page>
\end_layout
\end_inset
tag is now indistinguishable from a normal HTML tag.
Some find this aspect of DRYML disconcerting at first – how can you tell
what is an HTML tag and what it a defined DRYML tag? The answer is – you
can’t, and that’s quite deliberate.
This allows you to do nice tricks like define your own smart
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<form>
\end_layout
\end_inset
tag or
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<a>
\end_layout
\end_inset
tag (the Rapid library does exactly that).
Other tag-based template languages (e.g.
Java’s JSP) like to put everything in XML namespaces.
The result is very cluttered views that are boring to type and hard to
read.
From the start we put a very high priority on making DRYML templates compact
and elegant.
When you’re new to DRYML you might have to do a lot of looking things up,
as you would with any new language or API, but things gradually become
familiar and then view templates can be read and understood very easily.
\end_layout
\begin_layout Section
The Implicit Context
\end_layout
\begin_layout Standard
In addition to the most important goal behind DRYML - creating a template
language that would encourage re-use in the view layer, a secondary goal
is for templates to be concise, elegant and readable.
One aspect of DRYML that helps a lot in this regard is something called
the
\begin_inset Flex Emph
status collapsed
\begin_layout Plain Layout
implicit context
\end_layout
\end_inset
.
\end_layout
\begin_layout Standard
This feature was born of a simple observation that pretty much every page
in a web app renders some kind of hierarchy of application objects.
Think about a simple page in a blog - say, the permalink page for an individual
post.
The page as a whole can be considered a rendering of a
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
BlogPost
\end_layout
\end_inset
object.
Then we have sections of the page that display different “pieces” of the
post – the title, the date, the author’s name, the body.
Then we have the comments.
The list of comments as a whole is also a “piece” of the BlogPost.
Within that we have each of the individual comments, and the whole thing
starts again: the comment title, date, author… This can carry on even further,
for example some blogs are set up so that you can comment on comments.
\end_layout
\begin_layout Standard
This structure is incredibly common, perhaps even universal, as it seems
to be intrinsically tied to the way we visually parse information.
DRYML’s implicit context takes advantage of this fact to make templates
extremely concise while remaining readable and clear.
The object that you are rendering in any part of the page is known as the
\begin_inset Flex Emph
status collapsed
\begin_layout Plain Layout
context
\end_layout
\end_inset
, and every tag has access to this object through the method
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
this
\end_layout
\end_inset
.
The controller sets up the initial context, and the templates then only
have to mention where the context needs to
\begin_inset Flex Emph
status collapsed
\begin_layout Plain Layout
change
\end_layout
\end_inset
.
\end_layout
\begin_layout Standard
We’ll dive straight into some examples, but first a quick general point
about this guide.
If you like to use the full Hobo framework, you will probably always use
DRYML and the Rapid tag library together.
DRYML and Rapid have grown up together, and the design of each is heavily
influenced by the other.
Having said that, this is the DRYML Guide, not the Rapid Guide.
We won’t be using any Rapid tags in this guide, because we want to document
DRYML the language properly.
That will possibly be a source of confusion if you’re very used to working
with Rapid.
Just keep in mind that we’re not allowed to use any Rapid tags in this
guide and you’ll be fine.
\end_layout
\begin_layout Standard
In order to see the implicit context in its best light, we’ll start by defining
a
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<view>
\end_layout
\end_inset
tag, that simply renders the current context with HTML escaping.
Remember the context is always available as
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
this
\end_layout
\end_inset
:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<def tag="view"><%= h this.to_s %></def>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Next we’ll define a tag for making a link to the current context.
We’ll assume the object will be recognized by Rails’ polymorphic routing.
Let’s call the tag
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<l>
\end_layout
\end_inset
(for link):
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<def tag="l">
\end_layout
\begin_layout LyX-Code
<a href="#{url_for this}" param="default"/>
\end_layout
\begin_layout LyX-Code
</def>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Now let’s use these tags in a page template.
We’ll stick with the comfortingly boring blog post example.
In order to set the initial context, our controller action would need to
do something like this:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
def show
\end_layout
\begin_layout LyX-Code
@this = @blog_post = BlogPost.find(params[:id])
\end_layout
\begin_layout LyX-Code
end
\end_layout
\end_inset
\end_layout
\begin_layout Standard
The DRYML template handler looks for the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
@this
\end_layout
\end_inset
instance variable for the initial context.
It’s quite nice to also set the more conventionally named instance variable
as we’ve done here.
Now we’ll create the page.
Let’s assume we’re using a
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<page>
\end_layout
\end_inset
tag along the lines of those defined above.
We’ll also assume that the blog post object has these fields:
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
title
\end_layout
\end_inset
,
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
published_at
\end_layout
\end_inset
,
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
body
\end_layout
\end_inset
and
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
belongs_to :author
\end_layout
\end_inset
, and that the author has a
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
name
\end_layout
\end_inset
field:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<page>
\end_layout
\begin_layout LyX-Code
<content:>
\end_layout
\begin_layout LyX-Code
<h2><view:title/></h2>
\end_layout
\begin_layout LyX-Code
<div class="details">
\end_layout
\begin_layout LyX-Code
Published by <l:author><view:name/></l>
\end_layout
\begin_layout LyX-Code
on <view:published-at/>.
\end_layout
\begin_layout LyX-Code
</div>
\end_layout
\begin_layout LyX-Code
<div class="post-body">
\end_layout
\begin_layout LyX-Code
<view:body/>
\end_layout
\begin_layout LyX-Code
</div>
\end_layout
\begin_layout LyX-Code
</content:>
\end_layout
\begin_layout LyX-Code
</page>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
When you see a tag like
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<view:title/>
\end_layout
\end_inset
, you don’t get any prizes for guessing what will be displayed.
In terms of what actually happens, you can read this as “change the context
to be the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
title
\end_layout
\end_inset
attribute of the current context, then call the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<view>
\end_layout
\end_inset
tag”.
You might like to think of that change to the context as
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
this = this.title
\end_layout
\end_inset
(although in fact
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
this
\end_layout
\end_inset
is not assignable).
But really you just think of it as “view the title”.
Of what? Of whatever is in context, in this case the blog post.
\end_layout
\begin_layout Standard
Be careful with the two different uses of colon in DRYML.
A trailing colon as in
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<foo:>
\end_layout
\end_inset
indicates a parameter tag, whereas a colon joining two names as in
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<view:title/>
\end_layout
\end_inset
indicates a change of context.
\end_layout
\begin_layout Standard
When the tag ends, the context is set back to what it was.
In the case of
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<view/>
\end_layout
\end_inset
which is a self-closing tag familiar from XML, that happens immediately.
The
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<l:author>
\end_layout
\end_inset
tag is more interesting.
We set the context to be the author, so that the link goes to the right
place.
Inside the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<l:author>
\end_layout
\end_inset
that context remains in place so we just need
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<view:name/>
\end_layout
\end_inset
in order to display the author’s name.
\end_layout
\begin_layout Subsection*
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
with
\end_layout
\end_inset
and
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
field
\end_layout
\end_inset
attributes
\end_layout
\begin_layout Standard
The
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
with
\end_layout
\end_inset
attribute is a special DRYML attribute that sets the context to be the
result of any Ruby expression before the tag is called.
In DRYML any attribute value that starts with ’&’ is interpreted as a Ruby
expression.
Here’s the same example as above using only the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
with
\end_layout
\end_inset
attribute:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<page>
\end_layout
\begin_layout LyX-Code
<content:>
\end_layout
\begin_layout LyX-Code
<h2><view with="&@blog_post.title"/></h2>
\end_layout
\begin_layout LyX-Code
<div class="details">
\end_layout
\begin_layout LyX-Code
Published by <l
\end_layout
\begin_layout LyX-Code
with="&@blog_post.author">
\end_layout
\begin_layout LyX-Code
<view with="&this.name"/></l>
\end_layout
\begin_layout LyX-Code
on <view with="&@blog_post.published-at"/>.
\end_layout
\begin_layout LyX-Code
</div>
\end_layout
\begin_layout LyX-Code
<div class="post-body">
\end_layout
\begin_layout LyX-Code
<view with="&@blog_post.body"/>
\end_layout
\begin_layout LyX-Code
</div>
\end_layout
\begin_layout LyX-Code
</content:>
\end_layout
\begin_layout LyX-Code
</page>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Note that we could have used
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
&this.title
\end_layout
\end_inset
instead of
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
&@blog_post.title
\end_layout
\end_inset
.
\end_layout
\begin_layout Standard
The
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
field
\end_layout
\end_inset
attribute makes things more concise by taking advantage of a common pattern.
When changing the context, we very often want to change to some attribute
of the current context.
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
field="x"
\end_layout
\end_inset
is a shorthand for
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
with="&this.x"
\end_layout
\end_inset
(actually it’s not quite the same, using the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
field
\end_layout
\end_inset
version also sets
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
this_parent
\end_layout
\end_inset
and
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
this_field
\end_layout
\end_inset
, whereas
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
with
\end_layout
\end_inset
does not.
This is discussed later in more detail).
\end_layout
\begin_layout Standard
The same template again, this time using
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
field
\end_layout
\end_inset
:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<page>
\end_layout
\begin_layout LyX-Code
<content:>
\end_layout
\begin_layout LyX-Code
<h2><view field="title"/></h2>
\end_layout
\begin_layout LyX-Code
<div class="details">
\end_layout
\begin_layout LyX-Code
Published by <l field="author">
\end_layout
\begin_layout LyX-Code
<view field="name"/></l>
\end_layout
\begin_layout LyX-Code
on <view field="published-at"/>.
\end_layout
\begin_layout LyX-Code
</div>
\end_layout
\begin_layout LyX-Code
<div class="post-body">
\end_layout
\begin_layout LyX-Code
<view field="body"/>
\end_layout
\begin_layout LyX-Code
</div>
\end_layout
\begin_layout LyX-Code
</content:>
\end_layout
\begin_layout LyX-Code
</page>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
If you compare that example to the first one, you should notice that the
: syntax is just a shorthand for the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
field
\end_layout
\end_inset
attribute; i.e.,
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<view field="name">
\end_layout
\end_inset
and
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<view:name>
\end_layout
\end_inset
are equivalent.
\end_layout
\begin_layout Section
Field chains
\end_layout
\begin_layout Standard
Sometimes you want to drill down through several fields at a time.
Both the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
field
\end_layout
\end_inset
attribute and the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
:
\end_layout
\end_inset
shorthand support this.
For example:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<view:category.name/>
\end_layout
\begin_layout LyX-Code
<view field="category.name"/>
\end_layout
\end_inset
\end_layout
\begin_layout Subsection*
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
this_field
\end_layout
\end_inset
and
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
this_parent
\end_layout
\end_inset
\end_layout
\begin_layout Standard
When you change the context using
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
field="my-field"
\end_layout
\end_inset
(or the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<tag:my-field>
\end_layout
\end_inset
shorthand), the previous context is available as
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
this_parent
\end_layout
\end_inset
, and the name of the field is available as
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
this_field
\end_layout
\end_inset
.
If you set the context using
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
with="..."
\end_layout
\end_inset
, these values are not available.
That means the following apparently identical tag calls are not quite the
same:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<my-tag with="&@post.title"/>
\end_layout
\begin_layout LyX-Code
<my-tag with="&@post" field="title"/>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
If the tag requires
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
this_parent
\end_layout
\end_inset
and
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
this_field
\end_layout
\end_inset
, and in Rapid, for example, some do, then it must be called using the second
style.
\end_layout
\begin_layout Subsubsection*
Numeric field indices
\end_layout
\begin_layout Standard
If your current context is a collection, you can use the field attribute
to change the context to a single item.
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<my-tag field="7" />
\end_layout
\begin_layout LyX-Code
<% i=97 %>
\end_layout
\begin_layout LyX-Code
<my-tag field="&i" />
\end_layout
\end_inset
\end_layout
\begin_layout Standard
The
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<repeat>
\end_layout
\end_inset
tag sets
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
this_field
\end_layout
\end_inset
to the current index into the collection.
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<repeat:foos>
\end_layout
\begin_layout LyX-Code
<td><%= this_field %></td>
\end_layout
\begin_layout LyX-Code
<td><view /></td>
\end_layout
\begin_layout LyX-Code
</repeat>
\end_layout
\end_inset
\end_layout
\begin_layout Subsubsection*
Forms
\end_layout
\begin_layout Standard
When rendering the Rapid library’s
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<form>
\end_layout
\end_inset
tag, DRYML keeps track of even more metadata in order to add
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
name
\end_layout
\end_inset
attributes to form fields automatically.
This mechanism does not work if you set the context using
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
with=
\end_layout
\end_inset
.
\end_layout
\begin_layout Section
Tag attributes
\end_layout
\begin_layout Standard
As we’ve seen, DRYML provides parameters as a mechanism for Customizing
the markup that is output by a tag.
Sometimes we want to provide other kinds of values to control the behavior
of a tag: URLs, filenames or even Ruby values like hashes and arrays.
For this situation, DRYML lets you define tag attributes.
\end_layout
\begin_layout Standard
As a simple example, say your application has a bunch of help files in
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
public/help
\end_layout
\end_inset
, and you have links to them scattered around your views.
Here’s a tag you could define:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<def tag="help-link" attrs="file">
\end_layout
\begin_layout LyX-Code
<a class="help"
\end_layout
\begin_layout LyX-Code
href="#{base_url}/help/#{file}.html"
\end_layout
\begin_layout LyX-Code
param="default"/>
\end_layout
\begin_layout LyX-Code
</def>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<def>
\end_layout
\end_inset
takes a special attribute
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
attrs
\end_layout
\end_inset
.
Use this to declare a list (separated by commas) of attributes, much as
you would declare arguments to a method in Ruby.
Here we’ve defined one attribute,
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
file
\end_layout
\end_inset
, and just like arguments in Ruby,
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
file
\end_layout
\end_inset
becomes a local variable inside the tag definition.
In this definition we construct the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
href
\end_layout
\end_inset
attribute from the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
base_url
\end_layout
\end_inset
helper and
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
file
\end_layout
\end_inset
, using Ruby string interpolation syntax (
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
#{....}
\end_layout
\end_inset
).
Remember that you can use that syntax when providing a value for any attribute
in DRYML.
\end_layout
\begin_layout Standard
The call to this tag would look like this:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<help-link file="intro">Introductory Help</help-link>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Using regular XML-like attribute syntax –
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
file="intro"
\end_layout
\end_inset
– passes “intro” as a string value to the attribute.
DRYML also allows you to pass any Ruby value.
When the attribute value starts with
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
&
\end_layout
\end_inset
, the rest of the attribute is interpreted as a Ruby expression.
For example you could use this syntax to pass
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
true
\end_layout
\end_inset
and
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
false
\end_layout
\end_inset
values:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<help-link file="intro" new-window="&true">
\end_layout
\begin_layout LyX-Code
Introductory Help
\end_layout
\begin_layout LyX-Code
</help-link>
\end_layout
\begin_layout LyX-Code
<help-link file="intro" new-window="&false">
\end_layout
\begin_layout LyX-Code
Introductory Help
\end_layout
\begin_layout LyX-Code
</help-link>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
And we could add that
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
new-window
\end_layout
\end_inset
attribute to the definition like this:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<def tag="help-link" attrs="file, new-window">
\end_layout
\begin_layout LyX-Code
<a class="help"
\end_layout
\begin_layout LyX-Code
href="#{base_url}/help/#{file}.html"
\end_layout
\begin_layout LyX-Code
target="#{new_window ? '_blank' : '_self' }"
\end_layout
\begin_layout LyX-Code
param="default"/>
\end_layout
\begin_layout LyX-Code
</def>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
An important point to notice there is that the markup-friendly dash in the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
new-window
\end_layout
\end_inset
attribute became a Ruby-friendly underscore (
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
new_window
\end_layout
\end_inset
) in the local variable inside the tag definition.
Using the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
&
\end_layout
\end_inset
, you can pass any value you like – arrays, hashes, active-record objects…
In the case of boolean values like the one used in the above example, there
is a nicer syntax that can be used in the call…
\end_layout
\begin_layout Section
Flag attributes
\end_layout
\begin_layout Standard
That
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
new-window
\end_layout
\end_inset
attribute shown in the previous section is simple switch - on or off.
DRYML lets you omit the value of the attribute, giving a flag-like syntax:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<help-link file="intro" new-window>
\end_layout
\begin_layout LyX-Code
Introductory Help
\end_layout
\begin_layout LyX-Code
</help-link>
\end_layout
\begin_layout LyX-Code
<help-link file="intro">
\end_layout
\begin_layout LyX-Code
Introductory Help
\end_layout
\begin_layout LyX-Code
</help-link>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Omitting the attribute value is equivalent to giving
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
"&true"
\end_layout
\end_inset
as the value.
In the second example the attribute is omitted entirely, meaning the value
will be
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
nil
\end_layout
\end_inset
which evaluates to
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
false
\end_layout
\end_inset
in Ruby and so works as expected.
\end_layout
\begin_layout Subsubsection*
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
attributes
\end_layout
\end_inset
and
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
all_attributes
\end_layout
\end_inset
locals
\end_layout
\begin_layout Standard
Inside a tag definition two hashes are available in local variables:
\end_layout
\begin_layout Itemize
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
attributes
\end_layout
\end_inset
contains all the attributes that
\begin_inset Flex Emph
status collapsed
\begin_layout Plain Layout
were not declared
\end_layout
\end_inset
in the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
attrs
\end_layout
\end_inset
list of the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
def
\end_layout
\end_inset
but that were provided in the call to the tag.
\end_layout
\begin_layout Itemize
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
all_attributes
\end_layout
\end_inset
contains every attribute, including the declared ones.
\end_layout
\begin_layout Section
Merging Attributes
\end_layout
\begin_layout Standard
In a tag definition, you can use the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
merge-attrs
\end_layout
\end_inset
attribute to take any ‘extra’ attributes that the caller passed in, and
add them to a tag of your choosing inside your definition.
Let’s backtrack a bit and see why you might want to do that.
\end_layout
\begin_layout Standard
Here’s a simple definition for a
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<markdown-help>
\end_layout
\end_inset
tag--it’s similar to a tag defined in the Hobo Cookbook app:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<def tag="markdown-help">
\end_layout
\begin_layout LyX-Code
<a href="http://daringfireball.net/..."
\end_layout
\begin_layout LyX-Code
param="default"/>
\end_layout
\begin_layout LyX-Code
</def>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
You would use it like this:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
Add formatting using
\end_layout
\begin_layout LyX-Code
<markdown-help>markdown</markdown-help>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Suppose you wanted to give the caller the ability to choose the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
target
\end_layout
\end_inset
for the link.
You could extend the definition like this:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<def tag="markdown-help" attrs="target">
\end_layout
\begin_layout LyX-Code
<a href="http://daringfireball.net/..."
\end_layout
\begin_layout LyX-Code
target="&target" param="default"/>
\end_layout
\begin_layout LyX-Code
</def>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Now we can call the tag like this:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
Add formatting using
\end_layout
\begin_layout LyX-Code
<markdown-help target="_blank">markdown</markdown-help>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
OK, but maybe the caller wants to add a CSS class, or a javascript
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
onclick
\end_layout
\end_inset
attribute, or any one of a dozen potential HTML attributes.
This approach is not going to scale.
That’s where
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
merge-attrs
\end_layout
\end_inset
comes in.
As mentioned above, DRYML keeps track of all the attributes that were passed
to a tag, even if they were not declared in the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
attrs
\end_layout
\end_inset
list of the tag definition.
They are available in two hashes:
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
attributes
\end_layout
\end_inset
(that has only undeclared attributes) and
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
all_attributes
\end_layout
\end_inset
(that has all of them), but in normal usage you don’t need to access those
variables directly.
To add all of the undeclared attributes to a tag inside your definition,
just add the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
merge-attrs
\end_layout
\end_inset
attribute, like this:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<def tag="markdown-help">
\end_layout
\begin_layout LyX-Code
<a href="http://daringfireball.net/..."
\end_layout
\begin_layout LyX-Code
merge-attrs param="default"/>
\end_layout
\begin_layout LyX-Code
</def>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
\begin_inset Box Shaded
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout Plain Layout
Note that the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
merge
\end_layout
\end_inset
attribute is another way of merging attributes.
Declaring
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
merge
\end_layout
\end_inset
is a shorthand for declaring both
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
merge-attrs
\end_layout
\end_inset
and
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
merge-params
\end_layout
\end_inset
(which we’ll cover later).
\end_layout
\end_inset
\end_layout
\begin_layout Section
Merging selected attributes
\end_layout
\begin_layout Standard
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
merge-attrs
\end_layout
\end_inset
can be given a value - either a hash containing attribute names and values,
or a list of attribute names (comma separated), to be merged from the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
all_attributes
\end_layout
\end_inset
variable.
Examples:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<a merge-attrs="href, name">
\end_layout
\begin_layout LyX-Code
<a merge-attrs="&my_attribute_hash">
\end_layout
\end_inset
\end_layout
\begin_layout Standard
A requirement that crops up from time to time is to forward to a tag all
the attributes that it understands (i.e.
the attributes from that tag’s
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
attrs
\end_layout
\end_inset
list), and to forward some or all the other attributes to tags called within
that tag.
Say for example, we are declaring a tag that renders a section of content,
with some navigation at the top.
We want to be able to add CSS classes and so on to the main
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<div>
\end_layout
\end_inset
that will be output, but the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<navigation>
\end_layout
\end_inset
tag also defines some special attributes, and these need to be forwarded
to it.
\end_layout
\begin_layout Standard
To achieve this we take advantage of a helper method
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
attrs_for
\end_layout
\end_inset
.
Given the name of a tag, it returns the list of attributes declared by
that tag.
\end_layout
\begin_layout Standard
Here’s the definition:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<def tag="section-with-nav">
\end_layout
\begin_layout LyX-Code
<div class="section"
\end_layout
\begin_layout LyX-Code
merge-attrs="&attributes -
\end_layout
\begin_layout LyX-Code
attrs_for(:navigation)">
\end_layout
\begin_layout LyX-Code
<navigation
\end_layout
\begin_layout LyX-Code
merge-attrs="&attributes &
\end_layout
\begin_layout LyX-Code
attrs_for(:navigation)"/>
\end_layout
\begin_layout LyX-Code
<do param="default"/>
\end_layout
\begin_layout LyX-Code
</div>
\end_layout
\begin_layout LyX-Code
</def>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Note that:
\end_layout
\begin_layout Itemize
The expression
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
attributes - attrs_for(:navigation)
\end_layout
\end_inset
returns a hash of only those attributes from the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
attributes
\end_layout
\end_inset
hash that are
\begin_inset Flex Emph
status collapsed
\begin_layout Plain Layout
not
\end_layout
\end_inset
declared by
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<navigation>
\end_layout
\end_inset
(The
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
-
\end_layout
\end_inset
operator on
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
Hash
\end_layout
\end_inset
comes from HoboSupport)
\end_layout
\begin_layout Itemize
The expression
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
attributes & attrs_for(:navigation)
\end_layout
\end_inset
returns a hash of only those
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
attributes
\end_layout
\end_inset
from the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
attributes
\end_layout
\end_inset
hash that are declared by
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<navigation>
\end_layout
\end_inset
(The
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
&
\end_layout
\end_inset
operator on
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
Hash
\end_layout
\end_inset
comes from HoboSupport)
\end_layout
\begin_layout Itemize
The
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<do>
\end_layout
\end_inset
tag is a “do nothing” tag, defined by the core DRYML taglib, which is always
included.
\end_layout
\begin_layout Subsubsection*
The class attribute
\end_layout
\begin_layout Standard
If you have the following definition:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<def tag="foo">
\end_layout
\begin_layout LyX-Code
<div id="foo" class="bar" merge-attrs />
\end_layout
\begin_layout LyX-Code
</def>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
and the user invokes it with:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<foo id="baz" class="bop" />
\end_layout
\end_inset
\end_layout
\begin_layout Standard
The following content will result:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<foo id="baz" class="bar bop" />
\end_layout
\end_inset
\end_layout
\begin_layout Standard
The
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
class
\end_layout
\end_inset
attribute receives special behavior when merging.
All other attributes are overridden with the user specified values.
The
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
class
\end_layout
\end_inset
attribute takes on the values from both the tag definition and invocation.
\end_layout
\begin_layout Section
Repeated and optional content
\end_layout
\begin_layout Standard
As you would expect from any template language, DRYML has the facility to
repeat sections of content, and to optionally render or not render given
sections according to your application’s data.
DRYML provides two alternative syntaxes, much as Ruby does (e.g.
Ruby has the block
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
if
\end_layout
\end_inset
and the one-line suffix version of
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
if
\end_layout
\end_inset
).
\end_layout
\begin_layout Subsubsection*
Conditionals -
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
if
\end_layout
\end_inset
and
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
unless
\end_layout
\end_inset
\end_layout
\begin_layout Standard
DRYML provides
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
if
\end_layout
\end_inset
and
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
unless
\end_layout
\end_inset
both as tags, which come from the core tag library, and are just ordinary
tag definitions, and as attributes, which are part of the language:
\end_layout
\begin_layout Standard
The tag version:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<if test="&logged_in?"><p>Welcome back</p></if>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
The attribute version:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<p if="&logged_in?">Welcome back</p>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Important note! The test is performed (in Ruby terms) like this:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
if (...your test expression...).blank?
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Got that? Blankiness not truthiness (
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
blank?
\end_layout
\end_inset
comes from ActiveSupport by the way – Rails’ mixed bag of core-Ruby extensions).
So for example, in DRYML:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<if test="&current_user.comments">
\end_layout
\end_inset
\end_layout
\begin_layout Standard
is a test to see if there are any comments – empty collections are considered
blank.
We are of the opinion that Matz made a fantastic choice for Ruby when he
followed the Lisp / Smalltalk approach to truth values, but that view templates
are a special case, and testing for blankness is more often what you want.
\end_layout
\begin_layout Standard
Can we skip
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<unless>
\end_layout
\end_inset
? It’s like
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<if>
\end_layout
\end_inset
with the nest negated.
You get the picture, right?
\end_layout
\begin_layout Subsubsection*
Repetition
\end_layout
\begin_layout Standard
For repeating sections of content, DRYML has the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<repeat>
\end_layout
\end_inset
tag (from the core tag library) and the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
repeat
\end_layout
\end_inset
attribute.
\end_layout
\begin_layout Standard
The tag version:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<repeat with="&current_user.new_messages">
\end_layout
\begin_layout LyX-Code
<h3><%= h this.subject %></h3>
\end_layout
\begin_layout LyX-Code
</repeat>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
The attribute version:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<h3 repeat="&current_user.new_messages">
\end_layout
\begin_layout LyX-Code
<%= h this.subject %>
\end_layout
\begin_layout LyX-Code
</h3>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
Notice that as well as the content being repeated, the implicit context
is set to each item in the collection in turn.
\end_layout
\begin_layout Section
Even/odd classes
\end_layout
\begin_layout Standard
It’s a common need to want alternating styles for items in a collection
- e.g.
striped table rows.
Both the repeat attribute and the repeat tag set a scoped variable
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
scope.even_odd
\end_layout
\end_inset
which will be alternately ‘even’ then ‘odd’, so you could do:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<h3 repeat="&new_messages" class="#{scope.even_odd}">
\end_layout
\begin_layout LyX-Code
<%= h this.subject %>
\end_layout
\begin_layout LyX-Code
</h3>
\end_layout
\end_inset
\end_layout
\begin_layout Standard
That example illustrates another important point – any Ruby code in attributes
is evaluated
\begin_inset Flex Emph
status collapsed
\begin_layout Plain Layout
inside
\end_layout
\end_inset
the repeat.
In other words, the
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
repeat
\end_layout
\end_inset
attribute behaves the same as wrapping the tag in a
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
<repeat>
\end_layout
\end_inset
tag.
\end_layout
\begin_layout Subsubsection*
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
first_item?
\end_layout
\end_inset
and
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
last_item?
\end_layout
\end_inset
helpers
\end_layout
\begin_layout Standard
Another common need is to give special treatment to the first and last items
in a collection.
The
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
first_item?
\end_layout
\end_inset
and
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
last_item?
\end_layout
\end_inset
helpers can be used to find out when these items come up; e.g., we could
use
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
first_item?
\end_layout
\end_inset
to capitalise the first item:
\end_layout
\begin_layout Standard
\begin_inset Box Shadowbox
position "t"
hor_pos "c"
has_inner_box 1
inner_pos "t"
use_parbox 0
use_makebox 0
width "100col%"
special "none"
height "1in"
height_special "totalheight"
status open
\begin_layout LyX-Code
<h3 repeat="&new_messages">
\end_layout
\begin_layout LyX-Code
<%= h(first_item? ?
\end_layout
\begin_layout LyX-Code
this.subject.upcase :
\end_layout
\begin_layout LyX-Code
this.subject) %>
\end_layout
\begin_layout LyX-Code
</h3>
\end_layout
\end_inset
\end_layout
\begin_layout Subsubsection*