Kwartz Users' Guide

Makoto Kuwata <kwa(at)kuwata-lab.com>
last update: $Date: 2005-03-07 08:15:41 +0900 (Mon, 07 Mar 2005) $

Preface

This is the users' guide to Kwartz(*1), a template system which realized the concept of 'Separation of Presentation Logic and Presentation Data (SoPL/PD).'

(*1)
Development of Kwartz had subsidized by Exploratory Software Project of IPA (Information-Technology Promotion Agency Japan).

Table of Contents



Introduction to Kwartz

What's Kwartz?

Kwartz(*2) is a template system which realized the concept of 'Separation of Presentation Logic and Presentaion Data'(SoPL/PD).

Kwartz-ruby is an implemenation of Kwartz in Ruby. There is a plan to implement Kwartz in PHP or Java.

In the following, the word 'Kwartz' means the specification of the template system and the word 'Kwartz-ruby' means the implementation of it in Ruby language.

(*2)
'Kwartz' is pronounced like 'Quartz'.

Features

Kwartz has the following features:

Separates presentation logic from presentation data.

Using template systems such as Smarty, Velocity, XMLC, amrita, etc, you can separate HTML design from business logic as a template. With Kwartz, you can separate presentation logic from a template. In other words, Kwartz divides a template into 'presentation data' and 'presentation logic'. You need not mix presentation logic into HTML files nor main program.

Very fast

Kwartz creates a script from a template (= presentation data and presentaion logic). All you have to do in main program is to call the output script. Because Kwartz doesn't use DOM trees or the like, it is both fast and light-weight.

Multiple programing languages

Kwartz can create output scripts for Ruby(eRuby), PHP, and JSP(JSTL 1.1 & 1.0) from a template file, because Kwartz uses an internal intermediate language. You don't have to change the presentation layer at all, even if you changed programming language.

Doesn't break HTML design at all

You must use directives like {foreach ...}{/foreach} in Smarty or #foreach(...) in Jakarta Velocity. These directives break the HTML design of template. Kwartz doesn't break HTML design because Kwartz uses id attributes for marking in an HTML template.

Can handle any text file

Kwartz uses an original template parser; not using an HTML or XML parser, it is able to handle any text file (HTML, PostScript, RTF, and so on). This also means that Kwartz can handle non-well-formed XML files, as well as well-formed XML files. This is an advantage of Kwartz against Enhydra XMLC or amrita, which handle only XML/HTML files.

Auto-Sanitizing and Partial-Sanitizing Support

Kwartz can do sanitizing automatically. You don't need to write 'CGI.escapeHTML(var)' or 'htmlspecialchars($var)'. You are free to turn sanitizing on/off, as well specifying which parts of the template to sanitize.


Simple Example

In Kwartz, a template is defined as both presentation data and presentation logic. They may be described in separate files.

This is an example of a presentation data file.

Presentation data file(example1.html):

<table>
  <tr id="list">
    <td id="item">foo</td>
  </tr>
</table>

And the following is an example of presentation logic. In presentation logic, you can operate on elements which are marked in presentation data.

Presentation logic file(example1.plogic):

#item {            // The element which is marked by 'id="item"'
  value: member;      // replace the content with value of a variable 'member'
  remove: "id";       // remove the id attribte
}

#list {            // The element which is marked by 'id="list"'
  remove: "id";       // remove the id attribute
  plogic: {           // define the presentation logic
    foreach (member in member_list) {
      @stag;          // start tag
      @cont;          // content
      @etag;          // end tag
    }
  }
}

Kwartz creates output scripts automatically for eRuby, PHP, and JSP (JSTL 1.1&1.0) from the above two files. This action is called 'compiling'. To compile, enter one of the following commands:

### for eRuby
$ kwartz -l eruby  -p example1.plogic example1.html > example1.rhtml

### for PHP
$ kwartz -l php    -p example1.plogic example1.html > example1.php

