templo

Templo

WARNING: ::use:: syntax changed in the 1.1 release of mtwin package, please read below !

Introduction

Templo is a template engine designed to generate XHTML in neko.

Templo works as follow :

- the program chooses a template file to render (ex: mypage.mtt) using mtwin.templo.Loader
- the Loader calls the temploc executable to compile the template into a .n
- temploc preprocesses the page to expand the macro calls and generate an intermediate xml template
- templo transforms the produced xml into a .neko file containing all instructions required to produce the template result
- templo compiles the .neko file into a neko module (.n) using the nekoc compiler
- the Loader loads the freshly compiled module and executes it to render some xhtml

After this first execution, the module stays available for later calls and the Loader will jump straight to point 3 until mypage.mtt or macros.mtt files are modified.

This behaviour can be compared to java jsp files which are compiled into .class files and used until the origin .jsp file is modified.

This workflow implies the following :

  • you have a temporary directory, which your apache user can write in
  • the apache user can call the temploc executable to compile .mtt files
  • the apache user can call the nekoc executable to compile .neko files

Sample usage of templo in haxe :

// set some parameters
mtwin.templo.Loader.BASE_DIR = "/home/user/project/tpl/";
mtwin.templo.Loader.TMP_DIR = "/home/user/project/tmp/";
mtwin.templo.Loader.MACROS = null; // no macro file

// the template context which will be available 
var context = { 
  userName  : "Nestor",
  lovesHaxe : true,
  data      : [1,2,3,4,5]
};

// loads template 
var t = new mtwin.templo.Loader("mypage.mtt");
var r = t.execute(context);
neko.Lib.print(r);

Installation

Templo is available in the Motion-Twin public haxe libraries (available using cvs:)

cvs -d :pserver:anonymous@cvs.motion-twin.com:/cvsroot co haxe_mtwin

- First you should take a look at the lib/mtwin/templo directory in the library path. It contains a temploc.hxml file which can be run using haxe to produce the temploc executable.
- Put this executable in an accessible folder (BIN_PATH) and check that your apache user will be able to execute it.
- mtwin package should be installed in your haxe library path

Why using temploc executable instead of a raw library ?

In fact, you have the choice, if you don't want to install another executable on your system, you can use mtwin.templo.Template instead of mtwin.templo.Loader. The executable just provides a unified way of compiling templates, and let you compile your templates offline.

Templates syntax

Templates contain expressions delimited by :: :: just like the haxe.Template syntax.

Here comes the first template example :

<html>
    <head>
        <title>Foo</title>
    </head>
    <body>
        <h1>This is my home page</h1>

        ::if user.isLogged::
            <div>Welcome ::user.login::</div>
        ::elseif specialOffer::
            <div>I have something special for you today</div>
        ::else::
            <div>I don't know you but you look cool</div>
        ::end::

        <h2>List of connected users</h2>
        <ul>
        ::foreach connectedUser listOfConnectedUsers::
            <li><a href="/user/::connectedUser.id::">::connectedUser.login::</a></li>
        ::end::
        </ul>
    </body>
</html>

print

The default behaviour of ::exp:: is to print the content of exp inside the template.

For instance "::login::" may produce "Jackson".

Because templo try to be smart, it will automatically escape your output to produce clean XHTML.

// in haxe
context.message = " is a < b or b > c, maybe is should include <p> in this ?";

// in the template
::message::

// will produce
is a &lt; b or b &gt; c, maybe is should include &lt;p&gt; in this ?

Each expression is a neko one, the syntax is close to the haxe syntax but there is no private/protected protection nor magic getter/setter because neko is not haxe.

You may call methods, access fields, arrays, etc... starting for the context provided by the haxe application.

His last message was : ::someUser.lastMessage()::

raw

If you want to print some pre-produced XHTML inside your template, you can prefix the expression with raw :

// in haxe
context.myMessage = "<p>Hello haxe</p>";

with the template

before : ::myMessage:: 
after : ::raw myMessage::

will produce

before : &lt;p&gt;Hello haxe&lt;/p&gt;
after : <p>Hello haxe</p>

if

This is the usual condition you find everywhere in the world (or nearly) :

::if <somecondition>::
write xhtml
::end::

elseif

::if <somecondition>::
write xhtml
::elseif <someothercondition>::
write xhtml
::end::

else

::if <somecondition>::
write xhtml
::elseif <someothercondition>::
write xhtml
::else::
write default xhtml
::end::

cond

This is a small improvement of ::if::. Sometimes it is cleaner and nice to put the ::if:: inside the element. Templo will understand this
as write this element only if the ::cond:: evals to true.

<ul id="userMenu" ::cond user.isLogged::>
    <li><a href="/logout">Log out</a></li>
    <li><a href="/account">My account</li>
</ul>

foreach

Repeat over some iterable.

::foreach value iterable::
    You can use ::value:: there
::end::

For example if the context provides listOfNumbers = [0,2,5,6], you can use :

::foreach n listOfNumbers::
    Number = ::n::
::end::

