Only in html-template.mine/: api.fasl diff -ur html-template-0.7.0/doc/index.html html-template.mine/doc/index.html --- html-template-0.7.0/doc/index.html 2006-09-30 00:36:28.000000000 +0200 +++ html-template.mine/doc/index.html 2006-12-11 04:16:53.000000000 +0100 @@ -31,10 +31,9 @@ for.)
It is loosely modeled after the Perl module HTML::Template and compatible -with a subset of its syntax, i.e. it should be possible to use your -HTML-TEMPLATE templates with HTML::Template as well (but usually not -the other way around). +href="http://html-template.sf.net/">HTML::Template and partially +compatible with a its syntax, though both libraries contain some +extensions that the other does not support.
HTML-TEMPLATE translates templates into efficient closures which can be re-used as often as needed. It uses an intelligent cache @@ -57,9 +56,9 @@ href="http://www.cliki.net/Lisp%20Markup%20Languages">Lisp markup languages but found that HTML::Template's approach usually works best for me: The graphical designers only need to learn a minimal set -of new tags (three of them) and can update their templates -independently from the work done on the backend. It is simple and it -just works. YMMV, of course... +of new tags and can update their templates independently from the work +done on the backend. It is simple and it just works. YMMV, of +course...
HTML-TEMPLATE is intended to be portable and should work with all conforming Common Lisp implementations but is mainly tested and @@ -111,6 +110,8 @@
*template-symbol-package*
*force-default*
*value-access-function*
+ *call-template-access-function*
+ *call-value-access-function*
*ignore-empty-lines*
*warn-on-creation*
@@ -398,13 +399,13 @@
where name is one of TMPL_VAR
,
-TMPL_LOOP
, TMPL_REPEAT
, TMPL_IF
, TMPL_UNLESS
,
+TMPL_LOOP
, TMPL_REPEAT
, TMPL_CALL
, TMPL_IF
, TMPL_UNLESS
,
TMPL_INCLUDE
, /TMPL_LOOP
, /TMPL_REPEAT
,
/TMPL_IF
, /TMPL_UNLESS
, or TMPL_ELSE
. Case doesn't matter,
i.e. tmpl_var
or Tmpl_Var
would also be
legal names.
-If name is one of the first four listed above then
+If name is one of the first seven listed above then
attribute must follow, otherwise it must not follow where
attribute is any sequence of characters delimited by
"
, '
, or by whitespace. There's
@@ -465,10 +466,10 @@
"NAME=
" notation which is not supported by
HTML-TEMPLATE.
-The TMPL_VAR
and TMPL_INCLUDE
tags can
-appear anywhere and as often as you like in your templates while the
-other tags must obey certain rules - they must follow one of these
-patterns
+The TMPL_VAR
, TMPL_INCLUDE
, and
+TMPL_CALL
tags can appear anywhere and as often as you
+like in your templates while the other tags must obey certain rules -
+they must follow one of these patterns
<!-- TMPL_IF attribute --> text <!-- /TMPL_IF --> @@ -520,7 +521,7 @@ to fill and print a template.Each of the template tags
TMPL_VAR
,TMPL_IF
,TMPL_UNLESS
, -TMPL_LOOP
, andTMPL_REPEAT
is associated with a particular symbol at +TMPL_LOOP
,TMPL_CALL
, andTMPL_REPEAT
is associated with a particular symbol at generation time. This symbol is the result ofINTERN
ing the tag's attribute string into the package*TEMPLATE-SYMBOL-PACKAGE*
. The @@ -687,6 +688,50 @@ including printer. +
<!-- TMPL_CALL symbol -->
+ ++The value associated with symbol should be a sequence (as +specified by+*SEQUENCES-ARE-LISTS*
) +of template calls, each of which specifies a substructure and a +template to apply to that. By default, calls are just lists, with the +CAR specifying the template name and the CDR containing the +substructure. (See*CALL-TEMPLATE-ACCESS-FUNCTION*
+and*CALL-VALUE-ACCESS-FUNCTION*
+for ways to customize what calls look like.) ++
TMPL_CALL
combines aspects ofTMPL_LOOP
and +TMPL_INCLUDE
, it iterates over a sequence of values the +way loops do, but instead of using part of the current template to +print the values each value contains its own information about which +subtemplate should be applied to it. + ++* (with-open-file (s "/tmp/paragraph" :direction :output :if-exists :supersede) + (write-string "<p class='fancy'><!-- TMPL_VAR text --></p>" s)) +"<p class='fancy'><!-- TMPL_VAR text --></p>" +* (with-open-file (s "/tmp/header" :direction :output :if-exists :supersede) + (write-string "<h1><!-- TMPL_VAR text --></h1>" s)) +"<h1><!-- TMPL_VAR text --></h1>" +* (fill-and-print-template "<body><!-- TMPL_CALL parts --></body>" + '(:parts ((#P"/tmp/header" :text "Chapter 1") + (#P"/tmp/paragraph" :text "There once was a platypus...") + (#P"/tmp/header" :text "Chapter 5") + (#P"/tmp/paragraph" :text "And lived happily ever after.")))) +<h1>Chapter 1</h1><p class='fancy'>There once was a platypus...</p><h1>Chapter 5</h1><p class='fancy'>And lived happily ever after.</p></body> +++Note that you do not have to include full pathnames in the call +structures. You can use
*DEFAULT-TEMPLATE-PATHNAME*
+to specify most of it, or set*CALL-TEMPLATE-ACCESS-FUNCTION*
+to a function that creates pathnames any way you like. +
The HTML-TEMPLATE dictionary
HTML-TEMPLATE exports the following symbols (some of which are also @@ -1037,7 +1082,7 @@ list(symbol values &optional in-loop-p)
which is used to associate symbols with their values when a template printer is invoked.in-loop-p
is true whenever this -function is called from within aTMPL_LOOP
tag. +function is called from within aTMPL_LOOP
orTMPL_CALL
tag.The default value is @@ -1068,6 +1113,35 @@
[Special variable] +
*call-template-access-function* + ++ +
The value of this variable should be a designator +for a function which takes one argument (the call structure) and +returns either a template printer or a value that can be used as the +first argument tocreate-template-printer
. +This function will be used to determine the template that should be +used for a call in aTMPL_CALL
tag. +The default value +is #'
CAR
. +This variable takes effect at invocation +time.
[Special variable] +
*call-value-access-function* + ++ +
The value of this variable should be a designator +for a function which takes one argument (the call structure) and +returns a structure to use as the value for a call in a +TMPL_CALL
tag.The default value is #'
CDR
. +This variable takes effect at invocation +time.
[Special variable]
*ignore-empty-lines*
Only in html-template.mine/: errors.fasl Only in html-template.mine/: packages.fasl diff -ur html-template-0.7.0/packages.lisp html-template.mine/packages.lisp --- html-template-0.7.0/packages.lisp 2006-09-14 13:44:31.000000000 +0200 +++ html-template.mine/packages.lisp 2006-11-19 05:50:14.000000000 +0100 @@ -32,7 +32,9 @@ (defpackage :html-template (:nicknames :template) (:use :cl) - (:export :*convert-nil-to-empty-string* + (:export :*call-template-access-function* + :*call-values-access-function* + :*convert-nil-to-empty-string* :*default-template-output* :*default-template-pathname* :*escape-char-p* Only in html-template.mine/: specials.fasl diff -ur html-template-0.7.0/specials.lisp html-template.mine/specials.lisp --- html-template-0.7.0/specials.lisp 2006-09-14 16:02:00.000000000 +0200 +++ html-template.mine/specials.lisp 2006-12-11 03:59:26.000000000 +0100 @@ -105,6 +105,14 @@ "The function which associates \(attribute) symbols with their values.") +(defvar *call-template-access-function* #'car + "Accessor function for extracting the called template from a +TMPL_CALL form.") + +(defvar *call-value-access-function* #'cdr + "Accessor function for extracting the values from a TMPL_CALL +form.") + (defvar *force-default* nil "The default value for the FORCE keyword argument to CREATE-TEMPLATE-PRINTER.") Only in html-template.mine/: template.fasl diff -ur html-template-0.7.0/template.lisp html-template.mine/template.lisp --- html-template-0.7.0/template.lisp 2006-09-30 00:36:26.000000000 +0200 +++ html-template.mine/template.lisp 2006-12-11 03:59:26.000000000 +0100 @@ -50,49 +50,54 @@ #+:lispworks (editor:setup-indent "with-use-value-restart" 1 2 4) -(defun create-simple-printer (string-list &optional symbol/pathname (next-fn #'no-values)) - "Used internally to create template printers for TMPL_VAR, -TMPL_INCLUDE, and for strings which don't include template -tags. SYMBOL/PATHNAME is the symbol or pathname associated with the -tag. NEXT-FN is the next function to be called in the chain of -closures. STRING-LIST is a list of strings in reverse order to be -printed first." +(defun create-simple-printer (string-list &optional (next-fn #'no-values)) + "Used internally to create template printers for strings which don't +include template tags. NEXT-FN is the next function to be called in +the chain of closures. STRING-LIST is a list of strings in reverse +order to be printed first." (let ((string (list-to-string string-list))) - (etypecase symbol/pathname - (null - ;; no tag, just print STRING - (lambda (values) - (write-string string *template-output*) - (funcall next-fn values))) - (symbol - ;; TMPL_VAR tag - (lambda (values) - (write-string string *template-output*) - (let* ((value (funcall *value-access-function* symbol/pathname values)) - (string (typecase value - (null - (if *convert-nil-to-empty-string* - "" - (with-use-value-restart (symbol/pathname) - (signal-template-missing-value-error - "Value for symbol ~S is NIL" - symbol/pathname)))) - (string value) - (otherwise - (cond (*format-non-strings* (format nil "~A" value)) - (t (with-use-value-restart (symbol/pathname) - (error 'template-not-a-string-error - :value value - :format-control "Value ~S for symbol ~S is not a string" - :format-arguments (list value symbol/pathname))))))))) - (write-string (funcall *string-modifier* string) *template-output*)) - (funcall next-fn values))) - (pathname - ;; TMPL_INCLUDE tag - (lambda (values) - (write-string string *template-output*) - (funcall (car (gethash symbol/pathname *printer-hash*)) values) - (funcall next-fn values)))))) + (lambda (values) + (write-string string *template-output*) + (funcall next-fn values)))) + +(defun create-var-printer (string-list symbol next-fn) + "Used internally to create template printers for TMPL_VAR. SYMBOL is +the symbol associated with the tag. NEXT-FN is the next function to be +called in the chain of closures. STRING-LIST is a list of strings in +reverse order to be printed first." + (let ((string (list-to-string string-list))) + (lambda (values) + (write-string string *template-output*) + (let* ((value (funcall *value-access-function* symbol values)) + (string (typecase value + (null + (if *convert-nil-to-empty-string* + "" + (with-use-value-restart (symbol) + (signal-template-missing-value-error + "Value for symbol ~S is NIL" + symbol)))) + (string value) + (otherwise + (cond (*format-non-strings* (format nil "~A" value)) + (t (with-use-value-restart (symbol) + (error 'template-not-a-string-error + :value value + :format-control "Value ~S for symbol ~S is not a string" + :format-arguments (list value symbol))))))))) + (write-string (funcall *string-modifier* string) *template-output*)) + (funcall next-fn values)))) + +(defun create-include-printer (string-list pathname next-fn) + "Used internally to create template printers for TMPL_INCLUDE. +PATHNAME is the pathname associated with the tag. NEXT-FN is the next +function to be called in the chain of closures. STRING-LIST is a list +of strings in reverse order to be printed first." + (let ((string (list-to-string string-list))) + (lambda (values) + (write-string string *template-output*) + (funcall (car (gethash pathname *printer-hash*)) values) + (funcall next-fn values)))) (defun create-if-printer (string-list symbol if-fn else-fn next-fn unlessp) "Used internally to create template printers for TMPL_IF and @@ -148,6 +153,34 @@ do (funcall body-fn values)))) (funcall next-fn values)))) +(defun create-call-printer (string-list symbol next-fn) + "Used internally to create template printers for TMPL_CALL tags. +SYMBOL is the symbol associated with the tag. BODY-FN is the template +printer for the body of the loop. NEXT-FN is the next function to be +called in the chain of closures. STRING-LIST is a list of strings in +reverse order to be printed first." + (let ((string (list-to-string string-list))) + (cond (*sequences-are-lists* + (lambda (values) + (write-string string *template-output*) + (dolist (call (funcall *value-access-function* + symbol values t)) + (fill-and-print-template + (funcall *call-template-access-function* call) + (funcall *call-value-access-function* call) + :stream *template-output*)) + (funcall next-fn values))) + (t + (lambda (values) + (write-string string *template-output*) + (loop for call across (funcall *value-access-function* + symbol values t) + do (fill-and-print-template + (funcall *call-template-access-function* call) + (funcall *call-value-access-function* call) + :stream *template-output*)) + (funcall next-fn values)))))) + (defun create-template-printer-aux (string-stack end-token) "Reads from *STANDARD-INPUT* and returns a template printer from what it reads. When this function is entered the stream pointer must @@ -227,10 +260,10 @@ ;; then we combine it with the strings before the tag ;; to create a template printer for TMPL_INCLUDE (values - (create-simple-printer (cons (skip-leading-whitespace string) - string-stack) - merged-pathname - next-fn) + (create-include-printer (cons (skip-leading-whitespace string) + string-stack) + merged-pathname + next-fn) else-follows)))) ((string-equal token "TMPL_VAR") ;; TMPL_VAR tag - first read the symbol which has to @@ -245,7 +278,7 @@ ;; to create a template printer for TMPL_VAR - note ;; that we don't skip leading and trailing whitespace ;; here - (create-simple-printer (cons string string-stack) + (create-var-printer (cons string string-stack) symbol next-fn) else-follows)))) @@ -283,6 +316,24 @@ body-fn next-fn) else-follows)))) + ((string-equal token "TMPL_CALL") + ;; TMPL_CALL tag - first read the symbol which has to + ;; follow and intern it + (let ((symbol (read-tag-rest :read-attribute t))) + (multiple-value-bind (next-fn else-follows) + ;; recursively create the template printer for the + ;; rest of the stream + (create-template-printer-aux (skip-trailing-whitespace) + end-token) + ;; create the printer that will output the strings + ;; before this tag and call the templates stored under + ;; SYMBOL + (values (funcall #'create-call-printer + (cons (skip-leading-whitespace string) + string-stack) + symbol + next-fn) + else-follows)))) ((string-equal token "/TMPL_LOOP") (unless (eq end-token :loop) ;; check if we expected /TMPL_LOOP here, i.e. if an open Only in html-template.mine/: test.fasl diff -ur html-template-0.7.0/test.lisp html-template.mine/test.lisp --- html-template-0.7.0/test.lisp 2006-09-30 00:36:26.000000000 +0200 +++ html-template.mine/test.lisp 2006-12-11 04:30:06.000000000 +0100 @@ -121,6 +121,12 @@ (test "2" "1234" '(:foo t :bar nil)) (test "3" "1234" '(:foo nil :baz t)) (test "4" "1234" '(:foo nil :baz nil)) +(test "X" "" '(:foo (("X")))) +(test "QUUX" "" '(:baz "Q" + :foo (("" :bar "U") + ("X" :bar "U")))) +(test "" "" '(:foo (("---")))) +(test nil "" '(:foo 57)) (let ((temp-name (make-pathname :name (format nil "template-test-~A" (random 1000000)) :defaults tmp-dir))) @@ -213,7 +219,11 @@ "[]" '(:vector #((:item "1") (:item "2") - (:item "3"))))) + (:item "3")))) + (test "QUUX" "" + '(:baz "Q" + :foo #(("" :bar "U") + ("X" :bar "U"))))) (let ((*upcase-attribute-strings* nil)) (test "The slow brown fox" Only in html-template.mine/: util.fasl