### for JSTL 1.1
$ kwartz -l jstl11 -p example1.plogic example1.html > example1.jsp

### for JSTL 1.0
$ kwartz -l jstl10 -p example1.plogic example1.html > example1.jsp

The following is the output script for each respective language.

Output Script for eRuby(example1.rhtml):

<table>
<% for member in member_list do %>
  <tr>
    <td><%= member %></td>
  </tr>
<% end %>
</table>

Output Script for PHP(example1.php):

<table>
<?php foreach ($member_list as $member) { ?>
  <tr>
    <td><?php echo $member; ?></td>
  </tr>
<?php } ?>
</table>

Output Script for JSTL 1.1(*3)(example1.jsp):

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<table>
<c:forEach var="member" items="${member_list}">
  <tr>
    <td><c:out value="${member}" escapeXml="false"/></td>
  </tr>
</c:forEach>
</table>

Output Script for JSTL1.0(example1.jsp):

<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<table>
<c:forEach var="member" items="${member_list}">
  <tr>
    <td><c:out value="${member}" escapeXml="false"/></td>
  </tr>
</c:forEach>
</table>

Using the command-line option '-e' when compiling, Kwartz will output sanitized scripts. For sanitizing, CGI.escapeHTML() is used in eRuby, htmlspecialchars() in PHP, and <c:out/> without escapeXml="false" in JSTL.

Then execute or import these output scripts into your main program, like this:

Main Program (Ruby) :

## set a data
member_list = [ 'Oboro', 'Ominae', 'Jaquemonde' ]

## print out using ERB
require 'erb'
require 'cgi'        # for sanitizing
str = File.open('example1.rhtml') { |f| f.read() }
str.untaint
trim_mode = 1
erb = ERB.new(str, $SAFE, trim_mode)
print erb.result(binding())

## or print out using ERuby
require 'eruby'
require 'cgi'        # for sanitizing
ERuby::import('example1.rhtml')

Main Program (PHP) :

<?php
   // set a data
   $member_list = array('Oboro', 'Ominae', 'Jaquemonde');
   
   // print out
   include('example1.php');
 ?>

Main Program (JSTL) :

public void doGet(HttpServletRequest request,
                  HttpServletResponse response)
                  throws ServletException, IOException {
   ...
   // set a data
   java.util.List member_list = new java.util.ArrayList();
   member_list.add("Oboro");
   member_list.add("Ominae");
   member_list.add("Jaquemonde");

   // print
   RequestDispatcher dispatcher = 
       request.getRequestDispatcher('example1.jsp');
   dispatcher.include(request, response);
   // or dispatcher.forward(request, response);
}

Calling or executing the output script, you might get a web page like this:

<table>
  <tr>
    <td>Oboro</td>
  </tr>
  <tr>
    <td>Ominae</td>
  </tr>
  <tr>
    <td>Jaquemonde</td>
  </tr>
</table>
(*3)
if you specify the command-line option '--charset=CHARSET', Kwartz will output <%@ page contentType="text/html; charset=CHARSET" %> when the language is JSTL.

Complex Example

The following is a little complex example which is borderd table.

Marking is done with id="mark:name" instead of id="name" in the following presentation data. You don't have to write remove: "id" in presentation logic file because Kwartz removes id="mark:name" automatically.

Presentation Data (example2.html):

<table>
 <tr bgcolor="#CCCCFF" id="mark:list">
  <td id="mark:name">foo</td>
  <td>
   <a href="mailto:foo@mail.com" id="mark:email">foo@mail.com</a>
  </td>
 </tr>
</table>

The following is the presentation logic file. In the presentation logic, you should detect whether odd or even line in the iteration.

Presentation Logic (example2.plogic):

// an element which is marked by 'id="mark:list"':
// * print value of a variable 'color' as bgcolor attribute value.
// * change value of a variable 'color' whether odd or even line.
#list {
  attr:  "bgcolor" color;
  plogic: {
    i = 0;
    foreach (member in member_list) {
      i += 1;
      color = i % 2 == 0 ? '#FFCCCC' : '#CCCCFF';
      @stag;
      @cont;
      @etag;
    }
  }
}