repeat

This is the same as a ::foreach:: but inside the element, just like ::cond::, templo will understand a ::repeat:: like print this element and its content for each element in the iterable.

<ul>
    <li ::repeat user listOfConnectedUsers::>
        <a href="/user/::user.id::">::user.name::</a>
    </li>
</ul>

repeat and foreach context

Because template design often requires loop data, templo creates for each repeat or foreach an information context accessible using repeat.<itemName>.* :

  • repeat.<item>.index : an integer starting from 0 to length - 1
  • repeat.<item>.number : an integer starting from 1 to length
  • repeat.<item>.odd : true if index is odd
  • repeat.<item>.even : true if index is even
  • repeat.<item>.first : true if current element is the first element
  • repeat.<item>.last : true if current element is the last element (when size is available)
  • repeat.<item>.size : the data length (when available)
<table>
    <tbody>
        ::foreach user listOfConnectedUsers::
        <tr class="odd::repeat.user.odd::">
            <td>::repeat.user.number::</td>
            <td>::user.name::</td>
        </tr>
        ::end::
    </tbody>
</table>

May produce something like :

<table>
    <tbody>
        <tr class="oddfalse">
            <td>1</td>
            <td>Joe White</th>
        </tr>
        <tr class="oddtrue">
            <td>2</td>
            <td>Janet Red</td>
        </tr>
        <tr class="oddfalse">
            <td>3</td>    
            <td>Diana Tres</td>
        </tr>
    </tbody>
</table>

set

This operation allows you to create a variable inside your template :

::set myVar = myValue::

Usefull to simplify conditions, create loop sums, etc...

::set isLogged = (user != null) && (user.id != null)::

::set sum = 0::
::foreach i listOfNumbers::
    number = ::i::
    ::set sum = sum + i::
::end::
Sum = ::sum::

fill

The ::fill:: acts quite like a ::set:: but allows you to capture some xhtml and put it into a variable :

::fill navigation::
<div>
    <a href="/previous">Previous</a> |
    ::foreach page resultPages::
    <a href="/page/::page::">::page::</a> |
    ::end::
    <a href="/next">Next</a>
</div>
::end::

<!-- we use the filled variable -->
::raw navigation::

<table>
    <thead>
        <tr>
            <th>#</th>
            <th>name</th>
        </tr>
    </thead>
    <tbody>
        ::foreach name listOfNames::
        <tr>
            <th>::startIndex + repeat.name.index::</th>
            <th>::name::</th>
        </tr>
    </tbody>
</table>

<!-- we reuse the filled variable to avoid executing the loop twice -->

::raw navigation::

::fill:: also provides some interesting features in coordiation with ::use::, as explained below.

use

The organization of your templates often requires to split things in different files for reuse. the ::use:: close allows you to call an external template from the current template. The called template will share its context with the current template.

userMenu.mtt :

<div id="userMenu">
    <div>Logged as ::user.login::</div>
    <ul>
        <li><a href="/logout">Log out</a></li>
        <li><a href="/account">My account</a></li>
    </ul>
</div>

myPage.mtt :

<html>
    <head>
        ...
    </head>
    <body>
        ...

        <!-- show the menu two times, ::end:: is requires there (explained later) -->
        ::use 'userMenu.mtt'::::end::
        ::use 'userMenu.mtt'::::end::
    
        ...
    </body>
</html>

At Motion-Twin, we are fond of this kind of templates :

design.mtt :

<html>
    <head>
        <title>My title</title>
    </head>
    <body>
        <h1>My title</h1>

        <!-- assume the template which will use design.mtt fills the content variable -->
        ::raw content::
    </body>
</html>

mypage.mtt :

::use 'design.mtt'::

    ::fill content::
        <h2>My page</h2>
        some data here
    ::end::

::end::

Because the ::use X:: ::fill content:: ::end:: ::end:: syntax is quite repetitive and the design.mtt approch to website templating is really usefull, each ::use:: defines a __content__ variable which is filled with the content of the ::use:: up to ::end::.

This means that the following usages are equivalent :

::use 'design.mtt'::
    ::fill __content__::
    <h2>The content to write in design.mtt</h2>
    ::end::
::end::

::use 'design.mtt'::
    <h2>The content to write in design.mtt</h2>
::end::

And the new design.mtt will looks like :

<html>
    <head>
        <title>My title</title>
    </head>
    <body>
        <h1>My title</h1>

        <!-- assume the template which will use design.mtt fills the content variable -->
        ::raw __content__::
    </body>
</html>

In the above examples, you can see that each used template files named is quoted ::use 'design.mtt'::. This is because (from mtwin package version 1.1) the use syntax is a regular neko expression. This means that the following code works :

::use theme+'.mtt'::
...
::end::

Providing __theme__ is a context variable set to the string 'design', templo will use the 'design.mtt' file, changing the variable to 'blueDesign' will tell templo to use 'blueDesign.mtt' instead.

attr

This is a pseudo attribute which may be used as follows :

