diff --git a/collects/web-server/default-web-root/htdocs/servlets/examples/static.html b/collects/web-server/default-web-root/htdocs/servlets/examples/static.html new file mode 100644 index 0000000000..b211384e7a --- /dev/null +++ b/collects/web-server/default-web-root/htdocs/servlets/examples/static.html @@ -0,0 +1,11 @@ + + Title + + + + + + +
Example, Mr.example@"@"foo.com
+ + diff --git a/collects/web-server/default-web-root/htdocs/servlets/examples/template-full.ss b/collects/web-server/default-web-root/htdocs/servlets/examples/template-full.ss new file mode 100644 index 0000000000..5643aab6ad --- /dev/null +++ b/collects/web-server/default-web-root/htdocs/servlets/examples/template-full.ss @@ -0,0 +1,13 @@ +#lang scheme +(require web-server/templates + web-server/http) +(provide (all-defined-out)) +(define interface-version 'v1) +(define timeout +inf.0) + +(define (start initial-request) + (make-response/full + 200 "Okay" + (current-seconds) TEXT/HTML-MIME-TYPE + empty + (list (include-template "static.html")))) \ No newline at end of file diff --git a/collects/web-server/default-web-root/htdocs/servlets/examples/template-simple.ss b/collects/web-server/default-web-root/htdocs/servlets/examples/template-simple.ss new file mode 100644 index 0000000000..61ef233ab8 --- /dev/null +++ b/collects/web-server/default-web-root/htdocs/servlets/examples/template-simple.ss @@ -0,0 +1,8 @@ +#lang scheme +(require web-server/templates) +(provide (all-defined-out)) +(define interface-version 'v1) +(define timeout +inf.0) + +(define (start initial-request) + (list #"text/html" (include-template "static.html"))) \ No newline at end of file diff --git a/collects/web-server/default-web-root/htdocs/servlets/examples/template-xexpr.ss b/collects/web-server/default-web-root/htdocs/servlets/examples/template-xexpr.ss new file mode 100644 index 0000000000..28407c62ca --- /dev/null +++ b/collects/web-server/default-web-root/htdocs/servlets/examples/template-xexpr.ss @@ -0,0 +1,11 @@ +#lang scheme +(require web-server/templates + xml) +(provide (all-defined-out)) +(define interface-version 'v1) +(define timeout +inf.0) + +(define (start initial-request) + `(html (pre ,(include-template "static.html")) + "versus" + ,(make-cdata #f #f (include-template "static.html")))) \ No newline at end of file diff --git a/collects/web-server/scribblings/templates.scrbl b/collects/web-server/scribblings/templates.scrbl new file mode 100644 index 0000000000..a42b233b04 --- /dev/null +++ b/collects/web-server/scribblings/templates.scrbl @@ -0,0 +1,228 @@ +#lang scribble/doc +@(require "web-server.ss") +@(require (for-label web-server/servlet + web-server/templates + scheme/list + xml)) + +@(define xexpr @tech[#:doc '(lib "xml/xml.scrbl")]{X-expression}) +@(define at-reader-ref @secref[#:doc '(lib "scribblings/scribble/scribble.scrbl")]{reader}) + +@title[#:tag "templates"]{Templates} + +@defmodule[web-server/templates] + +The @web-server provides a powerful Web template system for separating the presentation logic of a Web application +and enabling non-programmers to contribute to PLT-based Web applications. + +@local-table-of-contents[] + +@section{Static} + +Suppose we have a file @filepath{static.html} with the contents: +@verbatim[#:indent 2]|{ + + Fastest Templates in the West! + +

Bang!

+

Bang!