// an element which is marked by 'id="mark:name"':
// * print value of member['name'] as content of the element.
#name {
  value: member['name'];
}

// an element marked by 'id="mark:email"':
// * print value of member['emal'] as contentn of the element.
// * print "mailto:" and member['email'] as href attribute value.
//   (operator '.+' means string concatenation.)
#email {
  value: member['email'];
  attr:  "href" ("mailto:" .+ member['email']);
}

You will find that there is no HTML tag in the presentation logic and no logic in the presentation data. That is to say, Kwartz can separate presentation logic from presentation data.

Notice that you cannot write ctr++ because incremental operator (++) is not supported.

Compile:

### for eRuby
$ kwartz -l eruby  -p example2.plogic example2.html > example2.rhtml

### for PHP
$ kwartz -l php    -p example2.plogic example2.html > example2.php

### for JSTL 1.1
$ kwartz -l jstl11 -p example2.plogic example2.html > example2.jsp

### for JSTL 1.0
$ kwartz -l jstl10 -p example2.plogic example2.html > example2.jsp

Output Script:

### for eRuby
<table>
<% i = 0 %>
<% for member in member_list do %>
<%   i += 1 %>
<%   color = i % 2 == 0 ? "#FFCCCC" : "#CCCCFF" %>
 <tr bgcolor="<%= color %>">
  <td><%= member["name"] %></td>
  <td>
   <a href="<%= "mailto:" + member["email"] %>"><%= member["email"] %></a>
  </td>
 </tr>
<% end %>
</table>

### for PHP
<table>
<?php $i = 0; ?>
<?php foreach ($member_list as $member) { ?>
<?php   $i += 1; ?>
<?php   $color = $i % 2 == 0 ? "#FFCCCC" : "#CCCCFF"; ?>
 <tr bgcolor="<?php echo $color; ?>">
  <td><?php echo $member["name"]; ?></td>
  <td>
   <a href="<?php echo "mailto:" . $member["email"]; ?>"><?php echo $member["email"]; ?></a>
  </td>
 </tr>
<?php } ?>
</table>

### for JSTL
<table>
<c:set var="i" value="0"/>
<c:forEach var="member" items="${member_list}">
  <c:set var="i" value="${i + 1}"/>
  <c:set var="color" value="${i % 2 eq 0 ? '#FFCCCC' : '#CCCCFF'}"/>
 <tr bgcolor="<c:out value="${color}" escapeXml="false"/>">
  <td><c:out value="${member['name']}" escapeXml="false"/></td>
  <td>
   <a href="<c:out value="${fn:join('mailto:',member['email'])}" escapeXml="false"/>"><c:out value="${member['email']}" escapeXml="false"/></a>
  </td>
 </tr>
</c:forEach>
</table>

Other Examples of Presentation Logic

Kwartz enables you to write complex presentation logic natulally. This section shows some examples. See Presentation Pattern Catalog for details.

It is very important that tag/attribute names don't appear in presentation logic at all. This way, you don't need to change presentation logic files even if the tag/attribute names are changed in the presentation data.

Kwartz separates presentation logic from presentation data.



Directives

Presentation logic may be embedded into presentation data in Kwartz. You can choose to separate or not to sepearate presentation data and presentation logic.

To embed presentation logic into presentation data, use directives. Directive is a command to embed presentation logic into presentation data. In Kwartz, 'id' and 'kw:d' attributes are used to describe directives.

The following is an example to use directives.

Presentation Data(example3.html):

<table>
  <tr id="foreach:member=member_list">
    <td id="value:member['name']">foo</td>
    <td><a href="mailto:@{member['email']}@">
         @{member['email']}@</a></td>
  </tr>
  <tr id="dummy:d1">
    <td>bar</td>
    <td><a href="mailto:bar@mail.org">bar@mail.org</a></td>
  </tr>
  <tr id="dummy:d2">
    <td>baz</td>
    <td><a href="mailto:baz@mail.net">baz@mail.net</a></td>
  </tr>
