Stencl
Stencl is a simple templating library loosely based on BRL (the Beautiful Report Language). It leverages the power of the lisp reader to create a dynamic templating system that is easy to understand and powerful enough for most applications.
Downloading and Installation
The official repository is currently at https://github.com/dlowe-net/stencl .
Download the code, and place the directory with the asd file in
~/quicklisp/local-projects/
or somewhere in your asdf tree.
Writing templates
Here is an example of a stencl template:
<html><head><title>[#{title}]</title><body>
<ul>[(dolist (item #{list})
(out ]
<li>[item]</li>[))]
</ul>
<p>[#{footer}]</p>
</body></html>
and here's how you can get the template output:
(stencl:to-string (stencl:from-string template)
:title "My title"
:list '("alpha" "beta" "gamma" "delta")
:footer "generated by stencl")
and here's the output:
"<html><head><title>My title</title><body>
<ul>
<li>alpha</li>
<li>beta</li>
<li>gamma</li>
<li>delta</li>
</ul>
<p>generated by stencl</p>
</body></html>
"
In this example, there's some html, with some Common Lisp code thrown
in. The code is separated from the data with square brackets ([]
),
there's a stencl function in the middle, and there's a weird #{}
notation. What is going on here?
The OUT
function is what generates the output of the template. It
converts all of its arguments to strings automatically, then appends
them to the output stream of the template. The entire template starts
with an implicit stencl wrapper, so that everything inside is, by
default, a string that is output.
The square brackets are tricky, though. They delimit a string, but
the open bracket ([
) closes a string and the close bracket (]
)
starts a new string. So when you see [(out ]<p>[item]</p>[)]
, this
will produce a form (internally) like (out "<p>" item "</p>")
. This
preserves the clarity of the template with a simple transformation,
and was the central beautiful insight of the BRL language.
The notation of #{foo}
is a stencl innovation, and tells the stencl
library that FOO should be a keyword parameter to the generated
function.
Using Stencl
FROM-STREAM
stream
Generates a stencl function from the template text found on stream. The resulting function may be called with keywords defined inside the template.
FROM-FILE
path
Generates a stencl function from the file at the given pathname.
FROM-STRING
string
Generates a stencl function from a string.
TO-STREAM
stream template&rest
keyword args
Outputs the result of template to the stream, with the given keyword args. Returns the collected results of the template.
TO-FILE
output pathname template&rest
keyword args
Like TO-STREAM
, but outputs to a file, which is created if
necessary. Returns the collected results of the template.
TO-STRING
template&rest
keyword args
Like TO-STREAM
, but outputs to a string. The collected results of
the template are returned as the second value.
FORMAT-TEMPLATE
stream template&rest
keyword args
Generates a stencl function from template, calls it with keyword
args, and outputs it to stream if not NIL
. Outputs to
standard-output if stream is T
. Returns the string generated
and the collected results of the template.
OUT
arguments*
Usually called within a template, the stencl function converts all its arguments to strings and appends each string to the output of the template.
INCLUDE
template [additional or default args]
When found in a template definition, this form includes the template, forwarding all its keyword arguments to the interior template. This may be useful for providing nested templates for layout or other shared structures. The template argument is just another variable which is evaluated at runtime.
COLLECT
values
In a template definition, this form collects values into a list, which
are returned as the result of the template function. This is useful
for passing information calculated in the template back to the caller.
Returned results from included templates are also appended to the
result. COLLECT
is most useful when collecting property lists
(e.g. (:foo "bar" :baz 23)
), but any value can be returned.