Templating

Putting code and html together (either html in the code or vice versa) becomes unwieldy and ugly after a certain level of complexity (a little after “hello world”), and for this reason, web developers have traditionally used templates to separate off all the display work. There is debate over exactly how much power a templating system should have (should it be able to execute arbitrary code, or only adhere to a small set of tools), and the construction of a templating system is relatively easy, so templating systems are many and diverse. We have included with Arrow our own templating system – complete, extensible and (we think) intuitive – but have also made it easy for you to use any other templating system. If you already have a favorite, and have no interest in seeing another, (even if you might like it,) you can skip past the tutorial on ours and learn how to use your own

Template handling is, to some degree, integrated with the Arrow server. The arrow config file has a templates section.

Arrow::Template

Syntax

The Arrow Templating System uses preprocessing tags to distinguish template directives. These are both <?...?> and [?...?]. The square bracket version is meant to be used inside of other html tags (e.g., <a href="[?attr link?]">...)

The Directives

attr
<?attr attribute ?>
<?attr "fmtstring" % attribute ?>
A simple directive similar to attr_accessor in Ruby, this defines a method attribute on the template object which can be used to insert one or more objects into the template. Each object will be stringified and then joined together with the empty string, and the resultant String will replace each instance of the <attr> tag. If the tag specified a fmtstring, it will be used in the stringification instead of #to_s.
call
<?call attribute.method ?>
<?call “fmtstring” % attribute.method ?>
<?call attribute.method(args) ?>
This directive also defines a method of the template object, but also specifies one or more methods that should be called on the inserted objects before replacing the tag.
set
<?set attribute value ?>
<?set attribute value.method ?>
<?set attribute value.method(args) ?>
Sets template variable attribute to the specified value, which will then be accessible through the rest of the template.
escape
<?escape attribute.method ?>
Acts like the <call> directive, but HTML-escapes the String just before it is inserted into the output (e.g., turns all < and > characters into into the corresponding &lt; and &gt; entities).
urlencode
<?urlencode attribute.method ?>
Acts like the <call> directive, but URL-encodes the String just before it is inserted into the output.
if/elsif/else
<?if statement ?>
...
<?elsif statement ?>
...
<?else?>
...
<?end if ?>
Your run-of-the-mill conditional flow control. The else and elsifs are optional, the elsifs can be repeated any number of times (including zero), and the statement can be any template variable, method chain off a template variable, or regular expression match of a template variable (like <?if var.method =~ /regex/ ?>).
for
<?for arglist in iterable ?>
...
<?end for ?>
For each element of the iterable object (i.e., an object that implements the Enumerable interface), the element is assigned to a template variable (or multiple, if appropriate), and the enclosed content is rendered. The arglist can have default values (as in: <?for a,b=1 in a_hash ?>), array-slurping arguments (*args) and hashified arguments.
Also defined during iteration is a template variable iterator, which is an Arrow::Template::Iterator object that can be used to interact with the iteration. See the API documentation for the Arrow::Template::Iterator class for more on what it can do.
yield
<?yield arglist from attribute.block_method ?>
...
<?end yield ?>
Calls the specified block_method on the given template attribute, passing a rendering function for the contained content as the block. Each call to the block will assign arguments to the arglist and render the content. This is a more-generic version of the functionality represented by the for directive, useful for calling block-accepting methods besides #each.
import
<?import attribute ?>
<?import attribute as altname ?>
<?import attribute, attr2 ?>
<?import attribute as altname, attr2 as altname2 ?>
This tag causes one or more template values from a containing template to be imported into a subordinate one. For example, if there is a template that contains the attribute foo, rendering a sub-template which has <?import foo ?> in it would cause the sub-template’s foo to be set to the container template’s foo at the point at which it is rendered.
include
<?include filename ?>
<?include filename as name ?>
This directive is replaced either with the contents of the template specified (first form), or a placeholder attribute with the specified name (second form).
The first form merges the two templates together, while the second keeps them separate for the purposes of interaction. For example, you may wish to define a generic table template called “table.tmpl”, but include it in another template in such a way that it can be interacted with separately:
In the template:

The times for this race were:<br/>
<?include table.tmpl as thisrace ?>

Total times:<br/>
<?include table.tmpl as totals ?>
In the applet:

tmpl.thisrace.rows << ["fred" => 1.10], ["wilma" => "1.05"]
tmpl.totals.rows << ["fred" => 5.77], ["wilma" => 4.95]

Templated Applets – Hello World (templated)

First, we need to tell Arrow where to look for templates:

   1  templates: 
   2    path:
   3      - "/www/templates"
Template config section

The path config item takes one or more directories to use when searching for a template. An applet registers which templates it will use when it’s loaded via one or more template directives. If they’re not absolute paths (i.e., begin with /), then they’re searched for relative to the paths in the templates.path:

   1  class HelloApplet < Arrow::Applet
   2      # Applet signature
   3      template :hello => 'hello-world.tmpl'
   4  end
   5  # ~> -:2: uninitialized constant Arrow (NameError)
Loading a few templates

When the applet is running, it can now load the appropriate template by using its #load_template method and passing the key assigned to the template it wishes to load. You can then fill it with data and make it the return value of an action:

   1  def_action :templated do |txn, *args|
   2      self.log.debug "In the 'templated' action of the %s applet." %
   3          self.signature.name
   4  
   5      # Get a copy of the ':hello' template
   6      templ = self.load_template( :hello )
   7  
   8      # Fill in some values
   9      templ.txn = txn
  10      templ.time = Time.now.to_s
  11      templ.applet = self
  12  
  13      return templ
  14  end
  15  # ~> -:2: undefined method `def_action' for main:Object (NoMethodError)
To render a template, just return it from an action

So now we need to create a /www/templates/hello-world.tmpl file made to display our excitingly original page.

   1  <html>
   2    <head>
   3      <title><?attr message ?></title>
   4    </head>
   5    <body>
   6  
   7      <h1><?attr message ?></h1>
   8  
   9    </body>
  10  </html>
A page template

Which is, for the most part, just static XHTML that will be displayed verbatim. The template directives designate places where the display will be determined by the data that had been saved into the Arrow::Template object in the action itself. This template makes use of the attr, call, if, and include directives, as described above.

Renderers

(Describe the details of rendering, and how to influence how things are rendered.)

Adding Directives

(Describe in general terms how to add directives by putting files in an arrow/template/ directory)

Sample Directive

(How to build a template directive, step-by-step)

Using Other Templating Systems

(Describe how to change templating systems by using a different template.loader)

Writing a Loader

(How to write a loader for your templating system of choice)