</table>

Compile:

### for eRuby
$ kwartz -l eruby   example3.html > example3.rhtml

### for PHP
$ kwartz -l php     example3.html > example3.php

### for JSTL1.1
$ kwartz -l jstl11  example3.html > example3.jsp

Output script:

### for eRuby
<table>
<% for member in member_list do %>
  <tr>
    <td><%= member["name"] %></td>
    <td><a href="mailto:<%= member["email"] %>">
         <%= member["email"] %></a></td>
  </tr>
<% end %>
</table>

### for PHP
<table>
<?php foreach ($member_list as $member) { ?>
  <tr>
    <td><?php echo $member["name"]; ?></td>
    <td><a href="mailto:<?php echo $member["email"]; ?>">
         <?php echo $member["email"]; ?></a></td>
  </tr>
<?php } ?>
</table>

### for JSTL
<table>
<c:forEach var="member" items="${member_list}">
  <tr>
    <td><c:out value="${member['name']}" escapeXml="false"/></td>
    <td><a href="mailto:<c:out value="${member['email']}" escapeXml="false"/>">
         <c:out value="${member['email']}" escapeXml="false"/></a></td>
  </tr>
</c:forEach>
</table>

There are several directives such as conditional branching. See reference manual for details.

(*4)
This pattern is editable with a constant EMBED_PATTERN in configuration file(kwartz/config.rb).

Sanitizing

Kwartz supports Automatic Sanitizing and Partially Sanitizing.

Automatic Sanitizing

-e is the command-line optin to generate output script with sanitizing. CGI.escapeHTML() is used in eRuby for sanitizing, htmlspecialchars() in PHP, and <c:out/> tag without escapeXml="false" in JSTL.

Presentation Data (sanitize1.html):

<tr bgcolor="@{color}@">
  <td id="value:str">foo</td>
</tr>

Compile:

### for eRuby
$ kwartz -e -l eruby  sanitize1.html > sanitize1.rhtml
	
### for ERB
$ kwartz -e -l erb    sanitize1.html > sanitize1.rhtml
	
### for PHP
$ kwartz -e -l php    sanitize1.html > sanitize1.php
	
### for JSTL 1.1
$ kwartz -e -l jstl11 sanitize1.html > sanitize1.jsp

Output Scirpt:

### for eRuby
<tr bgcolor="<%= CGI::escapeHTML((color).to_s) %>">
  <td><%= CGI::escapeHTML((str).to_s) %></td>
</tr>

### for ERB
<tr bgcolor="<%=h(color)%>">
  <td><%=h(str)%></td>
</tr>

### for PHP
<tr bgcolor="<?php echo htmlspecialchars($color); ?>">
  <td><?php echo htmlspecialchars($str); ?></td>
</tr>

### for JSTL
<tr bgcolor="<c:out value="${color}"/>">
  <td><c:out value="${str}"/></td>
</tr>

Constant string and constant number are not sanitized. Conditional expression such as 'flag ? " checked" : ""' is not sanitized because the result is constant string whether the value of 'flag' is true or false.


Partially Sanitizing

The function E(expr) always sanitizes expression expr even when '-e' is not specified. The function X(expr) doesn't sanitize expression expr even when '-e' is specified.

Presentaion Data:

<table>
 <tr bgcolor="@{X(color)}@">
   <td id="value:E(str)">foo</td>
 </tr>
</table>

Compile:

### for eRuby
$ kwartz -e -l eruby  sanitize1.html > sanitize1.rhtml
	
### for ERB
$ kwartz -e -l erb    sanitize1.html > sanitize1.rhtml
	
### for PHP
$ kwartz -e -l php    sanitize1.html > sanitize1.php
	
### for JSTL 1.1
$ kwartz -e -l jstl11 sanitize1.html > sanitize1.jsp