<ul>
::foreach link links::
    <li><a ::attr title link.title; href link.href::>::link.title::</a></li>
::end::
</ul>

which may produce :

<ul>
    <li><a href="http://www.google.com" title="google search">google search</a></li>
    <li><a href="http://www.haxe.org" title="Haxe">Haxe</a></li>
</ul>

Please note that it is the same as writing :

<ul>
::foreach link links::
    <li><a href="::link.href::" title="::link.title::">::link.title::</a></li>
::end::
</ul>

The real interest of ::attr:: resides in forms select/option and input elements :

<!-- add the checked="checked" attribute only if someCondition evals to true -->
<input type="checkbox" value="some value" ::attr checked someCondition::/>

<select>
    ::foreach opt availableOptions::
    <option value="::opt.value::" ::attr selected (opt.value == currentValue)::>::opt.name::</option>
    ::end::
</select>

The usage of macros

Macros are stored in *one xml file*, usually macros.mtt in your template base dir.

A macro file looks like :

<macros>

  <!-- shows a user -->
  <macro name="userView(user)">
    <div id="user">
      <div class="name">::user.name::</div>
      <div class="lastLog">::user.lastLogDate::</div>
    </div>
  </macro>

  <!-- presents a date without hours -->
  <macro name="date(d)">::d.toString().substr(0,10)::</macro>

</macros>

And these macros must be used inside your templates like follows :

  ::foreach u userList::
  $$userView(::u::)
  ::end::

  <div>Last modification date : $$date(::lastModDate::)</div>

Macros are not processed as functions, they are written inside the template during pre-processing phase (see introduction).

Thus after Pre-Processing, the above example will look like :

  ::foreach u userList::
    <div id="user">
      <div class="name">::u.name::</div>
      <div class="lastLog">::u.lastLogDate::</div>
    </div>
  ::end::

  <div>Last modification date : ::lastModDate.toString().substr(0,10)::</div>

Macros are used by the preprocessor to write XHTML in your template, they do not understand :::: expressions ! That's why you can pass any string to a macro call and you must add ::var:: instead of just passing the var parameters.

Let's examine the following example :

<macro name="showText(style, someText)">
  <div class="textBox ::style::">
    ::someText::
  </div>
</macro>

.... other file ....

<!-- 

calls the showText macro using the string 'icyBox' as style 
and some html string as someText.

Because we may use some coma and parenthesis inside this parameters, 
we put it inside {} to delimit real macro call parameters.

-->

$$showText(icyBox, {
<p>This is the first time you enter our website, welcome and enjoy the trip</p>
<p>You may come back any time</p>
})

This produces :

<div class="textBox icyBox">
<p>This is the first time you enter our website, welcome and enjoy the trip</p>
<p>You may come back any time</p>  
</div>

While creating macros, you must ensure that their usage will produce well formed XML or the template won't compile.

For instance, a macro may produce onclick attributes :

<macro name="confirm(confirmMessage)">onclick="return confirm('::confirmMessage::');"</macro>

Used like this :

<a href="someurl" $$confirm(Really go to there ?)>somewhere</a>

Is legal and will work.

But the following usage is dangerous :

<a href="someurl" $$confirm(What about '" characters ?)>somewhere</a>

Because it will produce the following html :

<a href="someurl" onclick="return confirm('What about '" characters ?');">somewhere</a>

Optimized mode

During website development, you will want templo to check files for modification and automatic recompilation of templates when required.

But most of the time, when your website is deployed on the production server, your templates won't change and making the server check modification time of each file isn't required.

Templo provides and Optimized mode (ie: Production mode) which assume that your templates are all existing and pre-compiled. It will ignore template sources and will jump straight to neko modules.

This feature have some benefits :

- faster (no modification time check)
- safer (you compile all your templates before deploying the project, if one template fails to compile you know it before upload)
- you do not have to distribute your template sources, only compiled modules

Usually, during production you do not set the mtwin.templo.Loader.OPTIMIZED flag to true.

When your project is complete, you can put a flag in some config file which will set this flag on once on your production server.

Then before deploying you ensure that all your templates are compiled using a command looking like this :

// unix command example
temploc -m tpl/macros.mtt -r tpl/ -o bintpl/ `find tpl -name *.mtt`

After reading this document, you should understand the temploc parameters :

Usage: temploc -o </destination/dir> -m <macrofile.mtt> -r </templates/repository> <files...>

They are matching what you may call inside you haxe code :

mtwin.templo.Loader.TMP_DIR = </destionation/dir>
mtwin.templo.Loader.MACROS = <macrofile.mtt>
mtwin.templo.Loader.BASE_DIR = </templates/repository>

In your application, add

mtwin.templo.Loader.OPTIMIZED = true;

Examples

You can browser the subversion repository of the public hxBlog project to see how Templo can be used in a web application.

https://svn.motion-twin.com/hxblog/trunk

More

Templo use an intermediate xml attribute language to produce .neko files. This language is available in your templates, you can learn more in the templo attribute language document.

version #4066, modified 2008-08-29 00:57:26 by baurel