This is another request for discussion and vote on the topic of a macro
expansion facility to Velocity. This should be a 'more complete'
definition of the proposal. Currently, code exists to support
everything mentioned in this proposal.
For argumentative fluff on why this is good, why this doesn't break MVC,
etc, etc, please see my last message.
Acronyms Used
=============
VTL := Velocity Template Language - the set of directives and
references that make up the interpreted elements of a .vm template.
VM := Velocimacro - the name given to the facility being proposed.
schmoo := all non-VTL content in a Velocity template. This term is not
used in this proposal. Just hoping to get it into the vernacular. :)
Problem Illustrated Via Use Case / Use Story
============================================
Here is an illustrative example (which is totally contrived) :
Suppose, as uber-designer of a web design group, you wish to require
that in a Human Resources web app, when the picture, name and department
of an employee is shown on any of the pages, it is always shown the
exact same way :
<table>
<tr>
<td>
#if( $picture )
<img src='$picture'>
#else
Pict Unavail
#end
$name
</td>
<td>
<b>Department:</b> $department
</td>
</tr>
</table>
One solution would be the tried and (un)true way : have the designers
copy and paste this VTL/HTML blob anywhere this needs to be in a
template. This would work, until you, the uber-designer wants to change
it, for example making the employee name bold as well. You can guess
what will happen. Basically human visitor-pattern-esque misery, where
someone has to go to each template and fix it, as well as dental work
from all the gnashing of teeth. Another solution would be to use a
#include() directive, which is not only ugly and hard to read, it would
presuppose the $references that are going to be used w/in the template.
Not good either.
Here enters the idea of a Velocimacro. A Velocimacro is simply the
Velocity analogy to the #define found in the C language. To illustrate,
using the example above, the uber-designer would (or delegate to a
minion) define the following Velocimacro :
#macro( emp_pict_dept $name $pict $dept )
<table>
<tr>
<td>
#if( $pict )
<img src='$pict'>
#else
Pict Unavail
#end
$name
</td>
<td>
<b>Department:</b> $dept
</td>
</tr>
</table>
#end
(Ignore for now exactly where it's defined : just know it's in a single
place, and it's pure VTL code - a tempate.)
After this is parsed by the Velocity engine, any template designer may
used the following 'shortcut' to represent the above :
#emp_pict_dept( $employeeName $employeePict $employeeDept )
[Note : I am using references in the useage example that are different
than the references in the definition #macro() to emphasize that the
arguments to #emp_pict_dept() can be any references, not the specific
ones used for the defintion.]
Thus, when the template is processed, the
#emp_pict_dept( $employeeName $employeePict $employeeDept )
found in the designers template will be expanded (literally) in the
template to be
<table>
<tr>
<td>
#if( $employeePict )
<img src='$employeePict'>
#else
Pict Unavail
#end
$employeeName
</td>
<td>
<b>Department:</b> $employeeDept
</td>
</tr>
</table>
and with the output as expected.
So when uber-designer wants a change, it is simply made in one place,
and all templates using the VM will produce the correct output.
Implementation
==============
Currently, I have a working VM package that will drop into the current
source tree in CVS. If this is recieved positively by the listmembers,
it will be integrated by Jason and myself.
The VM facility adds one directive (actually, a 'Pluggable Directive')
to VTL :
#macro( vmname $arg1 $arg2 $arg3 ... $argN)
<macro body>
#end
where
vmname := name used to use the macro in VTL
$arg1..$argN := arguments to the defined macro. yes, its variable.
You can use 0..N of them.
<macro body> := any valid VTL/HTML code. This is the same stuff,
exactly, that you would put in a template.
A Velocimacro CAN NOT override or replace a Velocity directive. This
is not a configurable option - this I think is a Real Bad Thing. Of
course, this is open to discussion, but the idea makes me queasy. :)
Velocmacro Defintion Sources
============================
Currently, Velocimacros may be defined in 3 places. Note that in each
case, the way VM's are defined is the same as that shown above :
1) Global library template file :
This would be a library of useful / example VMs that come with the
Velocity distro. The control of its use and content is through a
property found in velocimacro.properties. Note that there may be a few
things like formatting problems (such as my favorite, $!email) that can
be solved entirely w/in VTL using a VM.
2) Local library template file :
This is a library of VMs defined by the local users. The control of
its use and content is through a property found in
velocimacro.properties.
Note that as the libraries are simply template files, and they are
controlled by the site, the VMs defined in the global and local files
are 'peers' : a local definition may override a global definition. This
sounds wrong at first blush, but if the site really wanted that to be
so, they would simply edit the global file to suit. I am against having
any VM defintions baked into Java code, BTW.
3) Inline w/in a Velocity template :
A designer may define macros w/in their own template, either inline
via the #macro() directive, or through #include() of a template
containing #macro() directive(s). These two cases, the VMs from the
explicit #macro() and #include() are considered to be peers in terms of
control : the administrator has control to either allow or disallow this
method of definition, as well as if they are allowed, preventing them
from overriding VMs defined in the global or local libraries. See
properties.
Current Properties
==================
Currently, the following properties are supported :
velocimacro.library.global := the filename of the global VM template
library. This is a regular .vm file. It can be omitted or blank.
velocimacro.library.local := the filename of the 'local' VM template
library. This is a regular .vm file. It can be omitted or blank.
velocimacro.permissions.allowInline := valid values true or false,
specifies if designers are allowed to define VMs within regular
templates either via #macro() or via #include() of something containing
a #macro(). Default = true
velocimacro.permissions.allowInlineToOverride := valid values true or
false, specifies if inline VMs definitions can replace library specified
VMs. Default = false
Note that allowInlineToOverride does not supercede allowInline:
allowInline must be true before allowInlineToOverride is even
considered.
Thus, it is possible to completely shut off VMs : use no local or global
library, and turn off allowInline. Voila.
Conclusion
==========
There still are tons of things to add, for example, properties
controlling and specifying output from the VM system (such as <!-- VM :
macro foo added -->, <!-- VM : bad macro definition --> and such for
debugging, sanity checks, etc). But I believe the implementation
defined here is feature complete enough to warrant inclusion in the
source tree. It works : if anyone wants to try it, email me for the
current code.
geir
--
Geir Magnusson Jr. [EMAIL PROTECTED]
Dakota tribal wisdom: "when you discover you are riding a dead horse,
the best strategy is to dismount."