Output Script:

### for eRuby
<table>
 <tr bgcolor="<%= color %>">
   <td><%= CGI::escapeHTML((str).to_s) %></td>
 </tr>
</table>

### for ERB
<table>
 <tr bgcolor="<%= color %>">
   <td><%=h(str)%></td>
 </tr>
</table>

### for PHP
<table>
 <tr bgcolor="<?php echo $color; ?>">
   <td><?php echo htmlspecialchars($str); ?></td>
 </tr>
</table>

### for JSTL
<table>
 <tr bgcolor="<c:out value="${color}" escapeXml="false"/>">
   <td><c:out value="${str}"/></td>
 </tr>
</table>

Directives id="Value:expr" and id="Attr:name=expr" always sanitize expression expr even when the sanitizing command-line option is not specified. Directives id="VALUE:expr:" and id="ATTR:name=expr" doesn't sanitize expression expr even when the sanitizing command-line option is specified. These are equivalent to id="value:E(expr)", id="attr:name=E(expr)", id="value:X(expr)", and id="attr:name=X(expr)".


Configuration for sanitizing

File 'kwartz/config.rb' is a configuration file for Kwartz-ruby. If you want to sanitize in default, change value of constant ESCAPE to true in the configuration file.



Other Topics

Restrictions

Kwartz parses presentation data file by regular expression pattern matching. It means that Kwartz doesn't use HTML parser nor XML parser for parsing presentation data. This approach enables Kwartz to handle any type of text file, and also brings the following restrictions to Kwartz.


Analyzing Global and Local Variables

Kwartz have a function to analyze presentation data/logic files and inspect variables.

In Kwartz, variables are called Global variables if they are set in the main program and are passed to output script. Variables are called Local variables if they are used only in the template. Kwartz have a function to detect whether variables are global or local and report the result.

Assume the following presentation data and presentation logic:

Presentation Data (analyze.html):

<span kw:d="value:title">Analyzer Example</span>
<dl id="mark:items">
 <dt kw:d="value:ctr"></dt>
 <dd kw:d="value:item">Foo</dd>
</dl>

Presentation Logic (analyze.plogic):

#items {
  plogic: {
    @stag;
    ctr = 0;
    foreach (item in list) {
      ctr += 1;
      @cont;
    }
    @etag;
  }
}

In this case, variables ctr and item are Local Variables because they are used only in the template. Variables title and list are Global Variables because they are set in the main program and passed to output script.

Invoking kwartz with the command-line option -a analyze reports global/local variables.

Analyzing Example:

$ kwartz -a analyze -p analyze.plogic analyze.html
Global: title list
Local:  ctr item

Rename Local Variable Names

In Ruby or PHP, if local variables in template have the same name of variables in main program, assignment into local variables effects the control of main program. To address this problem, Kwartz have a feature to rename local variable names in template automatically.

The command-line option '--rename' renames all of local variable names in template automatically.

The following is the example using 'analyze.html' and 'analyze.plogic' shown in the previous section.

Compile:

$ kwartz -l eruby  -p analyze.plogic --rename analyze.html
$ kwartz -l php    -p analyze.plogic --rename analyze.html
$ kwartz -l jstl11 -p analyze.plogic --rename analyze.html

Output Script:

### for eRuby
<%= title %>
<dl>
<% _ctr = 0 %>
<% for _item in list do %>
<%   _ctr += 1 %>
 <dt><%= _ctr %></dt>
 <dd><%= _item %></dd>
<% end %>
</dl>

### for PHP
<?php echo $title; ?>
<dl>
<?php $_ctr = 0; ?>
<?php foreach ($list as $_item) { ?>
<?php   $_ctr += 1; ?>
 <dt><?php echo $_ctr; ?></dt>
 <dd><?php echo $_item; ?></dd>
<?php } ?>
</dl>

