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, and TMPL_REPEAT is associated with a particular symbol at +TMPL_LOOP, TMPL_CALL, and TMPL_REPEAT is associated with a particular symbol at generation time. This symbol is the result of INTERNing 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 of TMPL_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 a TMPL_LOOP tag. +function is called from within a TMPL_LOOP or TMPL_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 to create-template-printer. +This function will be used to determine the template that should be +used for a call in a TMPL_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