+ + +}| + +If we write the following in our code: +@schemeblock[ + (include-template "static.html") +] + +Then the contents of @filepath{static.html} will be read @emph{at compile time} and compiled into a +Scheme program that returns the contents of @filepath{static.html} as a string: +@schemeblock[ + "\n Fastest Templates in the West!\n \n

Bang!

\n

Bang!

\n \n" +] + +@section{Dynamic} + +@scheme[include-template] gives the template access to the @emph{complete lexical context} of the including program. This context can be +accessed via the @at-reader-ref syntax. For example, if @filepath{simple.html} contains: +@verbatim[#:indent 2]|{ + + Fastest @thing in the West! + +

Bang!

+

Bang!

+ + +}| + +Then +@schemeblock[ + (let ([thing "Templates"]) + (include-template "simple.html")) +] +evaluates to the same content as the static example. + +There is no constraints on the values, the way they are used, or the way they are defined, that are made accessible to the template. +For example, +@schemeblock[ + (define (fast-template thing) + (include-template "simple.html")) + + (fast-template "Templates") + (fast-template "Noodles") +] +evalutes to two strings with the predictable contents: +@verbatim[#:indent 2]|{ + + Fastest Templates in the West! + +

Bang!

+

Bang!

+ + +}| + +and + +@verbatim[#:indent 2]|{ + + Fastest Noodles in the West! + +

Bang!

+

Bang!

+ + +}| + +@section{Gotchas} + +One of the most important things to remember about the @at-reader-ref syntax is that the @"@" symbol must be escaped in content: +@verbatim[#:indent 2]|{ + + Fastest @"@"s in the West! + +

Bang!

+

Bang!

+ + +}| + +The other gotcha is that since the template is compiled into a Scheme program, only its results will be printed. For example, suppose +we have the template: +@verbatim[#:indent 2]|{ + + @for[([c clients])]{ + + } +
@(car c), @(cdr c)
+}| + +If this is included in a lexical context with @scheme[clients] bound to @scheme[(list (cons "Young" "Brigham") (cons "Smith" "Joseph"))], +then the template will be printed as: +@verbatim[#:indent 2]|{ + +
+}| +because @scheme[for] does not return the value of the body. +Suppose that we change the template to use @scheme[for/list] (which combines them into a list): +@verbatim[#:indent 2]|{ + + @for/list[([c clients])]{ + + } +
@(car c), @(cdr c)
+}| + +Now the result is: +@verbatim[#:indent 2]|{ + + + +
+}| +because only the final expression of the body of the @scheme[for/list] is included in the result. We can capture all the sub-expressions +by using @scheme[list] in the body: +@verbatim[#:indent 2]|{ + + @for/list[([c clients])]{ + @list{ + + } + } +
@(car c), @(cdr c)
+}| +Now the result is: +@verbatim[#:indent 2]|{ + + + +
Young, Brigham
Smith, Joseph
+}| + +The templating library provides a syntactic form to deal with this issue for you called @scheme[in]: +@verbatim[#:indent 2]|{ + + @in[c clients]{ + + } +
@(car c), @(cdr c)
+}| +Notice how it also avoids the absurd amount of punctuation on line two. + +@section{HTTP Responses} + +The quickest way to generate an HTTP response from a template is using the @scheme[list] response type: +@schemeblock[ + (list #"text/html" (include-template "static.html")) +] + +If you want more control then you can generate a @scheme[response/full] struct: +@schemeblock[ + (make-response/full + 200 "Okay" + (current-seconds) TEXT/HTML-MIME-TYPE + empty + (list (include-template "static.html"))) +] + +Finally, if you want to include the contents of a template inside a larger @xexpr : +@schemeblock[ + `(html ,(include-template "static.html")) +] +will result in the literal string being included (and entity-escaped). If you actually want +the template to be unescaped, then create a @scheme[cdata] structure: +@schemeblock[ + `(html ,(make-cdata #f #f (include-template "static.html"))) +] + +@section{API Details} + +@defform[(include-template path)]{ + Compiles the template at @scheme[path] using the @at-reader-ref syntax within the enclosing lexical context. + + Example: + @schemeblock[ + (include-template "static.html") + ] +} + +@defform[(in x xs e ...)]{ + Expands into + @schemeblock[ + (for/list ([x xs]) + (list e ...)) + ] + + Template Example: + @verbatim[#:indent 2]|{ + @in[c clients]{ + @(car c), @(cdr c) + } + }| + + Scheme Example: + @schemeblock[ + (in c clients "" (car c) ", " (cdr c) "") + ] +} + \ No newline at end of file diff --git a/collects/web-server/scribblings/writing.scrbl b/collects/web-server/scribblings/writing.scrbl index 56b08917a6..2f3de6e8ef 100644 --- a/collects/web-server/scribblings/writing.scrbl +++ b/collects/web-server/scribblings/writing.scrbl @@ -165,4 +165,5 @@ things in the Web Language, they are sensitive to source code modification. @; ------------------------------------------------------------ @include-section["formlets.scrbl"] +@include-section["templates.scrbl"] @include-section["managers.scrbl"] diff --git a/collects/web-server/template/examples/basic.html b/collects/web-server/template/examples/basic.html index c8104be091..ed0197c19b 100644 --- a/collects/web-server/template/examples/basic.html +++ b/collects/web-server/template/examples/basic.html @@ -2,13 +2,11 @@ @title - @in[c @clients]{ - @list{ + @in[c clients]{ - } }
@(client-surname c), @(client-firstname c) @(client-email c)
diff --git a/collects/web-server/template/examples/run.ss b/collects/web-server/template/examples/run.ss index 79ec4e4f5a..ec85009b99 100644 --- a/collects/web-server/template/examples/run.ss +++ b/collects/web-server/template/examples/run.ss @@ -1,33 +1,5 @@ #lang scheme -(require xml - scribble/text - scheme/port) - -(define-syntax include-template - (syntax-rules () - [(_ p) - (with-output-to-string - (lambda () - (output (include/text p))))])) - -(define-syntax include-template/xexpr - (syntax-rules () - [(_ p) - (string->xexpr (include-template p))])) - -(define (string->xexpr s) - (with-input-from-string - s - (lambda () - (xml->xexpr (document-element (read-xml)))))) - -(define-syntax in - (syntax-rules () - [(_ x xs e ...) - (for/list ([x xs]) - e ...)])) - -; Examples +(require web-server/templates) (include-template "static.html") diff --git a/collects/web-server/templates.ss b/collects/web-server/templates.ss new file mode 100644 index 0000000000..5ee1b8798f --- /dev/null +++ b/collects/web-server/templates.ss @@ -0,0 +1,31 @@ +#lang scheme +(require xml + scribble/text + scheme/port) + +(define-syntax include-template + (syntax-rules () + [(_ p) + (with-output-to-string + (lambda () + (output (include/text p))))])) + +(define-syntax include-template/xexpr + (syntax-rules () + [(_ p) + (string->xexpr (include-template p))])) + +(define (string->xexpr s) + (with-input-from-string + s + (lambda () + (xml->xexpr (document-element (read-xml)))))) + +(define-syntax in + (syntax-rules () + [(_ x xs e ...) + (for/list ([x xs]) + (list e ...))])) + +(provide include-template + in) \ No newline at end of file