Kwartz User's Guide
last update: $Date: 2004/05/09 14:25:20 $
Preface
This is a user's guide of Kwartz(*1), a template system for web-designer and web-programmer.
Sorry for my poor English. You may need imagination and patience to read this document.
- (*1)
- Development of Kwartz is subsidized by Exploratory Software Project of IPA (Information-Technology Promotion Agency Japan).
Table of Contents
Table of Contents:- Preface
- About Kwartz
- How Kwartz works
- Directive
- What is directive?
- Marking
- Print value of expression
- Print value of expression 2
- Attribute value
- Assign
- Conditional branch
- Iteration(foreach)
- Iteration(list)
- Iteration with count
- Iteration with toggle switch
- Iteration (while)
- Dummy data
- Replacement of Element
- Include presentation data file
- Load presentation logic file
- Id and kd attributes
- Notes
- PL -- Presentation Language
- Practical Examples
- Tips
- Get output as string
- Auto-compile with comaring timestamp
- checked, selected, disabled
- Enable eRuby code in presentation logic
- Environment variable KWARTZ_OPTIONS
- Compile template as method of Ruby/PHP
- Use W3C Markup Validation Service
- Avoid same variable name as keywords
- Use name 'param' as a variable for HTTP request parameters
- Allow only logical expression as conditional expresion
- Appendix
Changelog
- 2004-05-09
-
- "..." is also string literal, in addition to '...'.
- auto-detect "\n" or "\r\n".
- command line option "--delete_idattr" which delete id attribute from presentation data.
- 2004-04-23
-
- new language ruby2 and php2
- updated about 'mkmethod'
- 2004-04-04
-
- utility script 'mkmethod'
- tips to get result as string
- fixed 'import()' => 'include()' in PHP
- 2004-04-01
-
- 'include' directive.
- 'load' directive and :load() statement.
- function E() and X()
- 2004-03-21
-
- delete only <span> tag and leave <div> tag
- 2004-03-20
-
- special macro BEGIN and END
- Ruby sanitizing
- 2004-03-19
-
- new command option '--enable_eruby=true'
- new keyword 'empty'
- new action 'analyze'
- new directive notation 'id="foreach:item:list"'
- 2004-03-13
-
- use id attribute instead of kd attribute.
- add description about new features
(
id="replace:name"
and:value(name=expr)
) - add new samples (navilink, breadcrumbs, error messages, calendar)
- 2004-03-08
-
- subsection 'Compare with null explicitly' added.
- some eratta fixed
- 2004-02-27
-
- 'Notes' of directives and presentaion language updated
- description about sanitizing updated
- 2004-02-24
-
- some eratta fixed
- 2004-02-12
-
- public release
About Kwartz
What's Kwartz?
Kwartz(*2) is a template system, implemented in Ruby. It has following features:
- Separate presentation logic from presentation data.
-
Using any template system such as Velocity, XMLC, Amrita, etc, you can separate HTML desgin 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 don't need to mix presentation logic into HTML file nor main program.
- Very fast
-
Kwartz creates a script for the output 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 tree or so, Kwartz works very fast, and light-weight.
- Multi programing language
-
Kwartz can create outputs script for Ruby, PHP, JSP and eRuby from a template file, because Kwartz uses an intermediate language internally. You don't have to change the presentation layer at all, even if you changed programming language. Kwartz supports the following language: Ruby, PHP, JSP, eRuby, ERB, Velocity
- Doesn't break HTML design at all
-
You must use directives like '#foreach(...)' in Jakarta Velocity or '[FOREACH ...]' in Template-Toolkit. These directives break HTML design of template. Kwartz doesn't break HTML design at all because Kwartz uses tag's attributes for marking in HTML template.
- Can handle any text file
-
Kwartz uses an original template parser, not using XML parser, so as to enable to handle any text file (HTML, PostScript, RTF, and so on). It means that Kwartz can handle invalid-formed XML files, as well as well-formed XML files. It is an advantage of Kwartz against Enhydra XMLC or amrita, which handle only XML/HML files.
- Auto-Sanitizing Supported
-
Kwartz can do sanitizing automatically. You don't need to write '
CGI.escapeHTML(var)
' or 'htmlspecialchars($var)
'. You are free to make sanitizing on/off, and can specify a certain part of template to sanitize or unsanitize.
- (*2)
- Pronounced same as 'Quartz'.
Simple Example
In Kwartz, you should define a template as presentation data and presentation logic. They are described in separate files.
This is a sample of presentation data file.
#{var}#
means that "Output the value of variable 'var'."id="xxx"
means that "I'll operate this element in presentation logic." (it is called 'marking').
Presentation data file(example.html):
<table> <tr id="xxx"> <td>#{var}#</td> </tr> </table>
And the following is a sample of presentation logic. In presentation logic, you can operate elements which are marked in presentation data.
:elem(xxx)
represents a element marked with name 'xxx' (= '<tr>...</tr>').@stag
represents a start tag (= '<tr>').@cont
represents a content(= '<td>#{user}#</td>').@etag
represents an end tag(= '</tr>').:foreach(var=list) ... :end
represents a iteration. In fact, this presentation logic re-define the marked element as to iterate from start tag to end tag.
Presentation logic file(example.plogic):
## Re-define an element to iterate start tag, content and end tag. :elem(xxx) ## element :foreach(var=list) @stag ## start tag @cont ## content @etag ## end tag :end :end
Kwartz creates output scripts automatically for Ruby, PHP, JSP, eRuby, ERB and Velocity from the above two files. This action is called 'Compile'. To compile, enter the following command:
### for Ruby $ kwartz -l ruby -p example.plogic example.html > example.rb or $ kwartz -l ruby2 -p example.plogic example.html > example.rb2 ### for PHP $ kwartz -l php -p example.plogic example.html > example.php or $ kwartz -l php2 -p example.plogic example.html > example.php2 ### for JSP $ kwartz -l jsp -p example.plogic example.html > example.jsp ### for eRuby $ kwartz -l eruby -p example.plogic example.html > example.rthml ### for ERB $ kwartz -l erb -p example.plogic example.html > example.erb ### for Velocity $ kwartz -l velocity -p example.plogic example.html > example.vm
The following is the output script for each language.
for Ruby (example.rb) :
print "<table>\n" for var in list do print " <tr id=\"xxx\">\n" print " <td>", var, "</td>\n" print " </tr>\n" end print "</table>\n"
for Ruby (example.rb2) -- get output as string:
_s << "<table>\n" for var in list do _s << " <tr id=\"xxx\">\n" _s << " <td>" << (var).to_s << "</td>\n" _s << " </tr>\n" end _s << "</table>\n"
for PHP (example.php) :
<table> <?php foreach ($list as $var) { ?> <tr id="xxx"> <td><?php echo $var; ?></td> </tr> <?php } ?> </table>
for PHP (example.php2) -- get output as string:
<?php ob_start(); ?> <table> <?php foreach ($list as $var) { ?> <tr id="xxx"> <td><?php echo $var; ?></td> </tr> <?php } ?> </table> <?php $_s = ob_get_contents(); ob_end_clean(); ?>
for JSP(*3)(example.jsp) :
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %> <table> <c:forEach var="var" items="${list}"> <tr id="xxx"> <td><c:out value="${var}" escapeXml="false"/></td> </tr> </c:forEach> </table>
for eRuby(example.rhtml) :
<table> <% for var in list do %> <tr id="xxx"> <td><%= var %></td> </tr> <% end %></table>
for ERB(example.erb) :
<table> % for var in list do <tr id="xxx"> <td><%= var %></td> </tr> % end </table>
for Velocity(example.vm) :
<table> #foreach ($var in $list) <tr id="xxx"> <td>$!{var}</td> </tr> #end </table>
Using command-line option -s when compiling, kwartz will output sanitized scripts (except Velocity).
For sanitizing, htmlspecialchars()
is used in PHP, CGI.escapeHTML()
in Ruby and eRuby, html_escape()
in ERB, <c:out/>
without escapeXml="false"
in JSP.
Then execute or import these output script in your main-program, like this:
Main program(ruby) :
require 'cgi' # for sanitizing s = File.open('example.rb') { |f| f.read } s.untaint # for CGI program eval s
Main program(ruby2) :
require 'cgi' # for sanitizing s = File.open('example.rb2') { |f| f.read } s.untaint # for CGI program _s = '' eval s print _s
Main program(php) :
<?php include('example.php'); ?>
Main program(php2) :
<?php include('example.php2'); echo $_s; ?>
Main program (jsp) :
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ... RequestDispatcher dispatcher = request.getRequestDispatcher('example.jsp'); dispatcher.include(request, response); // or dispatcher.forward(request, response); }
Main program(eruby) :
require 'eruby' require 'cgi' # for sanitizing ERuby::import('example.rhtml')
Main program(erb) :
require 'erb' include ERB::Util # for sanitizing s = File.open('example.erb') { |f| f.read } s.untaint # for CGI program erb = ERB.new(s, $SAFE, '%') erb.run(binding()) # or print erb.result(binding())
Main program(velocity) :
Velocity.init(); VelocityContext context = new VelocityContext(); Template template = Velocity.getTemplate("example.vm"); OutputStreamWriter writer = new OutputStreamWriter(System.out); template.merge(context, writer); writer.flush();
Calling or executing output scripts, you can get web page like this:
<table> <tr id="xxx"> <td>apple</td> </tr> <tr id="xxx"> <td>orange</td> </tr> <tr id="xxx"> <td>banana</td> </tr> </table>
You may mind that id attributes are appeared in each <tr></tr>.
You can delete it from web page if you write it as id="mark:xxx"
(which is equivalent to id="xxx"
but it removes id attribute itself),
or add command line option --delete_idattr=true
.
In addition, you can 'embed' presentation logic into presentation data in Kwartz. You can select 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' or 'kd' attributes are used to describe directives.
The following is a sample of 'embedding' presentation logic into presentation data. This sample is compiled into the same output as the script files in above. .
<table> <tr id="foreach:var=list"> <td id="value:var">foo</td> </tr> </table>
- (*3)
- if you specify a command line option '--charset=CHARSET', kwartz will output
<%@ page contentType="text/html; charset=CHARSET" %>
when JSP.
How Kwartz works
Presentation data
Kwartz separates presentation layer into 'presentation data' and 'presentation logic' with the following approach.
- In presentation data, mark a part which you will operate.
You should use
id="name"
to mark. - In presentation logic, operate a part which is marked.
It is called 'marking' to mark a region which you want to change or replace in the presentation data.
You can do 'marking' with attribute 'id' or 'kd'(*4), like id="name"
.
Example of marking:
<table> <tr id="list"> <td>#{item}#</td> </tr> </table>
Kwartz recognizes only marked elements, and regard unmarked elements as a normal plain texts. It means that Kwartz can handle any type of text, including not well-formed XML file.
Example of 'not well-formed XML file':
<span id="test"> aaa <bbb> ccc </ddd> </span>
- (*4)
- The attribute name is derived from 'Kwartz Directive'. You can change the name of attribute with command-line option --attr_name.
Intermediate code
Kwartz 'converts' presentation data into intermediate code.
When converting a presentation data into intermeidate code, Kwartz creates four macros for each marked elements.
- macro 'sttag_xxx' represents a start-tag.
- macro 'cont_xxx' represents contents.
- macro 'etag_xxx' represents a end-tag.
- macro 'elem_xxx' represents an element, which is constructed with start-tag, contents and end-tag.
Here is a sample of presentation data:
<table> <tr id="list"> <td>#{item}#</td> </tr> </table>
Kwartz will convert the presentation data into the following intermediate code. You'll find that four macros are defined and presentation data is described with these macros.
:macro(stag_list) ## start tag :print(" <tr id=\"list\">\n") :end :macro(cont_list) ## content :print(" <td>", item, "</td>\n") :end :macro(etag_list) ## end tag :print(" </tr>\n") :end :macro(elem_list) ## element :expand(stag_list) ## expand a macro :expand(cont_list) ## expand a macro :expand(etag_list) ## expand a macro :end :print("<table>\n") ## presentation data :expand(elem_list) ## expand macro :print("</table>\n")
'id="list"
' will be left in intermediate code.
If you want to delete it, use 'id="mark:list"
' instead, or add command line option --delete_idattr=true
Intermediate code is described in an itermediate language. The language is named 'PL'(Presentation Language).
Presentation Logic
Presentation logic is:
- Described in PL(Presentation Language), which is used as intermediate language.
- Described as a macro to overwrite the macro-definition which is automatically generated.
The following is a sample of presentation logic. This sample will overwrite macro 'elem_list' to iterate from start tag to end tag.
:macro(elem_list) :foreach(item=itemlist) ## iteration with foreach statement :expand(stag_item) ## start tag :expand(cont_item) ## content :expand(etag_item) ## end tag :end :end
And you can write the above presentation logic as the following. Kwartz parser will treat it as the same code as the above.
:elem(list) ## same as :macro(elem_item) :foreach(item=itemlist) @stag ## same as :expand(stag_item) @cont ## same as :expand(cont_item) @etag ## same as :eppand(etag_item) :end :end
Output script
Kwartz merges intermediate code and presentation logic, then expands macros and transforms it into an output script. This action is called 'translation'.
This is a sample of translation.
:print("<table>\n") :expand(elem_list) :print("</table>\n") | | expand macros V :print("<table>\n") :foreach(item=itemlist) :expand(stag_list) :expand(cont_list) :expand(etag_list) :end :print("</table>\n") | | expand macros V :print("<table>\n") :foreach(item=itemlist) :print(" <tr id=\"list\">\n") :print(" <td>", item, "</td>\n") :print(" </tr>\n") :end :print("</table>\n") | | output script for each langague V ### for Ruby print "<table>\n" for item in itemlist do print " <tr id=\"list\">\n" print " <td>", item, "</td>\n" print " </tr>\n" end print "</table>\n" ### for PHP <table> <?php foreach($itemlist as $item) { ?> <tr id="list"> <td><?php echo $item; ?></td> </tr> <?php } ?> </table> ### for JSP <table> <c:forEach var="item" items="${itemlist}"> <tr id="list"> <td><c:out value="${item}"/></td> </tr> </c:forEach> </table> ### for eRuby <table> <% for item in itemlist do %> <tr id="list"> <td><%= item %></td> </tr> <% end %></table> ### for ERB <table> % for item in itemlist do <tr id="list"> <td><%= item %></td> </tr> % end </table> ### for Velocity <table> #foreach ($item in $itemlist) <tr id="list"> <td>${item}</td> </tr> #end </table>
The remarking task is to call or execute these output scripts in main program.
These are the all what Kwartz does.
How to call or execute output script
The way to call or execute output script is different for each language. See other documents for detail.
- in Ruby:
-
Read and eval output file. You have to require cgi.rb when sanitiginz.
require 'cgi' # for sanitizing str = File.open('example.rb') { |f| f.read } str.untaint # for CGI program eval str
Or if you specified command line option `
-l ruby2
' :require 'cgi' # for sanitizing str = File.open('example.rb') { |f| f.read } str.untaint # for CGI program _s = '' eval str print _s
- in PHP:
-
Use include() function.
<?php include('example.phtml'); ?>
Or if you specified command line option `
-l php2
' :<?php include('example.phtml'); echo $_s; ?>
- in JSP (Servlet):
-
Use RequestDispatcher#forward() or RequestDispatcher#include().
RequestDispatcher dispatcher = request.getRequestDispatcher("example.jsp"); dispatcher.forward(request, response);
- in eRuby:
-
Use ERuby::import() when using eruby. You have to require cgi.rb when sanitiginz.
require 'eruby' require 'cgi' # for sanitizing ERuby::import('example.rhtml')
- in ERB:
-
Use ERB#run() when using ERB. You should specify '%' as trim parameter when creating ERB object. And you have to impoort
ERB::Util
when sanitizing.require 'erb' include ERB::Util # for sanitizing str = File.open('example.erb') { |f| f.read } str.untaint erb = ERB.new(str, $SAFE, '%') erb.run(binding()) # or print erb.result(binding())
- in Velocity:
-
Use org.apache.velocity.context.Context, org.apache.velocity.Template, and so on. See Velocity's manual for details.
Velocity.init(); VelocityContext context = new VelocityContext(); Template template = Velocity.getTemplate("example.vm"); OutputStreamWriter writer = new OutputStreamWriter(System.out); template.merge(context, writer); writer.flush();
Other examples of presentation logic
An example described in the previous section is to iterate from start tag to end tag. This section shows some other examples.
- You can iterate only contents. This is useful for <dl></dl>.
:macro(elem_list) :expand(stag_list) :foreach(item=itemlist) :expand(cont_list) :end :expand(etag_list) :end
or:elem(list) @stag :foreach(item=itemlist) @cont :end @etag :end
- You can print a value of variable or expression instead of a static content string.
:macro(elem_list) :expand(stag_list) :print(item['key']) :expand(etag_list) :end
or:elem(list) @stag :print(item['key']) @etag :end
- You can print a value of variable or expression instead of element.
:macro(elem_list) :print(item['key']) :end
or:elem(list) :print(item['key']) :end
- You can replace an element with other macro.
:macro(elem_list) :expand(other_macro) :end :macro(other_macro) :print('...') :print('...') :end
or:elem(list) :expand(other_macro) :end :macro(other_macro) :print('...') :print('...') :end
- You can delete start tag and end tag when you comment out them.
:macro(elem_list) ## :expand(stag_list) :expand(cont_list) ## :expand(etag_list) :end
or:elem(list) ## @stag @cont ## @etag :end
- You can delete an element. It is useful for delete dummy data.
:macro(elem_list) ## :expand(stag_list) ## :expand(cont_list) ## :expand(etag_list) :end
or:elem(item) ## @stag ## @cont ## @etag :end
- You can include a complex presentation logic like this:
:macro(elem_list) :set(ctr = 0) :foreach(item=itemlist) :set(ctr += 1) :set(color = ctr%2==0 ? 'red' : 'blue') :expand(stag_item) :expand(cont_item) :expand(etag_item) :end :end
or:elem(list) :set(ctr = 0) :foreach(item=itemlist) :set(ctr += 1) :set(color = ctr%2==0 ? 'red' : 'blue') @stag @cont @etag :end :end
It is very important that tag/attribute names don't appear in presentation logic at all. In other words, you don't need to change presentaion logic files even if the tag/attribute name is changed in the presesntation data.
It means that Kwartz separates presentation logic from presentation data.
Conclution
This is a chart of a process which represents how Kwartz works. In this chart, with '*' represents a file which is created automatically, Without '*' represents a file which have to be created manually by a developer.
Presentation Data Presentation Logic (HTML) (intermediate language) | | | convert | | | V | Intermediate Code* | (intermediate language) | | | | | +-------------+--------------+ | translate | V call/import Output Program* <============ Main Program (Ruby/PHP/JSP/eRuby) (Ruby/PHP/JSP/eRuby) | | output | V Web Page* (HTML)
Following files are created by a developer manually:
- presentation data file (HTML file)
- presentation logic file
- main program
Kwartz creates automatically:
- intermediate code
- output script
- web page
Term:
- convert
- To transform presentation data into intermediate code
- translate
- To transform intermeidate code and presentation logic into output script.
- compile
- 'Convert' and 'translate', that is, transform presentation data and presenstaion logics into an output script.
Directive
What is directive?
Kwartz allows presentation logics to embedded in presentation data. Commands for that purpose is called 'directive'.
'Directive' is a set of commands to embed presentation logics into presentation data.
Marking (id="name"
or id="mark:name"
) is also directive.
There are several directives for iteration, conditional branch, and so on.
Kwartz uses 'id' or/and 'kd' attribute to embed directives in presentation data ('kd' means 'Kwartz Directive'). Usually id attribute is used, but if you want to use id attribute for other purpose you can use kd attribute instead of id attribute. And you can use both id and kd attribute in a tag. See 'Id and kd attributes' for detail about id and kd attribute.
You may have a question: It is the most important feature of Kwartz that Kwartz separates presentation logics from presentation data. Why does Kwartz allow to embed presentation logics into presentation data?
The answer is 'to make it possible for developers to choose either approaches.' In other words, to increase option of development style. Kwartz doesn't force you to use one approach. Separate presentation logic and presentation data if you like to separate them, or mix them up if you like.
You may wonder that Kwartz becomes a ordinary template system if presentation logic is embedded into presentation data. But Kwartz has the following merits compared to other template system.
- Kwartz doesn't break HTML design. Compare to Jakarta Velocity or Template-Toolkit.
- Kwartz doesn't need comprex programming. Compare to Enhydra XMLC, which needs complex DOM programming.
- Kwartz works very fast and light-weight. Compared to amrita.
- Kwartz can handle any type of text. Compare to Enhydra XMLC or amrita.
- Kwartz can be used in several language. There is no other template system like this!.
Marking
Directive id="name"
or id="mark:name"
marks an element.
'Marking' is to mark an element with a name name
.
The marked element is described as a macro in intermediate code.
Presentation Data:
<li id="item">foo</li>
Intermediate Code:
:macro(stag_item) :print("<li id=\"item\">") :end :macro(cont_item) :print("foo") :end :macro(etag_item) :print("</li>\n") :end :macro(elem_item) :expand(stag_item) :expand(cont_item) :expand(etag_item) :end :expand(elem_item)
Output Program:
### Ruby print "<li id=\"item\">" print "foo" print "</li>\n" ### PHP <li id="item">foo</li> ### JSP + JSTL <li id="item">foo</li> ### eRuby <li id="item">foo</li> ### ERB <li id="item">foo</li> ### Velocity <li id="item">foo</li>
The difference between 'id="name"
' and 'id="mark:name"
' is that the former is leaved but the latter is removed when compiling into intermediate code.
Presentation Data:
<li id="mark:item">foo</li>
Intermediate Code:
:macro(stag_item) :print("<li>") :end :macro(cont_item) :print("foo") :end :macro(etag_item) :print("</li>\n") :end :macro(elem_item) :expand(stag_item) :expand(cont_item) :expand(etag_item) :end :expand(elem_item)
Print value of expression
#{/expression/}#
is a directive which prints the value of expression.
It is equivarent to 'print expression' in Ruby, or '<?php echo expression; ?>' in PHP.
Presentation Data:
Hello #{user}#!
Intermediate Code:
:print("Hello ", user, "!\n")
Output Program:
### Ruby print "Hello ", user, "!\n" ### PHP Hello <?php echo $user; ?>! ### JSP + JSTL Hello <c:out value="${user}" escapeXml="false"/>! ### eRuby Hello <%= user %>! ### ERB Hello <%= user %>! ### Velocity Hello $!{user}!
Expression is sanitized if you specified command option -s
(or --escape=true
) when compiling template.
Notice that only expressions except constant string and constant number are sanitized.
Output Program (with command option -s
) :
### Ruby print "Hello ", CGI.escapeHTML((user).to_s), "!\n" ### PHP Hello <?php echo htmlspecialchars($user); ?>! ### JSP + JSTL Hello <c:out value="${user}"/>! ### eRuby Hello <%= CGI.escapeHTML((user).to_s) %>! ### ERB Hello <%= html_escape(user) %>! ### Velocity Hello $!{user}!
Using function E()
or X()
, you can specify sanitizing on/off for each expressions.
E(expr)
means that expression expr is sanitized and X(expr)
means that expr is not sanitized.
These are not effected by command option -s
(or --escape=true
).
Presentation Data:
With sanitizing: #{E(expr)}#! Without sanitizing: #{X(expr)}#!
Intermediate Code:
:print("With sanitizing: ", E(expr), "\n") :print("Without sanitizing: ", X(expr), "\n")
Output Program:
### Ruby print "With sanitizing: ", CGI.escapeHTML((expr).to_s), "!\n" print "Without sanitizing: ", expr, "!\n" ### PHP With sanitizing: <?php echo htmlspecialchars($expr); ?>! Without sanitizing: <?php echo $expr; ?>! ### JSP + JSTL With sanitizing: <c:out value="${expr}"/>! Without sanitizing: <c:out value="${expr}" escapeXml="false"/>! ### eRuby With sanitizing: <%= CGI.escapeHTML((expr).to_s) %>! Without sanitizing: <%= expr %>! ### ERB With sanitizing: <%= html_escape(expr) %>! Without sanitizing: <%= expr %>! ### Velocity With sanitizing: $!{expr}! Without sanitizing: $!{expr}!
Print value of expression 2
id="value:expression"
is a directive to print the value of expression instead of the content.
It is more suitable for HTML design than #{expression}#
, because it allows to use dummy data.
Presentation Data:
<li id="value:hash['name']">foo</li>
Intermediate Code:
:print("<li>", hash['name'], "</li>\n")
Output Program:
### Ruby print "<li>", hash["name"], "</li>\n" ### PHP <li><?php echo $hash['name']; ?></li> ### JSP + JSTL <li><c:out value="${hash['name']}" escapeXml="false"/></li> ### eRuby <li><%= hash['name'] %></li> ### ERB <li><%= hash['name'] %></li> ### Velocity <li>$!{hash.get('name')}</li>
Using id="Value:expr
, you can specify to sanitize expression.
And using id="VALUE:expr
, you can specify not to sanitize expression.
These are equal to id="value:E(expr)
or id="value:X(expr)
.
Attribute value
id="attr:name=value"
(or id="attr:name:value"
) is a directive to set an attribute value.
It overwrites existing value if attribute name is setted.
In the following example, attribute 'class' has a dummy value 'odd', and the actual value is derived from a variable 'klass'.
Presentation Data:
<tr class="odd" id="attr:class=klass"> <td>foo</td> </tr>
Intermediate Code:
:print("<tr class=\"", klass, "\">\n") :print(" <td>foo</td>\n") :print("</tr>\n")
Output Program:
### Ruby print "<tr class=\"", klass, "\">\n" print " <td>foo</td>\n" print "</tr>\n" ### PHP <tr class="<?php echo $klass; ?>"> <td>foo</td> </tr> ### JSP + JSTL <tr class="<c:out value="${klass}" escapeXml="false"/>"> <td>foo</td> </tr> ### eRuby <tr class="<%= klass %>"> <td>foo</td> </tr> ### ERB <tr class="<%= klass %>"> <td>foo</td> </tr> ### Velocity <tr class="$!{klass}"> <td>foo</td> </tr>
Using id="Attr:name=value
, you can specify to sanitize value.
And using id="ATTR:name=value
, you can specify not to sanitize value.
These are equal to id="attr:name=E(value)
or attr:name=X(value)
.
Separating with ';
', you can list several attr:name=value
directives in one id attribute,
or list with other directive.
Presentation Data:
<font id="if:message;attr:class=klass;attr:bgcolor=color"> #{message}# </font>
Intermediate Code:
:if(message) :print("<font class=\"", klass, "\" bgcolor=\"", color, "\">\n") :print(" ", message, "\n") :print("</font>\n") :end
Assign
id="set:var=value
(or id="set:var:value
) is directive for assigning value into a variable.
Not only '=
' but also '+=
', '-=
', '*=
', '/=
', '.+=
' are available.
('.+=
' means string concatination.)
Presentation Data:
<dt id="set:var=value">foo</dt> <dd id="set:count+=1">123</dd>
Intermediate Code:
:set(var=value) :print("<dt>") :print("foo") :print("</dt>\n") :set(count+=1) :print("<dd>") :print("123") :print("</dd>\n")
Output Program:
### Ruby var = value print "<dt>" print "foo" print "</dt>\n" count += 1 print "<dd>" print "123" print "</dd>\n" ### PHP <?php $var = $value; ?> <dt>foo</dt> <?php $count += 1; ?> <dd>123</dd> ### JSP + JSTL <c:set var="var" value="${value}"/> <dt>foo</dt> <c:set var="count" value="${count + 1}"/> <dd>123</dd> ### eRuby <% var = value %><dt>foo</dt> <% count += 1 %><dd>123</dd> ### ERB % var = value <dt>foo</dt> % count += 1 <dd>123</dd> ### Velocity #set ($var = $value) <dt>foo</dt> #set ($count = $count + 1) <dd>123</dd>
Conditional branch
id="if:expression"
is a directive for conditional branch.
It enables only if-statement, cannot represent 'else' or 'else if'.
If you want to use 'else' or 'else if', you should describe then in presentation logic file.
Presentation Data:
<font color="red" id="if:flag_error"> Error!! </font>
Intermediate Code:
:if(flag_error) :print("<font color=\"red\">\n") :print(" Error!!\n") :print("</font>\n") :end
Output Program:
### Ruby if flag_error then print "<font color=\"red\">\n" print " Error!!\n" print "</font>\n" end ### PHP <?php if ($flag_error) { ?> <font color="red"> Error!! </font> <?php } ?> ### JSP + JSTL <c:choose> <c:when test="${flag_error}"> <font color="red"> Error!! </font> </c:when> </c:choose> ### eRuby <% if flag_error then %><font color="red"> Error!! </font> <% end %> ### ERB % if flag_error then <font color="red"> Error!! </font> % end ### Velocity #if ($flag_error) <font color="red"> Error!! </font> #end
Iteration(foreach)
id="foreach:var=list"
(or id="foreach:var=list"
) is a directive for iteration, with assignment each value of list into a variable.
Presentation Data:
<tr id="foreach:user=user_list"> <td>#{user}#</td> </tr>
Intermediate Code:
:foreach(user = user_list) :print("<tr>\n") :print(" <td>", user, "</td>\n") :print("</tr>\n") :end
Output Program:
### Ruby for user in user_list do print "<tr>\n" print " <td>", user, "</td>\n" print "</tr>\n" end ### PHP <?php foreach ($user_list as $user) { ?> <tr> <td><?php echo $user; ?></td> </tr> <?php } ?> ### JSP + JSTL <c:forEach var="user" items="${user_list}"> <tr> <td><c:out value="${user}" escapeXml="false"/></td> </tr> </c:forEach> ### eRuby <% for user in user_list do %><tr> <td><%= user %></td> </tr> <% end %> ### ERB % for user in user_list do <tr> <td><%= user %></td> </tr> % end ### Velocity #foreach ($user in $user_list) <tr> <td>$!{user}</td> </tr> #end
Iteration(list)
id="list:variable=list_expr"
(or id="list:variable:list_expr"
) is a directive for iteration.
It iterates only contents. Start tag and end tag are not iterated.
Presentation Data:
<tr id="list:user=user_list"> <td>#{user}#</td> </tr>
Intermediate Code:
:print("<tr>\n") :foreach(user = user_list) :print(" <td>", user, "</td>\n") :end :print("</tr>\n")
Output Program:
### Ruby print "<tr>\n" for user in user_list do print " <td>", user, "</td>\n" end print "</tr>\n" ### PHP <tr> <?php foreach ($user_list as $user) { ?> <td><?php echo $user; ?></td> <?php } ?> </tr> ### JSP + JSTL <tr> <c:forEach var="user" items="${user_list}"> <td><c:out value="${user}" escapeXml="false"/></td> </c:forEach> </tr> ### eRuby <tr> <% for user in user_list do %> <td><%= user %></td> <% end %></tr> ### ERB <tr> % for user in user_list do <td><%= user %></td> % end </tr> ### Velocity <tr> #foreach ($user in $user_list) <td>$!{user}</td> #end </tr>
Iteration with count
id="Foreach:var=list"
and id="List:var=list"
are directives for iteration with loop counting.
Counter variable name is var_ctr
. Counter starts with 1.
Presentation Data:
<tr id="Foreach:item=item_list"> <td id="value:item_ctr">1</td> <td id="value:item">foo</td> </tr>
Intermediate Code:
:set(item_ctr = 0) :foreach(item = item_list) :set(item_ctr += 1) :print("<tr>\n") :print(" <td>", item_ctr, "</td>\n") :print(" <td>", item, "</td>\n") :print("</tr>\n") :end
Output Program:
### Ruby item_ctr = 0 for item in item_list do item_ctr += 1 print "<tr>\n" print " <td>", item_ctr, "</td>\n" print " <td>", item, "</td>\n" print "</tr>\n" end ### PHP <?php $item_ctr = 0; ?> <?php foreach ($item_list as $item) { ?> <?php $item_ctr += 1; ?> <tr> <td><?php echo $item_ctr; ?></td> <td><?php echo $item; ?></td> </tr> <?php } ?> ### JSP + JSTL <c:set var="item_ctr" value="0"/> <c:forEach var="item" items="${item_list}"> <c:set var="item_ctr" value="${item_ctr + 1}"/> <tr> <td><c:out value="${item_ctr}" escapeXml="false"/></td> <td><c:out value="${item}" escapeXml="false"/></td> </tr> </c:forEach> ### eRuby <% item_ctr = 0 %><% for item in item_list do %><% item_ctr += 1 %><tr> <td><%= item_ctr %></td> <td><%= item %></td> </tr> <% end %> ### ERB % item_ctr = 0 % for item in item_list do % item_ctr += 1 <tr> <td><%= item_ctr %></td> <td><%= item %></td> </tr> % end ### Velocity #set ($item_ctr = 0) #foreach ($item in $item_list) #set ($item_ctr = $item_ctr + 1) <tr> <td>$!{item_ctr}</td> <td>$!{item}</td> </tr> #end
Iteration with toggle switch
id="FOREACH:var=list"
and id="FOREACH:var=list"
are directives for iteration with toggle-switching.
Toggle-switch's value is changed if loop counter is odd or even.
Variable Name of toggle-switch is var_tgl
.
Value of toggle-switch is 'odd'
and 'even'
by default.
You can change them with command-line option --odd_value and --even_value.
Presentation Data:
<table> <tbody id="LIST:item=item_list"> <tr class="#{item_tgl}#"> <td id="value:item">foo</td> </tr> </tbody> </table>
Intermediate Code:
:print("<table>\n") :print(" <tbody>\n") :set(item_ctr = 0) :foreach(item = item_list) :set(item_ctr += 1) :set(item_tgl = item_ctr % 2 == 0 ? 'even' : 'odd') :print(" <tr class=\"", item_tgl, "\">\n") :print(" <td>", item, "</td>\n") :print(" </tr>\n") :end :print(" </tbody>\n") :print("</table>\n")
Output Program:
### Ruby print "<table>\n" print " <tbody>\n" item_ctr = 0 for item in item_list do item_ctr += 1 item_tgl = (item_ctr % 2 == 0 ? "even" : "odd") print " <tr class=\"", item_tgl, "\">\n" print " <td>", item, "</td>\n" print " </tr>\n" end print " </tbody>\n" print "</table>\n" ### PHP <table> <tbody> <?php $item_ctr = 0; ?> <?php foreach ($item_list as $item) { ?> <?php $item_ctr += 1; ?> <?php $item_tgl = ($item_ctr % 2 == 0 ? 'even' : 'odd'); ?> <tr class="<?php echo $item_tgl; ?>"> <td><?php echo $item; ?></td> </tr> <?php } ?> </tbody> </table> ### JSP + JSTL <table> <tbody> <c:set var="item_ctr" value="0"/> <c:forEach var="item" items="${item_list}"> <c:set var="item_ctr" value="${item_ctr + 1}"/> <c:choose> <c:when test="${item_ctr % 2 == 0}"> <c:set var="item_tgl" value="even"/> </c:when> <c:otherwise> <c:set var="item_tgl" value="odd"/> </c:otherwise> </c:choose> <tr class="<c:out value="${item_tgl}" escapeXml="false"/>"> <td><c:out value="${item}" escapeXml="false"/></td> </tr> </c:forEach> </tbody> </table> ### eRuby <table> <tbody> <% item_ctr = 0 %><% for item in item_list do %><% item_ctr += 1 %><% item_tgl = (item_ctr % 2 == 0 ? 'even' : 'odd') %> <tr class="<%= item_tgl %>"> <td><%= item %></td> </tr> <% end %> </tbody> </table> ### ERB <table> <tbody> % item_ctr = 0 % for item in item_list do % item_ctr += 1 % item_tgl = (item_ctr % 2 == 0 ? 'even' : 'odd') <tr class="<%= item_tgl %>"> <td><%= item %></td> </tr> % end </tbody> </table> ### Velocity <table> <tbody> #set ($item_ctr = 0) #foreach ($item in $item_list) #set ($item_ctr = $item_ctr + 1) #if ($item_ctr % 2 == 0) #set ($item_tgl = 'even') #else #set ($item_tgl = 'odd') #end <tr class="$!{item_tgl}"> <td>$!{item}</td> </tr> #end </tbody> </table>
Iteration (while)
id="while:expression"
is a directive to iterate, with while-statement.
Assignment is not expression in Kwartz, except in while-condition.
Kwartz will cause error when compiling while-statemnt into JSP or Velocity, because there is no custom tags in JSTL or Velocity's directive which is equvalent to while-statment.
Presentation Data:
<tr id="while:row=sth.fetch"> <td>#{row[0]}#</td> </tr>
Intermediate Code:
:while(row=sth.fetch) :print("<tr>\n") :print(" <td>", row[0], "</td>\n") :print("</tr>\n") :end
Output Program:
### Ruby while row = sth.fetch do print "<tr>\n" print " <td>", row[0], "</td>\n" print "</tr>\n" end ### PHP <?php while ($row = $sth->fetch) { ?> <tr> <td><?php echo $row[0]; ?></td> </tr> <?php } ?> ### eRuby <% while row = sth.fetch do %><tr> <td><%= row[0] %></td> </tr> <% end %> ### ERB % while row = sth.fetch do <tr> <td><%= row[0] %></td> </tr> % end
Dummy data
id="dummy:name"
is a directive fore ignoring an element.
You should specify a unique string(*5) in name
.
name is a string to make different id attribute value in a XML/HTML document.
You cannot specify same string in a document.
Presentation Data:
<tr id="dummy:d1"> <td>bar</td> </tr> <tr id="dummy:d2"> <td>baz</td> </tr>
Intermediate Code:
## Nothing
Output Program:
## Nothing
- (*5)
- The string have no effectivity nor meaning.
Replacement of Element
'id="replace:name"
' replaces an element with other element which is marked with name name.
Presentation Data:
<html> <body> <!-- breadclumbs navigation --> <span id="breadclumbs"> <a href="/">Home</a> > <a href="/ruby">Ruby</a> > <a href="/ruby/kwartz">Kwartz</a> </span> .... <span id="replace:breadclumbs"> Home > Ruby > Kwartz </span> </body> </html
Intermediate Code:
:macro(stag_breadclumbs) :print(" <span id=\"breadclumbs\">\n") :end :macro(cont_breadclumbs) :print(" <a href=\"/\">Home</a>\n") :print(" > <a href=\"/ruby\">Ruby</a>\n") :print(" > <a href=\"/ruby/kwartz\">Kwartz</a>\n") :end :macro(etag_breadclumbs) :print(" </span>\n") :end :macro(elem_breadclumbs) :expand(stag_breadclumbs) :expand(cont_breadclumbs) :expand(etag_breadclumbs) :end :print("<html>\n") :print(" <body>\n") :print(" <!-- breadclumbs navigation -->\n") :expand(elem_breadclumbs) .... :expand(elem_breadclumbs) :print(" </body>\n") :print("</html\n")
Include presentation data file
id="include:'filename'"
directive reads and includes other presentation data file (HTML file).
Usually, <span/>
tag is used such as <span id="include:'filename'"/>
.
(In Kwartz, <span>
tags which contain only directives are deleted automatically.)
Presentation Data(tab.html) :
<span style="border-width:1 1 0 1; border-style:solid; padding:1 5 1 5"> <a href="#{tab['url']}#" id="value:tab['name']" style="text-decoration:none">TabName</a> </span>
Presentation Data(tab-main.html) :
<div id="foreach:tab:tablist"> <span id="include:'tab.html'"/> </div>
Intermediate Code:
:foreach(tab = tablist) :print("<div>\n") ## :print(" <span>") :print("<span style=\"border-width:1 1 0 1; border-style:solid; padding:1 5 1 5\">\n") :print(" <a href=\"", tab['url'], "\" style=\"text-decoration:none\">", tab['name'], "</a>\n") :print("</span>\n") ## :print("</span>\n") :print("</div>\n") :end
Presentation data files included are to be placed in other directory.
You can specify directories with a command line option --include_path=dir1,dir2,...
.
$ kwartz --include_path=dir1,dir2 tab-main.html
Load presentation logic file
id="load:'filename'"
directive reads and includes other presentation data file (HTML file).
Usually, <span/>
tag is used such as <span id="load:'filename'"/>
.
(In Kwartz, <span>
tags which contain only directives are deleted automatically.)
Usually, file name of presentation logic is specified by command line option(-p
or -P
).
load
directive is equal to these comamnd line options.
In other words, you can specify file name of presentation logic with load
directive as well as command line options.
And load
directive is avairable with command line options.
load
directive should be at the end of presentation data file.
Otherwise, presentation logic loaded may be overwritten by presentation data file.
Presentation Logic (load-lib.plogic) :
:elem(item) :foreach(item=itemlist) @stag @cont @etag :end :end :macro(BEGIN) :print("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n") :print("<html lang=\"en\">\n") :print(" <body bgcolor=\"#FFFFFF\">\n") :end :macro(END) :print(" </body>\n") :print("<html>\n") :end
Presentation Data(load-main.html) :
<ul id="mark:itemlist"> <li id="value:item">foobar</li> </ul> <span id="load:'load-lib.plogic'"/>
Intermediate Code:
:macro(stag_itemlist) :print("<ul>\n") :end :macro(cont_itemlist) :print(" <li>", item, "</li>\n") :end :macro(etag_itemlist) :print("</ul>\n") :end :macro(elem_itemlist) :expand(stag_itemlist) :expand(cont_itemlist) :expand(etag_itemlist) :end :expand(elem_itemlist) ## :print("<span>") :load('load-lib.plogic') ## :print("</span>\n")
Output Program:
### Ruby print "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n" print "<html lang=\"en\">\n" print " <body bgcolor=\"\#FFFFFF\">\n" print "<ul>\n" print " <li>", item, "</li>\n" print "</ul>\n" print " </body>\n" print "<html>\n" ### PHP <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html lang="en"> <body bgcolor="#FFFFFF"> <ul> <li><?php echo $item; ?></li> </ul> </body> <html> ### JSP + JSTL <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html lang="en"> <body bgcolor="#FFFFFF"> <ul> <li><c:out value="${item}" escapeXml="false"/></li> </ul> </body> <html> ### eRuby <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html lang="en"> <body bgcolor="#FFFFFF"> <ul> <li><%= item %></li> </ul> </body> <html> ### ERB <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html lang="en"> <body bgcolor="#FFFFFF"> <ul> <li><%= item %></li> </ul> </body> <html> ### Velocity <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html lang="en"> <body bgcolor="#FFFFFF"> <ul> <li>$!{item}</li> </ul> </body> <html>
Presentation data files loaded are to be placed in other directory.
You can specify directories with a command line option --load_path=dir1,dir2,...
.
$ kwartz --load_path=dir1,dir2 load-main.html
Id and kd attributes
- Kd attribute is equivalent to id attribute in Kwartz. All what you write in id attribute is able to be in kd attribute.
- If a tag has both id attribute and kd attribte, Kwartz puts the latter before the former.
For example, if you write '
id="name" kd="foreach:item=list"
', kd attribute is enabled but not id attribute.
- However, if kd attribute has only
attr
directives, then id attribute is enabled. For example, if you write 'id="name" kd="attr:href=url"
, the element is marked with a namename
.
- HTML Specification defines that id attribute value can contain only alphabet, number, underbar(
_
), colon(:
), hyphen(-
) and period(.
) (in ASCII character). Other characters cannot be in id attribute value. It means thatid="foreach:item=list"
orid="attr:name=value"
are not valid in HTML specification. It is recommended to writeid="foreach:item:list"
orid="attr:name:value"
instead.
- Kd attribte is Kwartz original and it is not in HTML specification. So, you gets errors when you use W3C Markup Vaidation Service with HTML files which contain kd attributes. Kwartz gives you a CGI/PHP script which delete kd attribute from your HTML file and send it to W3C Markup Vaidation Service. They are included in Kwartz archive, and avaialble at here.
- You can change kd attribte name with command line option
--attr_name=xxx
. For example,--attr_name=style
make kwartz to use style attributes instead of kd attributes.
Notes
There are some notes to use directives.
- One id or kd attribute can have only one directive.
For example, '
id="set:var=100;value:var"
' causes an error.## NG <span id="set:var=100;value:var">???</span>
- However,
attr
directive can be used with other directive in a kd attribute, and multiple attr directive can be in a kd attribute.## OK <foo id="foreach:var=list;attr:class=var.class;attr:href=var.href"> .... </foo>
- You cannot ommit end-tag when using Kwartz directive.
You can use empty-element tag, such as '
<foo .../>
'.## NG <ul> <li id="value:item">foo </ul> ## OK <a id="attr:name=refname"/>
- Tag <span></span> tag is deleted if it have only directives.
For example, presentation data '
Hello <span id="value:name">World</span>!
' will be converted into intermediate code ':print('Hello ' .+ name .+ '!')
', with deleting <span></span>.## presentation data Hello <span id="value:name">World</span>! ## intermediate code :print("Hello ") :print(name) :print("!\n")
- You must describe attributes like
attr="value"
in start tag which contains Kwartz directives. In other words, you cannot omit attribute name or attribute value when using directives. This is not a bug; It's an intended behavior so as to make Kwartz available in any text file.## NG <input type="checkbox" id="foreach:item=list" checked/> ## OK <input type="checkbox" id="foreach:item=list" checked="checked"/> ## NG <option id="attr:value=item.value" #{item.selected ? 'selected' : ''}#/> ## OK <option value="#{item.value}#" #{item.selected ? 'selected' : ''}#>
- A directive which contains a character '
>
' will be failed to compile. For example, '<span id="while:i>0">
' will be failed to compile. You should write it as '<span id="while:0<i">
'.
Kwartz parses a presentation data with regular expression matching. Therefore, '<span id="while:i>0">
' will be parsed as '<span id="while:i>
' and '0">
', and covertion will be fail.<span id="while:i>0"> ... </span> ## NG <span id="while:0<i"> ... </span> ## OK
- '
id="set:var=flag?v1:v2"
' or 'id="value:flag?v1:v2"
' will cause syntax error, because kwartz recognize ':v2
' as beginning of statement (like ':print
' or ':if
'). You should cover ':v2
' with '()
' or insert a space between ':
' and 'v2
'.<span id="set:var=flag?v1:v2"/> ## NG <span id="set:var=flag?v1:(v2)"/> ## OK <span id="set:var=flag?v1: v2"/> ## OK
PL -- Presentation Language
Kwartz uses intermeidate language internally. The intermediate language is named PL(Presentation Language). PL is used for two purpose in Kwartz:
- To describe intermediate code (which is automatically generated from presentation data)
- To describe presentation logic
In fact, Kwartz describe presentation data and presentation logic in PL.
This section shows how to write PL program.
Comment
Characters coming after '#
' in a line is a comment.
You should use '##
' instead of '#
',
because '#*
' or '#@
' may have special functions in the future.
## comment
String
"..."
or '...'
represents string literal.
The former can contain special char: line feed (\n), carriage return (\r) or tab (\t).
'foo bar' ## String "foo bar\n" ## String which contains line feed character
Boolean and null
Keywords true
, false
, null
are available in expression.
:set(flag = obj == null? true : false)
true
, false
and null
are translated to proper keywords in each language.
Language name | true | false | null |
---|---|---|---|
Ruby,eRuby,ERB | true | false | nil |
PHP | TRUE | FALSE | NULL |
JSP(JSTL) | true | false | null |
Velocity | true | false | - |
Translation a PL program which contains keyword null
into Velocity script will cause error,
because Velocity doesn't have a keyword null
.
But Kwartz will translate 'expr==null
' and 'expr!=null
' to '!expr
' and 'expr
'
when translating into Velocity.
PL Program:
:set(flag = obj == null? true : false) :if (expr != null) :print("expr is not null.\n") :else :print("epxr is null.\n") :end
Output Script:
### Velocity #if (! $obj) #set ($flag = true) #else #set ($flag = false) #end #if ($expr) expr is not null. #else epxr is null. #end
Variable
A variable starts with alphabet or '_
', and follows with alphabet or number or '_
'.
You don't need to decrare variable, nor specify it's type(*6).
- (*6)
- Because of this feature, it is very difficult to support "static language" in Kwartz.
Operator
There are several operators. Comparable operator is available for number and string(*7).
Comparable operator | == != < <= > >= |
Logical operator | && || ! |
Arithmetic operator | + - * / % |
String Concatenation operator | .+ |
Conditional operator | ?: |
Conditional Operator is available in Ruby, eRuby, ERB and PHP. But it is not available in JSP (to be exact, in expression language of JSTL) and Velocity. Therefore, conditional operator will be translated to if-statement in JSP or Velocity.
PL Program:
:set(color = ctr%2==0 ? '#CCCCFF' : '#FFCCCC')
Output Script:
### Ruby color = (ctr % 2 == 0 ? "\#CCCCFF" : "\#FFCCCC") ### PHP <?php $color = ($ctr % 2 == 0 ? '#CCCCFF' : '#FFCCCC'); ?> ### JSP + JSTL <c:choose> <c:when test="${ctr % 2 == 0}"> <c:set var="color" value="#CCCCFF"/> </c:when> <c:otherwise> <c:set var="color" value="#FFCCCC"/> </c:otherwise> </c:choose> ### eRuby <% color = (ctr % 2 == 0 ? '#CCCCFF' : '#FFCCCC') %> ### ERB % color = (ctr % 2 == 0 ? '#CCCCFF' : '#FFCCCC') ### Velocity #if ($ctr % 2 == 0) #set ($color = '#CCCCFF') #else #set ($color = '#FFCCCC') #end
- (*7)
- It is very difficult to support Perl in Kwartz, because Perl have different operator for number and String (like '==' and 'eq', '!=' and 'ne', ...).
:print(...)
is a print statement. Any expression can be in print statement.
PL Program:
:print('foo', bar, "baz\n") ## print a string and value of a variable
Output Script:
### Ruby print "foo", bar, "baz\n" ### PHP foo<?php echo $bar; ?>baz ### JSP + JSTL foo<c:out value="${bar}" escapeXml="false"/>baz ### eRuby foo<%= bar %>baz ### ERB foo<%= bar %>baz ### Velocity foo$!{bar}baz
When you make auto-sanitizing enabled, the output scripts of Ruby, PHP, eRuby and ERB will be the following:
### Ruby print "foo", CGI.escapeHTML((bar).to_s), "baz\n" ### PHP foo<?php echo htmlspecialchars($bar); ?>baz ### eRuby foo<%= CGI.escapeHTML((bar).to_s) %>baz ### ERB foo<%= html_escape(bar) %>baz
Assignment
':set(var=value)
' is an assignment statement.
Spaces around '(
' or '=
' are allowed.
Also '+=
', '-=
', '*=
', '/=
', '%=
' and '.+=
' are allowed.
'.+=
' means string concatination.
PL Program:
:set(name = 'Foo') ## assign a string 'Foo' into variable :set(count += 1) ## increment value of variable :set(str .+= '.txt') ## append a string
Output Script:
### Ruby name = "Foo" count += 1 str << ".txt" ### PHP <?php $name = 'Foo'; ?> <?php $count += 1; ?> <?php $str .= '.txt'; ?> ### JSP + JSTL <c:set var="name" value="Foo"/> <c:set var="count" value="${count + 1}"/> <c:set var="str" value="${str}${'.txt'}"/> ### eRuby <% name = 'Foo' %><% count += 1 %><% str << '.txt' %> ### ERB % name = 'Foo' % count += 1 % str << '.txt' ### Velocity #set ($name = 'Foo') #set ($count = $count + 1) #set ($str = "${str}.txt")
Array and Hash
You can refer an array as arr[expr]
.
Also you can refer a hash in the same way.(*8).
PL Program:
:set(list[n] = 10) ## assign into n-th element of array :print(list[i], "\n") ## print i-th element of array :set(hash['key'] = 'foo') ## assign into a hash element :print(hash['key']) ## print a hash element
Output Script:
### Ruby list[n] = 10 print list[i], "\n" hash["key"] = "foo" print hash["key"] ### PHP <?php $list[$n] = 10; ?> <?php echo $list[$i]; ?> <?php $hash['key'] = 'foo'; ?> <?php echo $hash['key']; ?> ### JSP + JSTL <c:set var="list[n]" value="10"/> <c:out value="${list[i]}" escapeXml="false"/> <c:set var="hash['key']" value="foo"/> <c:out value="${hash['key']}" escapeXml="false"/> ### eRuby <% list[n] = 10 %><%= list[i] %> <% hash['key'] = 'foo' %><%= hash['key'] %> ### ERB % list[n] = 10 <%= list[i] %> % hash['key'] = 'foo' <%= hash['key'] %> ### Velocity #set ($list.get($n) = 10) $!{list.get($i)} #set ($hash.key = 'foo') $!{hash.get('key')}
You can also refer a hash as 'hash[:key]
'.
'key
' must be a string which is constructed only with alphabet, number or '_
'.
'hash[:key]
' will be translated according to target programming language:
Target language | Result of translation |
---|---|
Ruby, eRuby, ERB | hash[:key] |
PHP | $hash['key'] |
JSP | hash['key'] |
Velocity | hash.key |
PL Program:
:set(hash[:key] = 'foo') :print(hash[:key])
Output Script:
### Ruby hash[:key] = "foo" print hash[:key] ### PHP <?php $hash['key'] = 'foo'; ?> <?php echo $hash['key']; ?> ### JSP + JSTL <c:set var="hash['key']" value="foo"/> <c:out value="${hash['key']}" escapeXml="false"/> ### eRuby <% hash[:key] = 'foo' %><%= hash[:key] %> ### ERB % hash[:key] = 'foo' <%= hash[:key] %> ### Velocity #set ($hash.key = 'foo') $!{hash.key}
- (*8)
- Operators for array and hash are same, therefore it is difficult to support a language, say Perl, which use different operator for array and hash.
Property
You can refer a property of an object as 'object.property
'.
Property is not a method, so property cannot accept any arguments.
There is no way to create a new object nor define a new class in PL. These tasks must be done in main program.
PL Program:
:print(user.name)
Output Script:
### Ruby print user.name ### PHP <?php echo $user->name; ?> ### JSP + JSTL <c:out value="${user.name}" escapeXml="false"/> ### eRuby <%= user.name %> ### ERB <%= user.name %> ### Velocity $!{user.name}
Iteration
:foreach(variable=value_list) ... :end
represents iteration, known as foreach statement.
Each item in an array list
is assigned into variable var
in iteration.
PL Program:
:foreach(item = list) :print(item, "\n") :end
Output Script:
### Ruby for item in list do print item, "\n" end ### PHP <?php foreach ($list as $item) { ?> <?php echo $item; ?> <?php } ?> ### JSP + JSTL <c:forEach var="item" items="${list}"> <c:out value="${item}" escapeXml="false"/> </c:forEach> ### eRuby <% for item in list do %><%= item %> <% end %> ### ERB % for item in list do <%= item %> % end ### Velocity #foreach ($item in $list) $!{item} #end
You can also use while-statment. Kwartz will cause an error if you translate while-statement into JSP or Velocity script, because there is no JSTL custom tag or Velocity directive which are equvalent to while-statment.
PL Program:
:set(i = 0) :while(i < length) :print(list[i]) :set(i += 1) :end
Output Script:
### Ruby i = 0 while i < length do print list[i] i += 1 end ### PHP <?php $i = 0; ?> <?php while ($i < $length) { ?> <?php echo $list[$i]; ?><?php $i += 1; ?> <?php } ?> ### eRuby <% i = 0 %><% while i < length do %><%= list[i] %><% i += 1 %><% end %> ### ERB % i = 0 % while i < length do <%= list[i] %> % i += 1 % end
for-statement (like C or Java) is not available.
Conditional branch
':if(condition) ... :elsif(condition) ... :else ... :end
' represents conditional branch.
PL Program:
:if(ctr % 2 == 0) ## if even :set(klass='even') ## then assign a string 'even' :else ## else :set(klass='odd') ## assign a string 'odd' :end
Output Script:
### Ruby if ctr % 2 == 0 then klass = "even" else klass = "odd" end ### PHP <?php if ($ctr % 2 == 0) { ?> <?php $klass = 'even'; ?> <?php } else { ?> <?php $klass = 'odd'; ?> <?php } ?> ### JSP + JSTL <c:choose> <c:when test="${ctr % 2 == 0}"> <c:set var="klass" value="even"/> </c:when> <c:otherwise> <c:set var="klass" value="odd"/> </c:otherwise> </c:choose> ### eRuby <% if ctr % 2 == 0 then %><% klass = 'even' %><% else %><% klass = 'odd' %><% end %> ### ERB % if ctr % 2 == 0 then % klass = 'even' % else % klass = 'odd' % end ### Velocity #if ($ctr % 2 == 0) #set ($klass = 'even') #else #set ($klass = 'odd') #end
Macro
':macro(macro_name) ... :end
' represents a macro definition.
':expand(macro_name)
' or '@macro_name
' represents a macro expantion.
The following is a sample of macro definition and expantion. It represents start tag, content and end tag of an element '<li>#{item}#</li>'.
## macro definition :macro(stag_item) :print("<li>") :end :macro(cont_item) :print(item) :end :macro(etag_item) :print("</li>") :end ## macro expantion (it is equivalent to '<li>#{item}#</li>') :expand(stag_item) :expand(cont_item) :expand(etag_item)
Macro expantion can be in macro definition.
## macro definition :macro(stag_item) :print("<li>") :end :macro(cont_item) :print(item) :end :macro(etag_item) :print("</li>") :end :macro(elem_item) ## macro definition containing macro expantion :expand(stag_item) :expand(cont_item) :expand(etag_item) :end ## equivalent to '<li>#{item}#</li>' :expand(elem_item)
Special syntax for defining macro of an element is available. The syntax is only for element macro definition.
## Macro definition for an element :elem(item) @stag @cont @etag :end ## Above is equal to the following :macro(elem_item) :expand(stag_item) :expand(cont_item) :expand(etag_item) :end
You can use '@stag
', '@cont
' and '@etag
' only in :elem() .. :end
.
Using them in :macro() .. :end
will be error.
## NG :macro(elem_item) ## must be ':elem(item)' @stag @cont @etag :end
Kwartz provides a special syntax to define a macro of content of elements.
It is equivalent to a directive id="value:expr"
.
## replace content of element with epxression value :value(item = expr) ## above is equal to the following :macro(cont_item) :print(expr) :end
Macro cannt accept any arguments.
Macro BEGIN and END
'BEGIN' and 'END' are special macros. If you define them, you can add codes at beginning/end of the output script.
Presentation data:
Hello <span id="value:user">World</span>!
PL Program:
:macro(BEGIN) :print("<html>\n") :print(" <body>\n") :end :macro(END) :print(" </body>\n") :print("</html>\n") :end
Output script:
### Ruby print "<html>\n" print " <body>\n" print " Hello " print user print "!\n" print " </body>\n" print "</html>\n" ### PHP <html> <body> Hello <?php echo $user; ?>! </body> </html>
Function
You can use the following two functions in PL.
- E(expr)
- Sanitize expression expr. Sanitizing is done even when command line option for sanitizing is not specified.
- X(expr)
- Don't sanitize expression expr, even when command line options for sanitizing is specified.
Currently, you cannot define or use any function except above two.
Empty
'empty
' is a keyword in order to check whether value is null or empty string.
It can be praced only at the right-side of ==
or !=
operator.
PL Program:
:if (str1 == empty) :print("str1 is empty.\n") :elsif(str2 != empty) :print("str2 is not empty.\n") :end
Output Script:
### Ruby if (str1 == nil || str1 == "") then print "str1 is empty.\n" elsif (str2 != nil && str2 != "") then print "str2 is not empty.\n" end ### PHP <?php if (! $str1) { ?> str1 is empty. <?php } elseif ($str2) { ?> str2 is not empty. <?php } ?> ### JSP + JSTL <c:choose> <c:when test="${(empty str1)}"> str1 is empty. </c:when> <c:when test="${!(empty str2)}"> str2 is not empty. </c:when> </c:choose> ### eRuby <% if (str1 == nil || str1 == '') then %>str1 is empty. <% elsif (str2 != nil && str2 != '') then %>str2 is not empty. <% end %> ### ERB % if (str1 == nil || str1 == '') then str1 is empty. % elsif (str2 != nil && str2 != '') then str2 is not empty. % end ### Velocity #if ((! $str1 || $str1 == '')) str1 is empty. #elseif (($str2 && $str2 != '')) str2 is not empty. #end
Load presentation logic file
:load('filename')
loads other presentation logic file.
You can specify directories at where files are placed by command line option --load_path=dir1,dir2,...
.
PL Program:
:load('file.plogic')
:load()
can load only presentation logic file.
It cannot load presentation data file or output program.
Raw-code
Code of the target program language (such as Ruby, PHP, ...) can be in PL program.
'::: raw code
' represents a raw-code of target program language.
Kwartz outputs a string after ':::
' as a raw-code.
The following is an example which include PHP code.
PL Program:
::: <?php foreach($hash as $key => $value) { ?> :print("key=", key, " value=", value, "\n") ::: <?php } ?>
Output Script:
### PHP <?php foreach($hash as $key => $value) { ?> key=<?php echo $key; ?> value=<?php echo $value; ?> <?php } ?>
Command line option '--enable_eruby=true' enables you to embed several language code in presentation logic file. See 'Enable eRuby code in presentation logic' for more detail.
Global and Local Variable
In Kwartz, variables are called Global variables which are setted in main program and passed to output script. And variables are called Local variables which are used only in template.
Assum the following presentation data and presentation logic:
Presentation data (analyze.html) :
<html> <body> <table> <caption id="value:title">Sample</caption> <tr id="item_list" bgcolor="#{color}#"> <td id="value:item_ctr">1</td> <td id="value:item">Foo</td> </tr> </table> </body> </html>
Presentation logic (analyze.plogic) :
:elem(item_list) :set(item_ctr = 0) :foreach(item = item_list) :set(item_ctr += 1) :set(color = item_ctr%2 == 0 ? '#FFCCCC' : '#CCCCFF') @stag @cont @etag :end :end
There are several variables. They are classified as bellow:
- Global variables
-
Variables
title
anditem_list
are needed to be setted in main program and passed to template. These variables are called Global variables in Kwartz. - Local variables
-
Variables
item
,item_ctr
andcolor
are used only in template. These variables are called Local variables in Kwartz.
Invoking kwartz command with command option -a analyze
analyzes template and reports global/local variables.
$ kwartz -p analyze.plogic -a analyze analyze.html global variables: title item_list local variables: color item item_ctr
Kwartz detemines global/lobal variables according to the following rule:
- When Kwartz finds a new variable ...
- If it appears in left-side of assignment, Kwartz recognizes it as local variable.
- If it appears as loop variable of foreach statement, Kwartz recognizes it as local variable.
- Otherwize, Kwartz recognizes it as global variable.
Kwartz reports warnings if you global variables are appeared in left-side of assignment or used as loop variable of foreach statement. Because role of template system is to display global variables, and templates should not change or modify values of global variables. In addition, kwartz reports warning when uninitalized local variables are used in template.
Analyzing template and reporting global/local variables are very useful, especially presentation data/logic are large and complex. It also helps you to find typo of variable names.
Notes
- '
:print(condition?v1:v2)
' or ':set(var=flag?v1:v2)
' will cause syntax error, because kwartz recognize ':v2
' as beginning of statement (like ':print
' or ':if
'). You should cover ':v2
' with '()
' or insert a space between ':
' and 'v2
'.:set(var=flag?v1:v2) ## NG :set(var=flag?v1:(v2)) ## OK :set(var=flag?v1: v2) ## OK
- Nested macro definition/expantion will be in infinite-loop when compiling.
Please take care not to define nested macro.
:macro(elem_foo) :expand(stag_foo) :expand(elem_foo) ## nested macro :expand(etag_foo) :end :expand(elem_foo) ## infinite-loop
Features under consideration
The following features are not implemented. They may be implemented in the future release (or not).
- break, continue
- user-define function/procedure
- method call (with arguments)
- macro with arguments
- creating an array or hash
Practical Examples
Border colored table
This is a example of border colored table. Each colors in odd lines and even lines are different. The example is so easy that you'll do it only with directives.
Presentation data(table1.html):
<table border="0"> <tbody id="Foreach:user=user_list"> <tr bgcolor="#CCCCFF" id="attr:bgcolor=color;set:color=user_ctr%2==0?'#FFCCCC':'#CCCCFF'"> <td id="value:user[:name]">foo</td> <td id="value:user[:email]">foo@foo.com</td> </tr> <tr id="dummy:d1"> <td>bar</td> <td>bar@bar.net</td> </tr> <tr id="dummy:d2"> <td>baz</td> <td>baz@baz.org</td> </tr> </tbody> </table>
Intermediate code:
:print("<table border=\"0\">\n") :set(user_ctr = 0) :foreach(user = user_list) :set(user_ctr += 1) :print(" <tbody>\n") :set(color=user_ctr%2==0?'#FFCCCC':'#CCCCFF') :print(" <tr bgcolor=\"", color, "\">\n") :print(" <td>", user[:name], "</td>\n") :print(" <td>", user[:email], "</td>\n") :print(" </tr>\n") :print(" </tbody>\n") :end :print("</table>\n")
Compile:
$ kwartz -l ruby table1.html > table1.rb $ kwartz -l ruby2 table1.html > table1.rb2 $ kwartz -l php table1.html > table1.php $ kwartz -l jsp table1.html > table1.jsp $ kwartz -l eruby table1.html > table1.rhtml $ kwartz -l erb table1.html > table1.erb $ kwartz -l velocity table1.html > table1.vm
The following output scripts will be created after compiling.
Ruby:
print "<table border=\"0\">\n" user_ctr = 0 for user in user_list do user_ctr += 1 print " <tbody>\n" color = (user_ctr % 2 == 0 ? "\#FFCCCC" : "\#CCCCFF") print " <tr bgcolor=\"", color, "\">\n" print " <td>", user[:name], "</td>\n" print " <td>", user[:email], "</td>\n" print " </tr>\n" print " </tbody>\n" end print "</table>\n"
Ruby2:
_s << "<table border=\"0\">\n" user_ctr = 0 for user in user_list do user_ctr += 1 _s << " <tbody>\n" color = (user_ctr % 2 == 0 ? "\#FFCCCC" : "\#CCCCFF") _s << " <tr bgcolor=\"" << (color).to_s << "\">\n" _s << " <td>" << (user[:name]).to_s << "</td>\n" _s << " <td>" << (user[:email]).to_s << "</td>\n" _s << " </tr>\n" _s << " </tbody>\n" end _s << "</table>\n"
PHP:
<table border="0"> <?php $user_ctr = 0; ?> <?php foreach ($user_list as $user) { ?> <?php $user_ctr += 1; ?> <tbody> <?php $color = ($user_ctr % 2 == 0 ? '#FFCCCC' : '#CCCCFF'); ?> <tr bgcolor="<?php echo $color; ?>"> <td><?php echo $user['name']; ?></td> <td><?php echo $user['email']; ?></td> </tr> </tbody> <?php } ?> </table>
JSP:
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %> <table border="0"> <c:set var="user_ctr" value="0"/> <c:forEach var="user" items="${user_list}"> <c:set var="user_ctr" value="${user_ctr + 1}"/> <tbody> <c:choose> <c:when test="${user_ctr % 2 == 0}"> <c:set var="color" value="#FFCCCC"/> </c:when> <c:otherwise> <c:set var="color" value="#CCCCFF"/> </c:otherwise> </c:choose> <tr bgcolor="<c:out value="${color}" escapeXml="false"/>"> <td><c:out value="${user['name']}" escapeXml="false"/></td> <td><c:out value="${user['email']}" escapeXml="false"/></td> </tr> </tbody> </c:forEach> </table>
eRuby:
<table border="0"> <% user_ctr = 0 %><% for user in user_list do %><% user_ctr += 1 %> <tbody> <% color = (user_ctr % 2 == 0 ? '#FFCCCC' : '#CCCCFF') %> <tr bgcolor="<%= color %>"> <td><%= user[:name] %></td> <td><%= user[:email] %></td> </tr> </tbody> <% end %></table>
ERB:
<table border="0"> % user_ctr = 0 % for user in user_list do % user_ctr += 1 <tbody> % color = (user_ctr % 2 == 0 ? '#FFCCCC' : '#CCCCFF') <tr bgcolor="<%= color %>"> <td><%= user[:name] %></td> <td><%= user[:email] %></td> </tr> </tbody> % end </table>
Velocity:
<table border="0"> #set ($user_ctr = 0) #foreach ($user in $user_list) #set ($user_ctr = $user_ctr + 1) <tbody> #if ($user_ctr % 2 == 0) #set ($color = '#FFCCCC') #else #set ($color = '#CCCCFF') #end <tr bgcolor="$!{color}"> <td>$!{user.name}</td> <td>$!{user.email}</td> </tr> </tbody> #end </table>
Navigation of 'Next' or 'Previous'
If there is a sequence of HTML files, each files should have navigation link such as 'Next page' or 'Previous page'. Here we create the next/previous link.
- In main program, set the url of next/previous page to variable
next_url
/prev_url
. If there is not next/previous page, set null tonext_url
/prev_url
. - In template, print navigation link with <a></a> tag and variables
next_url
/prev_url
. If variables are null, then don't print <a></a> tag. - Navigation apprears at the top and the end of document.
Presentation Data (navilink.html):
<html> <body> <span id="mark:navilink"> <a id="mark:prev" href="#{prev_url}#"> < Previous page </a> <a id="mark:next" href="#{next_url}#"> Next page > </a> </span> ...... ...... ...... <span id="mark:navilink2"> < Previous page Next page > </span> </body> </html>
Presentation Logic (navilink.plogic):
## if URL is null then don't print start-tag and end-tag :elem(next) :if(next_url != null) @stag @cont @etag :else @cont :end :end :elem(prev) :if(prev_url != null) @stag @cont @etag :else @cont :end :end ## replace an element 'navi2' with an element 'navi'. :elem(navilink2) @elem_navilink :end
You can use kd="replace:navilink"
to replace an element named 'navilink2' with an element named 'navilink'.
Compile:
$ kwartz -p navilink.plogic -l ruby navilink.html > navilink.rb $ kwartz -p navilink.plogic -l ruby2 navilink.html > navilink.rb2 $ kwartz -p navilink.plogic -l php navilink.html > navilink.php $ kwartz -p navilink.plogic -l jsp navilink.html > navilink.jsp $ kwartz -p navilink.plogic -l eruby navilink.html > navilink.rhtml $ kwartz -p navilink.plogic -l erb navilink.html > navilink.erb $ kwartz -p navilink.plogic -l velocity navilink.html > navilink.vm
The following output scripts will be created after compiling.
Ruby:
print "<html>\n" print " <body>\n" if prev_url != nil then print " <a href=\"", prev_url, "\">\n" print " < Previous page\n" print " </a>\n" else print " < Previous page\n" end print " \n" if next_url != nil then print " <a href=\"", next_url, "\">\n" print " Next page >\n" print " </a>\n" else print " Next page >\n" end ...... ...... ...... if prev_url != nil then print " <a href=\"", prev_url, "\">\n" print " < Previous page\n" print " </a>\n" else print " < Previous page\n" end print " \n" if next_url != nil then print " <a href=\"", next_url, "\">\n" print " Next page >\n" print " </a>\n" else print " Next page >\n" end print "\n" print " </body>\n" print "</html>\n"
Ruby2:
_s << "<html>\n" _s << " <body>\n" if prev_url != nil then _s << " <a href=\"" << (prev_url).to_s << "\">\n" _s << " < Previous page\n" _s << " </a>\n" else _s << " < Previous page\n" end _s << " \n" if next_url != nil then _s << " <a href=\"" << (next_url).to_s << "\">\n" _s << " Next page >\n" _s << " </a>\n" else _s << " Next page >\n" end ...... ...... ...... if prev_url != nil then _s << " <a href=\"" << (prev_url).to_s << "\">\n" _s << " < Previous page\n" _s << " </a>\n" else _s << " < Previous page\n" end _s << " \n" if next_url != nil then _s << " <a href=\"" << (next_url).to_s << "\">\n" _s << " Next page >\n" _s << " </a>\n" else _s << " Next page >\n" end _s << "\n" _s << " </body>\n" _s << "</html>\n"
PHP:
<html> <body> <?php if ($prev_url != NULL) { ?> <a href="<?php echo $prev_url; ?>"> < Previous page </a> <?php } else { ?> < Previous page <?php } ?> <?php if ($next_url != NULL) { ?> <a href="<?php echo $next_url; ?>"> Next page > </a> <?php } else { ?> Next page > <?php } ?> ...... ...... ...... <?php if ($prev_url != NULL) { ?> <a href="<?php echo $prev_url; ?>"> < Previous page </a> <?php } else { ?> < Previous page <?php } ?> <?php if ($next_url != NULL) { ?> <a href="<?php echo $next_url; ?>"> Next page > </a> <?php } else { ?> Next page > <?php } ?> </body> </html>
JSP:
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %> <html> <body> <c:choose> <c:when test="${prev_url != null}"> <a href="<c:out value="${prev_url}" escapeXml="false"/>"> < Previous page </a> </c:when> <c:otherwise> < Previous page </c:otherwise> </c:choose> <c:choose> <c:when test="${next_url != null}"> <a href="<c:out value="${next_url}" escapeXml="false"/>"> Next page > </a> </c:when> <c:otherwise> Next page > </c:otherwise> </c:choose> ...... ...... ...... <c:choose> <c:when test="${prev_url != null}"> <a href="<c:out value="${prev_url}" escapeXml="false"/>"> < Previous page </a> </c:when> <c:otherwise> < Previous page </c:otherwise> </c:choose> <c:choose> <c:when test="${next_url != null}"> <a href="<c:out value="${next_url}" escapeXml="false"/>"> Next page > </a> </c:when> <c:otherwise> Next page > </c:otherwise> </c:choose> </body> </html>
eRuby:
<html> <body> <% if prev_url != nil then %> <a href="<%= prev_url %>"> < Previous page </a> <% else %> < Previous page <% end %> <% if next_url != nil then %> <a href="<%= next_url %>"> Next page > </a> <% else %> Next page > <% end %> ...... ...... ...... <% if prev_url != nil then %> <a href="<%= prev_url %>"> < Previous page </a> <% else %> < Previous page <% end %> <% if next_url != nil then %> <a href="<%= next_url %>"> Next page > </a> <% else %> Next page > <% end %> </body> </html>
ERB:
<html> <body> % if prev_url != nil then <a href="<%= prev_url %>"> < Previous page </a> % else < Previous page % end % if next_url != nil then <a href="<%= next_url %>"> Next page > </a> % else Next page > % end ...... ...... ...... % if prev_url != nil then <a href="<%= prev_url %>"> < Previous page </a> % else < Previous page % end % if next_url != nil then <a href="<%= next_url %>"> Next page > </a> % else Next page > % end </body> </html>
Velocity:
<html> <body> #if ($prev_url) <a href="$!{prev_url}"> < Previous page </a> #else < Previous page #end #if ($next_url) <a href="$!{next_url}"> Next page > </a> #else Next page > #end ...... ...... ...... #if ($prev_url) <a href="$!{prev_url}"> < Previous page </a> #else < Previous page #end #if ($next_url) <a href="$!{next_url}"> Next page > </a> #else Next page > #end </body> </html>
Breadclumbs
It is very popular that Web page have links such as 'Home > Ruby > Kwartz'. It is called 'Breadcrumbs' or 'Trail'.
Here we create that.
- In main program, set title and path to a hash, and add it to an array.
- In template, print title and path, separating with '>'.
## Ruby breadcrumbs = [] breadcrumbs << { :title => 'Home', :path => '/' } breadcrumbs << { :title => 'Ruby', :path => '/ruby/' } breadcrumbs << { :title => 'Kwartz', :path => '/ruby/kwartz/' } ## PHP $breadcrumbs = array('title' => 'Home', 'path' => '/'); $breadcrumbs = array('title' => 'Ruby', 'path' => '/ruby/'); $breadcrumbs = array('title' => 'Kwartz', 'path' => '/ruby/kwartz/'); ## Java List breadcrumbs = new ArrayList(); Map hash = new HashMap(); hash.put('title', 'Home'); hash.put('path', '/'); breadcrumbs.add(hash); hash = new HashMap(); hash.put('title', 'Ruby') hash.put('path', '/ruby/') breadcrumbs.add(hash); hash = new HashMap(); hash.put('title', 'Kwartz') hash.put('path', '/ruby/kwartz/') breadcrumbs.add(hash);
Presentation Data:
<span id="mark:breadcrumbs"> <a id="mark:item" href="#{item_path}#"> <span id="value:item_title">Home</span> </a> <span id="mark:arrow"> > </span> </span>
Presentation Logic:
:elem(breadcrumbs) :set(item_ctr = 0) :foreach(item = breadcrumbs) :set(item_ctr += 1) :if(item_ctr > 1) ## each time except first time @elem_arrow ## print < :end @elem_item ## and print <a></a>. :end :end :elem(item) :set(item_path = item[:path]) ## retrieve path and :set(item_title = item[:title]) ## title from a hash. :if(item_path!=null) @stag @cont @etag :else ## if path is null then @cont ## don't print <a></a>. :end :end
Compile:
$ kwartz -p breadcrumbs.plogic -l ruby breadcrumbs.html > breadcrumbs.rb $ kwartz -p breadcrumbs.plogic -l ruby2 breadcrumbs.html > breadcrumbs.rb2 $ kwartz -p breadcrumbs.plogic -l php breadcrumbs.html > breadcrumbs.php $ kwartz -p breadcrumbs.plogic -l jsp breadcrumbs.html > breadcrumbs.jsp $ kwartz -p breadcrumbs.plogic -l eruby breadcrumbs.html > breadcrumbs.rhtml $ kwartz -p breadcrumbs.plogic -l erb breadcrumbs.html > breadcrumbs.erb $ kwartz -p breadcrumbs.plogic -l velocity breadcrumbs.html > breadcrumbs.vm
The following output scripts will be created after compiling.
Ruby:
item_ctr = 0 for item in breadcrumbs do item_ctr += 1 if item_ctr > 1 then print "> " end item_path = item[:path] item_title = item[:title] if item_path != nil then print " <a href=\"", item_path, "\">\n" print item_title print " </a>\n" else print item_title end end
Ruby2:
item_ctr = 0 for item in breadcrumbs do item_ctr += 1 if item_ctr > 1 then _s << "> " end item_path = item[:path] item_title = item[:title] if item_path != nil then _s << " <a href=\"" << (item_path).to_s << "\">\n" _s << (item_title).to_s _s << " </a>\n" else _s << (item_title).to_s end end
PHP:
<?php $item_ctr = 0; ?> <?php foreach ($breadcrumbs as $item) { ?> <?php $item_ctr += 1; ?> <?php if ($item_ctr > 1) { ?> > <?php } ?> <?php $item_path = $item['path']; ?> <?php $item_title = $item['title']; ?> <?php if ($item_path != NULL) { ?> <a href="<?php echo $item_path; ?>"> <?php echo $item_title; ?> </a> <?php } else { ?> <?php echo $item_title; ?><?php } ?> <?php } ?>
JSP:
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %> <c:set var="item_ctr" value="0"/> <c:forEach var="item" items="${breadcrumbs}"> <c:set var="item_ctr" value="${item_ctr + 1}"/> <c:choose> <c:when test="${item_ctr > 1}"> > </c:when> </c:choose> <c:set var="item_path" value="${item['path']}"/> <c:set var="item_title" value="${item['title']}"/> <c:choose> <c:when test="${item_path != null}"> <a href="<c:out value="${item_path}" escapeXml="false"/>"> <c:out value="${item_title}" escapeXml="false"/> </a> </c:when> <c:otherwise> <c:out value="${item_title}" escapeXml="false"/></c:otherwise> </c:choose> </c:forEach>
eRuby:
<% item_ctr = 0 %><% for item in breadcrumbs do %><% item_ctr += 1 %><% if item_ctr > 1 then %>> <% end %><% item_path = item[:path] %><% item_title = item[:title] %><% if item_path != nil then %> <a href="<%= item_path %>"> <%= item_title %> </a> <% else %><%= item_title %><% end %><% end %>
ERB:
% item_ctr = 0 % for item in breadcrumbs do % item_ctr += 1 % if item_ctr > 1 then > % end % item_path = item[:path] % item_title = item[:title] % if item_path != nil then <a href="<%= item_path %>"> <%= item_title %> </a> % else <%= item_title %> % end % end
Velocity:
#set ($item_ctr = 0) #foreach ($item in $breadcrumbs) #set ($item_ctr = $item_ctr + 1) #if ($item_ctr > 1) > #end #set ($item_path = $item.path) #set ($item_title = $item.title) #if ($item_path) <a href="$!{item_path}"> $!{item_title} </a> #else $!{item_title}#end #end
This example shows you how to switch messages according to condition.
Presentaion data:
<span id="noerror">No errors.</span> <font id="error" color="red">Error occurred!</font>
Presentaion logic:
:elem(error_report) @stag :if(error_report == null) @elem_noerror ## prints 'No errors.' :else @elem_error ## prints 'Error occurred!' :end @etag :end
In the above presentation logic, elements(@elem_noerror
and @elem_noerror
) are used instead of content of error_report
element.
This way is very useful. Remember it.
If you have an array which contains several error messages, say:
Presentaion data:
<font id="error_list" color="red"> <span id="value:error">Name is required.</span><br> </font>
Presentation logic:
:elem(error_list) :if(error_list != null) @stag :foreach(error = error_list) @cont :end @cont :end :end
From the viewpoint of 'Separation of presentation from business logic', it is not a good idea to set error message in main program, because error messages should be included in presentation layer. Role of presentation layer is to determine what message should be used according to error code.
The following way is one of the idea to share the responsibility of template and main program.
- In main program, error messages are stored in a hash using error code as keys.
- In template, error messages are displayed with conditional branch. You may display error messages which are prepared in main program, or display original messages.
Presentaion Data:
<font id="if:errors!=null" color="red"> <!-- Display error messages that is prepared in main program. --> <span id="if:errors['age']!=null">#{errors['age']}#<br><span> <span id="if:errors['tel']!=null">#{errors['tel']}#<br><span> <!-- Display original error messages. --> <span id="if:errors['name']!=null">Name is required.<br></span> <span id="if:errors['mail']!=null">Mail address is required.<br></span> </font>
You may create a error message catalog and load it from main program. It is also one of the preferred approach.
HTML Form
An example in this section is a form to register user name and gender.
There are several files in this example.
- register.cgi
- CGI Main program (Ruby).
- register.html
- Template for registration page.
- register.rhtml
- Output script for registration page (eRuby).
- finish.html
- Template for finished page.
- finish.rhtml
- Output script for finished page (eRuby).
Form CGI program (main program) is created with the following approach:
- if no parameter given, then print registration page.
- if parameters are given...
- if given parameters are valid then regist them and print finish page.
- else print registration page again with error messages.
Auto-sanitizing and eruby is adopted in this example.
Template for registration page(register.html):
<html> <body> <form action="/cgi-bin/register.cgi" method="POST"> <span id="if:error_list==null"> <font color="#FF0000" id="foreach:error=error_list"> <span id="value:error">Name is empty.</span><br> </font> </span> <table border="0" cellspacing="1" cellpadding="5"> <tr> <td>Name:</td> <td> <input type="text" name="name" size="20" id="attr:value=param[:name]"> </td> </tr> <tr> <td>Gender:</td> <td> <input type="radio" name="gender" value="M" #{X(param[:gender]=='M'?'checked':'')}#>Man <input type="radio" name="gender" value="W" #{X(param[:gender]=='W'?'checked':'')}#>Woman </td> </tr> <tr> <td colspan="2" align="right"> <input type="submit" value=" Regist "> <input type="reset" value="reset"> </td> </tr> </table> </form> </body> </html>
Output script for registration page(register.rhtml):
<html> <body> <form action="/cgi-bin/register.cgi" method="POST"> <% if error_list == nil then %><% for error in error_list do %> <font color="#FF0000"> <%= CGI.escapeHTML((error).to_s) %><br> </font> <% end %><% end %> <table border="0" cellspacing="1" cellpadding="5"> <tr> <td>Name:</td> <td> <input type="text" name="name" size="20" value="<%= CGI.escapeHTML((param[:name]).to_s) %>"> </td> </tr> <tr> <td>Gender:</td> <td> <input type="radio" name="gender" value="M" <%= (param[:gender] == 'M' ? 'checked' : '') %>>Man <input type="radio" name="gender" value="W" <%= (param[:gender] == 'W' ? 'checked' : '') %>>Woman </td> </tr> <tr> <td colspan="2" align="right"> <input type="submit" value=" Regist "> <input type="reset" value="reset"> </td> </tr> </table> </form> </body> </html>
Template of a finished page(finish.html):
<html> <body> Registration finished with the following data:<br> Name: <span id="value:param[:name]">Foo</span><br> Gender: <span id="if:param[:gender]=='M'">Man</span> <span id="if:param[:gender]=='W'">Woman</span> </body> </html>
Output script for finished page(finish.rhtml):
<html> <body> Registration finished with the following data:<br> Name: <%= CGI.escapeHTML((param[:name]).to_s) %><br> Gender: <% if param[:gender] == 'M' then %>Man<% end %><% if param[:gender] == 'W' then %>Woman<% end %> </body> </html>
CGI main program(register.cgi):
#!/usr/bin/ruby ## create a hash object from CGI object require 'cgi' cgi = CGI.new param = {} cgi.param.each do |key, value| param[key.intern] = value[0] end param.default = '' ## action for submit button error_list = nil if !param.empty? then ## check input data error_list = [] if param[:name] == '' then error_list << 'Name is empty.' end case param[:gender] when 'M', 'W' # OK else error_list << 'Gender is not selected.' end ## if input parameter is valid then print finished page(finish.rhtml), ## else print the sampe page(register.rhtml) if error_list.empty? then error_list = nil filename = 'finish.rhtml' ... data registration process ... else filename = 'register.rhtml' end end ## print web page require 'eruby' ERuby::import(filename)
Calendar
This is an example to show calendar, and also an example which explains component-approach development. Here is the final output.
Calendar requires a complex presentation logic. Therefore, this is one of the best example which represents effectiveness of 'separation of presentation logic and presentation data'.
This example has two presentation data files and two presentation logic files. And main program is writtein in PHP.
- calendar.html, calendar.plogic
- Presentation data file and presentation logic file to display calendar of a month.
- calendar-page.html, calendar-page.plogic
- Presentation data file and presentation logic file for whole web page.
- calendar-page.php
- Main program.
Presentation Data (calendar.html) :
<table cellpadding="2" summary=""> <caption> <i id="value:month">Jan</i> <i id="value:year">20XX</i> </caption> <thead> <tr bgcolor="#CCCCCC"> <th><span class="holiday">S</span></th> <th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th> </tr> </thead> <tbody> <tr id="mark:week"> <td><span id="mark:day" class="holiday"> </span></td> <td id="dummy:d1"> </td> <td id="dummy:d2">1</td> <td id="dummy:d3">2</td> <td id="dummy:d4">3</td> <td id="dummy:d5">4</td> <td id="dummy:d6">5</td> </tr> <tr id="dummy:w1"> <td><span class="holiday">6</span></td> <td>7</td><td>8</td><td>9</td> <td>10</td><td>11</td><td>12</td> </tr> <tr id="dummy:w2"> <td><span class="holiday">13</span></td> <td>14</td><td>15</td><td>16</td> <td>17</td><td>18</td><td>19</td> </tr> <tr id="dummy:w3"> <td><span class="holiday">20</span></td> <td>21</td><td>22</td><td>23</td> <td>24</td><td>25</td><td>26</td> </tr> <tr id="dummy:w4"> <td><span class="holiday">27</span></td> <td>28</td><td>29</td><td>30</td> <td>31</td><td> </td><td> </td> </tr> </tbody> </table>
Presentation logic (calendar.plogic) :
:elem(week) :set(day = ' ') :set(wday = 1) :while(wday < first_weekday) :if(wday == 1) @stag :end @cont :set(wday += 1) :end :set(day = 0) :set(wday -= 1) :while(day < num_days) :set(day += 1) :set(wday = wday % 7 + 1) :if(wday == 1) @stag :end @cont :if(wday == 7) @etag :end :end :if(wday != 7) :set(day = ' ') :while(wday != 6) @cont :set(wday += 1) :end @etag :end :end :elem(day) :if(wday == 1) @stag :print(day) @etag :else :print(day) :end :end
Presentation data (calendar-page.html) :
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <style type="text/css"> <!-- .holiday {color:#FF0000;} --> </style> </head> <body> <table border="0" summary=""> <tr id="mark:calendar_list"> <td id="mark:calendar" valign="top"> .... calendar here ... </td> </tr> </table> </body> </html>
Presentation logic (calendar-page.plogic) :
:elem(calendar_list) :set(calendar_ctr = 0) :foreach(calendar = calendar_list) :set(calendar_ctr += 1) :if(calendar_ctr % colnum == 1) @stag :end @cont :if(calendar_ctr % colnum == 0) @etag :end :end :if(calendar_ctr % colnum != 0) :set(calendar = '') :while(calendar_ctr % colnum != 0) @cont :set(calendar_ctr += 1) :end @etag :end :end :elem(calendar) @stag :print(calendar) @etag :end
Main program (calendar-main.php) :
<?php // year, month, number of days, 1st weekday // (or use date() function) $month_data[] = array(2004, 'Jan', 31, 'Thu'); $month_data[] = array(2004, 'Feb', 29, 'Sat'); $month_data[] = array(2004, 'Mar', 31, 'Tue'); $month_data[] = array(2004, 'Apr', 30, 'Thu'); $month_data[] = array(2004, 'May', 31, 'Sat'); $month_data[] = array(2004, 'Jun', 30, 'Tue'); #$month_data[] = array(2004, 'Jul', 31, 'Thu'); #$month_data[] = array(2004, 'Aug', 31, 'Sun'); #$month_data[] = array(2004, 'Sep', 30, 'Wed'); #$month_data[] = array(2004, 'Oct', 31, 'Fri'); #$month_data[] = array(2004, 'Nov', 30, 'Mon'); #$month_data[] = array(2004, 'Dec', 31, 'Wed'); $weekday2num = array( 'Sun'=>1, 'Mon'=>2, 'Tue'=>3, 'Wed'=>4, 'Thu'=>5, 'Fri'=>6, 'Sat'=>7,); // output buffering start ob_start(); // main loop $colnum = 4; foreach($month_data as $data) { $year = $data[0]; $month = $data[1]; $num_days = $data[2]; $first_weekday = $weekday2num[$data[3]]; // include 'calendar.inc' content as string include('calendar.php'); $str = ob_get_contents(); ob_clean(); // append content string into $calendar_list[] $calendar_list[] = $str; } // output buffering stop ob_end_clean(); // include main page, with $calendar_list[] include('calendar-page.php'); ?>
Compile and Execution :
$ kwartz -l php -p calendar.plogic calendar.html > calendar.php $ kwartz -l php -p calendar-page.plogic calendar-page.html > calendar-page.php $ php -q calendar-main.php > calendar-main.html
Tips
Get output as string
It is possible to get result of executing output scrit as string in Ruby or PHP.
Use ruby2 or ERB in Ruby:
## ruby2 s = File.open('file.erb') { |f| f.read() } # or File.read('file.erb') in 1.8 s.untaint _s = '' eval s str = _s # get output as string ## ERB s = File.open('file.erb') { |f| f.read() } # or File.read('file.erb') in 1.8 require 'erb' s.untaint erb = ERB.new(s, $SAFE, '%') str = erb.result(binding()) # get output as string
Use php2 or Output Buffering functions (ob_start(), ob_get_contents(), ob_end_clean()) in PHP:
// php2 <?php include('file.php'); $str = $_s; ?> // use output buffering functions <?php ob_start(); // start output buffering include('file.php'); $str = ob_get_contents(); // get result as string ob_end_clean(); // stop output buffering ?>
Auto-compile with comaring timestamp
Auto-compile is available. Auto-compile is a function to compare each file's timestamp and compile it if needed. Compilation will be done when:
- Output script file does not exist.
- Presentation data file is newer than output script file.
- Presentation logic file exists and it is newer than output script file.
Sorry, kwartz is implemented only in Ruby, so auto-compile function is available only for Ruby programmer.
You can use auto-compile with Kwartz::compile()
.
Here is a sample of main program:
pdata = 'sample.html' # presentation data file script = 'sample.rhtml' # output script file plogic = 'sample.plogic' # presentation logic file lang = 'eruby' # target language require 'kwartz' Kwartz::compile(pdata, script, plogic, lang) requre 'eruby' ERuby::import(script)
checked, selected, disabled
A function to output 'checked="checked"', 'selected="selected"' or 'disabled="disabled"' easily for HTML/XHTML is added in Kwartz.
-
#{@CHECK(condition)}#
or#{@C(condition)}#
-
Prints '
checked="checked"
' ifcondition
is true -
#{@SELECT(condition)}#
or#{@S(condition)}#
-
Prints '
selected="selected"
' ifcondition
is true -
#{@DISABLE(condition)}#
or#{@D(condition)}#
-
Prints '
disabled="disabled"
' ifcondition
is true
For example, see the following presentation data:
<input type="radio" name="gender" value="M" #{gender=='M' ? 'checked="checked"' : ''}#>Man <option name="lang" value="ruby" #{lang=='ruby' ? 'selected="selected"' : ''}#>Ruby <input type="radio" name="os" value="win" #{os=='mac' ? 'disabled="disabled"' : ''}#>Windows
You can write the above presentation data more simply like this:
<input type="radio" name="gender" value="M" #{@C(gender=='M')}#>Man <option name="lang" value="ruby" #{@S(lang=='ruby')}#>Ruby <input type="radio" name="os" value="win" #{@D(os=='mac')}#>Windows
Kwartz will convert it into the following intermediate code:
:print("<input type=\"radio\" name=\"gender\" value=\"M\"\n") :print(" ", @C(gender=='M'), ">Man\n") :print("<option name=\"lang\" value=\"ruby\"\n") :print(" ", @S(lang=='ruby'), ">Ruby\n") :print("<input type=\"radio\" name=\"os\" value=\"win\"\n") :print(" ", @D(os=='mac'), ">Windows\n")
Note: The function is experimental. It may be changed or deleted in the future.
Enable eRuby code in presentation logic
Command line option --enable_eruby=true
enables eRuby code appeared in presentation logic.
'%' notation is available and you can refer target language name by variable '__lang__' in eRuby code.
Presentation logic:
:set(message = 'a sample message') :set(number = 20) % if __lang__ == 'php' ::: <?php $str = sprintf('(%03d) %s', $number, $message); ?> % elsif __lang__ == 'ruby' ::: s = '(%03d) %s' % [number, message] % elsif __lang__ == 'erb' :::% str = '(%03d) %s' % [number, message] % elsif __lang__ == 'eruby' ::: <<% %>% str = '(%03d) %s' % [number, message] %<% %>> % else :set(s = '(' .+ number .+ ') ' .+ message) % end :print(s, "\n")
Output script:
### Ruby message = "a sample message" number = 20 s = '(%03d) %s' % [number, message] print s, "\n" ### PHP <?php $message = 'a sample message'; ?> <?php $number = 20; ?> <?php $str = sprintf('(%03d) %s', $number, $message); ?> <?php echo $s; ?> ### JSP + JSTL <c:set var="message" value="a sample message"/> <c:set var="number" value="20"/> <c:set var="s" value="${'('}${number}${') '}${message}"/> <c:out value="${s}" escapeXml="false"/> ### eRuby <% message = 'a sample message' %><% number = 20 %> <% str = '(%03d) %s' % [number, message] %> <%= s %> ### ERB % message = 'a sample message' % number = 20 % str = '(%03d) %s' % [number, message] <%= s %> ### Velocity #set ($message = 'a sample message') #set ($number = 20) #set ($s = "(${number}) ${message}") $!{s}
eRuby code is executed with ERB. If you are using Ruby 1.8.X or later, you don't need to install ERB because it is bundled with Ruby 1.8.X. If you are using Ruby 1.6.X, you have to install ERB.
Environment variable KWARTZ_OPTIONS
You can specify kwartz command line options at an environment variable 'KWARTZ_OPTIONS'.
For example, if you are using ERB as a default target language and auto-sanitizing, set the environment variable like this:
$ export KWARTZ_OPTIONS='-l erb -s' # sh, bash $ setenv KWARTZ_OPTIONS '-l erb -s' # csh, tcsh
Value of the environment variable is overwrited by actual command-line options. For example, when you have done the above setting and specified a command-line option '-l eruby', kwartz will output a script for eRuby, not ERB.
Note: kwartz command parses a value of the environment variable by String#split(' '). Therefore, kwartz command cannot parse a complex variable of the enviroment variable correctly.
Compile template as method of Ruby/PHP
Using 'mkmethod' script, you can compile template and define it as method or module of Ruby.
Usage of mkmethod is here:
bash$ mkmethod -h Usage: mkmethod [-svh] [-p file] [-l lang] [-M module] [-m method] file.html -p file : presentation logic file -l lang : ruby/ruby2/eruby/erb/php/php2 (default 'ruby2') -M module/class : module/class name (default none) -m method : method name (default 'expand_' + file) -A arg1,arg2,... : method arguments (default nil) -s : sanitizing (equals to '--escape=true') -h, --help : print help and exit -v : print version and exit --optname=value : options for kwartz
Command line option '-l ruby', '-l eruby' or '-l php' creates a method which prints result to standard output, '-l ruby2', '-l erb' or '-l php2' creates a method which returns output as a string. Default is 'ruby2'.
bash$ mkmethod -p hoge.plogic hoge.html def expand_hoge(_args) user = _args[:user] list = _args[:list] _s = '' _s << "Hello " << (user).to_s << "!\n" _s << "<ul>\n" for item in list do _s << " <li>" << (item).to_s << "</li>\n" end _s << "</ul>\n" return _s end bash$ mkmethod -l php2 -p hoge.plogic hoge.html <?php function expand_hoge(&$_args) { $user = &$_args['user']; $list = &$_args['list']; ob_start(); ?> Hello <?php echo $user; ?>! <ul> <?php foreach ($list as $item) { ?> <li><?php echo $item; ?></li> <?php } ?> </ul> <?php $_s = ob_get_contents(); ob_end_clean(); return $_s; } // function end ?>
Command line option '-M' enables you to specify module name in Ruby or class name in PHP.
bash$ mkmethod -p hoge.plogic -M Hoge hoge.html module Hoge def self.expand_hoge(_args) user = _args[:user] list = _args[:list] _s = '' _s << "Hello " << (user).to_s << "!\n" _s << "<ul>\n" for item in list do _s << " <li>" << (item).to_s << "</li>\n" end _s << "</ul>\n" return _s end end bash$ mkmethod -l php2 -p hoge.plogic -M Hoge hoge.html <?php class Hoge { function expand_hoge(&$_args) { $user = &$_args['user']; $list = &$_args['list']; ob_start(); ?> Hello <?php echo $user; ?>! <ul> <?php foreach ($list as $item) { ?> <li><?php echo $item; ?></li> <?php } ?> </ul> <?php $_s = ob_get_contents(); ob_end_clean(); return $_s; } // function end } // class end ?>
Script 'mkmethod' detects global variables of template automatically using Analyze function of Kwartz. You can specify method arguments with command line option '-A'.
bash$ mkmethod -p hoge.plogic -M Hoge -A 'user,list' hoge.html module Hoge def self.expand_hoge(_args) user = _args[:user] list = _args[:list] return self._expand_hoge(user, list) end def self._expand_hoge(user, list) _s = '' _s << "Hello " << (user).to_s << "!\n" _s << "<ul>\n" for item in list do _s << " <li>" << (item).to_s << "</li>\n" end _s << "</ul>\n" return _s end end bash$ mkmethod -l php2 -p hoge.plogic -M Hoge -A 'user,list' hoge.html <?php class Hoge { function expand_hoge(&$_args) { $user = &$_args['user']; $list = &$_args['list']; return Hoge::_expand_hoge($user, $list); } // function end function _expand_hoge(&$user, &$list) { ob_start(); ?> Hello <?php echo $user; ?>! <ul> <?php foreach ($list as $item) { ?> <li><?php echo $item; ?></li> <?php } ?> </ul> <?php $_s = ob_get_contents(); ob_end_clean(); return $_s; } // function end } // class end ?>
Command line option for sanitizing '-s' or '--escape=true' are also avairable.
Use W3C Markup Validation Service
W3C provides Markup Validation service (http://validator.w3.org). You will get errors when you tried it with Kwartz HTML template (presentation data file) because of 'kd' attribute.
There are nice PHP/CGI scripts (validator.php and validator.cgi) in Kwartz archive. They delete 'kd' attribtes from a HTML file and pass it to W3C Markup Validation Service. Put them at your web server and try it. Or you can try it at Kwartz website.
Avoid same variable name as keywords
If you are using Ruby or JSP, that is, languages which doesn't require '$' or other prefix for variable, avoid same variable name as keywords of the language. And also avoid the same name as built-in variables or build-in functions.
If you use a same name as keywords or build-in variables, you'll get strange errors.
For example, the following are keywords in Ruby.
BEGIN class ensure nil self when END def false not super while alias defined? for or then yield and do if redo true begin else in rescue undef break elsif module retry unless case end next return until
JSP(and JSTL's Expression Lanugage) have the following built-in variables or keywords (see JavaServer Pages(TM) Standard Tag Library Specification (Final Release) for detail).
and or not eq ne lt le gt ge div mod true false null empty page pageScope request requestScope session sessionScope application applicationScope header headerValues param paramValues cookie
PHP user doesn't need to wonder such problem because PHP needs to use variable prefix '$', but if you may change programming language from PHP to Java or other, you should remind this problem.
Use name 'param' as a variable for HTTP request parameters
URL Parameter variable is named 'param
' in JSTL's Expression Language.
You should also use name 'param
' for parameters in Ruby or PHP for portability of presentation layer.
For example, the following HTML page exists:
<form action="/cgi/form.cgi" method="post"> User name: <input type="text" name="user"><br> Age: <input type="text" name="age"><br> <input type="submit"> </form>
You should create the following presentation data.
Confirmation: User name:<span id="value:param['user']">Foobar</span><br> Age: <span id="value:param['age']">99</span><br>
If you need portability of presentation data and logic, you should use name 'param
' as form parameters variable in all languages.
For example, in Ruby:
require 'cgi' cgi = CGI.new param = {} cgi.params.each do |key,value| param[key] = value.first end .... s = File.open('form.rb') { |f| f.read } s.untaint eval s ## or ERuby::import('form.rhtml'), etc..
For example, in PHP:
<?php $param = &$_REQUEST; .... include('form.php'); ?>
By the same reason, you should use 'cookie
' as cookie variable, and use 'session
' as session variable.
However, JSP doesn't allow us to change request values of parameters
(*9).
For example, you can write a code such as $_POST['user'] = trim($_POST['user']);
in PHP,
but you cannot in JSP.
If you want avoid this restriction, you should set request parameters in a HashMap object and name it other name but 'param
'.
// Servlet Example public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Get HTTP request parameters and set them into HashMap object. String[] names = { 'name', 'age' }; for (int i = 0; i < names.length; i++) { String key = names[i]; String value = (String)request.getParameter(key); if (value != null) { value = value.trim(); // trim an extra space params.put(key, value); } } // Regist HashMap object with a name 'params'. // Use 'params' instead of 'param' in JSP and JSTL. request.setAttribute("params", params); // Forward to JSP String filename = "form.jsp"; String url = filename; request.getRequestDispatcher(url).forward(request, response); }
- (*9)
- It is because that there is getParameter() method in HttpServletRequest but not setParameter() method.
Allow only logical expression as conditional expresion
It is very different in each programming language how to interpret conditional expression of if-statement or conditional operator. Therefore, you should allow only logical expression in conditional expression.
For example, see the following presentation data:
<font id="if:error" color="red"> ERROR! : #{error}# </font>
In this case, programming languages takes each own ointerpretation of value of error
as conditional expression.
value of error |
Ruby | PHP | JSP(JSTL) | Velocity |
---|---|---|---|---|
empty string ('') | True | False | False | True |
string (not empty) | True | True | False | True |
zero | True | False | False | True |
number (not zero) | True | True | False | True |
true | True | True | True | True |
false | False | False | False | False |
null | False | False | False | False |
If you want to make templates portable, you should describe only logical expressin in conditional expression.
For example, ':if(error)
' should be ':if(error!=null)
' or ':if(error!='')
',
and ':if(!error)
' should be ':if(error==null)
' or ':if(error=='')
'.
If you want to check if string is null or empty, use keyword 'empty'. See empty for detail about keyword 'empty'.
Appendix
Appendix1: kwartz command
Script 'kwartz' is a commad-line script to convert, translate and compile a HTML template into outptu script.
You remember these terms? OK, I'll show you again.
- Convert
- Transform presentation data into intermediate code.
- Translate
- Merge intermediate code and presentation logic, and transform them into output script.
- Compile
- Transform presentation data and presentation logic into output script. 'Compile' means 'convert and traslate'.
And here is the usage of 'kwartz' script.
Usage:
-
kwartz [options...] [-p plogic-file] file.html > file.output
-
Process one file (normal mode).
-
kwartz -O outfile-suffix [-P plogic-suffix] [options...] *.html
-
Process each files (batch mode). You must specify suffix of output script file with option -O. And you may specify suffix of presentation logic file with option -P.
Options:
- -h, --help
- Help.
- -v
- Version information.
- -a action
- Action to do. action may be 'convert', 'translate', 'compile'(default) or 'analyze'.
- -l lang
- Target language of output script. lang may be ruby, ruby2, php, php2, jsp, eruby, erb, erbscan, velocity.
- -s
-
Make auto-sanitizing enabled. All language but velocity can be sanitized.
This option is equal to
--escape=true
.
- -p plogic-file
-
Filename of presentation logic. It is possible to specify several filename using '
,
' as a separator.
- -O outfile-suffix
- Suffix of output file. Kwartz will be 'batch mode' when option -O is specified.
- -P plogic-suffix
- Suffix of presentation logic file. Used with batch-mode.
- -T
- Compare timestamp. If output script file is newer than input, kwartz will do nothing. Used with batch-mode.
- -S
- Suppress messages. Used with batch-mode.
- --attr_name=name
- Attribute name used as a directive. Default is 'kd'.
- --charset=charset
-
Character set. kwartz will print '
<%@ page contentType="text/html; charset=charset" %>
' in JSP.
- --delete_idattr=true|false
-
Delete
id="name"
from presentation data.
- --enable_eruby=true|false
-
Use eRuby as a preprocessor for presentation logic. '%' notation is available.
Variable '
__lang__
' represents target language name. It needs ERB to be installed.
- --escape=true|false
- Auto-sanitizing.
- --even_value=value
-
Even value. Default is
'even'
. Directive FOREACH and LIST will use this value.
- --footer=string
- Footer string.
- --header=string
-
Header string.
'
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
' is used as default in JSP. If you don't want to print any header text, use--header=''
.
- --include_path=dir1,dir2],...
-
Specify directories from which '
include
' directive includes.
- --load_path=dir1,dir2],...
-
Specify directories from which '
load
' directive or:load()
statement loads.
- --odd_value=value
-
Odd value. Default is
'odd'
. Directive FOREACH and LIST will use this value.
Example:
- Compile presentation data file 'file1.html' and get an output script 'file1.rb'.
The target language is Ruby.
$ kwartz -l ruby file1.html > file1.rb or $ kwartz -l ruby -O .rb file1.html
- Compile presentation data file 'file1.html' and a presentation logic file 'file1.plogic', and get an output script 'file1.php'.
The target language is PHP.
$ kwartz -l php -p file1.plogic file1.html > file1.php or $ kwartz -l php -p file1.plogic -O .php file1.html or $ kwartz -l php -O .php -P .plogic file1.html
- Batch mode; Compile each presentation data file and presentaion logic file into output script(*.jsp) with charset EUC-JP.
The target language is JSP.
$ kwartz -l jsp -O .jsp -P .plogic --charset=EUC-JP *.html
- Convert presentation data into intermediate code.
$ kwartz -a convert file1.html | more
appendix2: BNF of PL(Presentation Language)
(undocumented)