### for JSTL
<c:out value="${title}" escapeXml="false"/>
<dl>
<c:set var="_ctr" value="0"/>
<c:forEach var="_item" items="${list}">
  <c:set var="_ctr" value="${_ctr + 1}"/>
 <dt><c:out value="${_ctr}" escapeXml="false"/></dt>
 <dd><c:out value="${_item}" escapeXml="false"/></dd>
</c:forEach>
</dl>

Constant RENAME in configuration file specifies whether rename local variable names in default.


Span Tag Deletion

The span tags which contains only directives are regarded as dummy tags and deleted automatically.

Presentation Data:

<h1><span id="mark:title">title</span></h1>

Hello <span id="value:user">World</span>!

Presentation Logic:

#title {
  value: title;
}

Output Script(for eRuby):

<h1><%= title %></h1>

Hello <%= user %>!

The span tags are not removed when they have other attributes.

Presentation Data:

<h1><span id="mark:title" class="title">title</span></h1>

Hello <span kw:d="value:user" style="color:black">World</span>!

Output Script(for eRuby):

<h1><span class="title"><%= title %></span></h1>

Hello <span style="color:black"><%= user %></span>!

Appending Expression to Start Tag

The following is an example to print only attribute variable such as <input type="..." checked>.

Presentation Data:

<input type="checkbox" name="foo" value="Y" id="foo" />

Presentation Logic:

#foo {
  append: flag ? ' checked' : '';
}

Output Script:

### for eRuby
<input type="checkbox" name="foo" value="Y" id="foo"<%= flag ? " checked" : "" %> />

### for PHP
<input type="checkbox" name="foo" value="Y" id="foo"<?php echo $flag ? " checked" : ""; ?> />

### for JSTL 1.1
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<input type="checkbox" name="foo" value="Y" id="foo"<c:out value="${flag ? ' checked' : ''}" escapeXml="false"/> />

### for JSTL 1.0
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<c:choose><c:when test="${flag}">
<input type="checkbox" name="foo" value="Y" id="foo" checked />
</c:when><c:otherwise>
<input type="checkbox" name="foo" value="Y" id="foo" />
</c:otherwise></c:choose>

A directive append(expr) appends the value of the expression expr in the element tag.

Presentation Data:

<input type="checkbox" name="foo" value="Y" id="append:flag?' checked':''" />

There are useful functions to print checked="checked" or selected="selected" easily. C(expr), S(expr), D(expr) prints checked="checked", selected="selected", disabled="disabled" respectively when the expression expr is true.

Presentation Data:

<input type="checkbox" name="foo" value="Y" id="foo" />

Presentation Logic:

#foo {
  append: C(foo == 100);
}

Output Script:

### for eRuby
<input type="checkbox" name="foo" value="Y" id="foo"<%= foo == 100 ? " checked=\"checked\"" : "" %> />

### for PHP
<input type="checkbox" name="foo" value="Y" id="foo"<?php echo $foo == 100 ? " checked=\"checked\"" : ""; ?> />

### for JSTL 1.1
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<input type="checkbox" name="foo" value="Y" id="foo"<c:out value="${foo eq 100 ? ' checked="checked"' : ''}" escapeXml="false"/> />

### for JSTL 1.0
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<c:choose><c:when test="${foo eq 100}">
<input type="checkbox" name="foo" value="Y" id="foo" checked="checked" />
</c:when><c:otherwise>
<input type="checkbox" name="foo" value="Y" id="foo" />
</c:otherwise></c:choose>

Describe Presentation Logic in Target Language

Lines staring with '<%' or '<?' are printed as it is. It enables you to write presentation logics in Ruby, PHP, and Java.

Presentation Logic:

#list {
  plogic: {
    @stag;
    <% ENV.each { |name, value| %>
      @cont;
    <% } %>
    @etag;
  }
}

Lines starting with '<%' or '<?' are called 'raw-code'. Raw-code is statement and not expression, therefore you can write raw-code in 'plogic:' part but not in 'value:' part and 'attr:' part.