sunet/doc/latex/surfletapi.tex

1475 lines
62 KiB
TeX
Raw Normal View History

\section{API description}
\label{sec:surflet-api}
The \surflet server comes with an extensive set of modules. This
section describes the modules and the programming interfaces. See the
howto section \ref{sec:surflethowto} for a practical guide. Note that
most of the procedures mentioned here are meant to be called from
within a \surflet.
\subsection{The \surflet server}
The \surflet server provides basic procedures to send web content to a
client. To enable the SUnet webserver to serve \surflets, you have to
add the \surflet handler to it, which resides in the
\exi{surflet-handler} structure. \Eg:
\begin{alltt}
(httpd
(make-httpd-options
\dots
with-request-handler
(alist-path-dispatcher
(list
(cons "surflet" (surflet-handler (with-surflet-path
"web-server/root/surflets"))))
(rooted-file-or-directory-handler "web-server/root/htdocs"))))
\end{alltt}
This will set up the \surflet handler to handle requests directed to
the directory \ex{/surflet/}. The \surflet handler can only handle
requests directed to \surflets. Here's the interface description:
\defun{surflet-handler}{options}{request-handler}
\begin{desc}
This procedures sets up the \surflet handler and returns the request
handler for the SUnet webserver. The options argument is similar to
the one passed to \ex{httpd} and is exlpained below.
The \surflet handler accepts requests (solely) to \surflets whose
file name must have the extension \ex{.scm}. The structure of
\surflets is explained below. The \surflet handler receives the
\ex{request} from \ex{httpd}, translates it to a
\ex{surflet-request} and passes it to the requested \surflet. The
\surflet in turn is expected to return a \ex{surflet-response},
which the \surflet handler translates to a \ex{repsonse} for
\ex{httpd}. Thus, the \surflet deals only with \ex{surflet-request}
and \ex{surflet-response} objects. The structure of these objects
and how they are passed around is explained below.
A \surflet may also return a \ex{redirect} \ex{response}, which the
\surflet handler passes to the \ex{httpd} untouched. See
\ref{sec:http-responses} for details.
The \surflet handler calls the \surflet wrapped into an error
handler that catches any error the \surflet may yield. In this
case, it terminates the \surflets session (see below for more on
sessions) and returns an \ex{error} \ex{response} to \ex{httpd} with
the error code 500 "Internal Server Error" and a description of
the error.
\end{desc}
The \var{options} argument can be constructed in a similar way to the
options argument of \ex{httpd}. The procedures' names are of the form
\ex{with-\ldots} and they all either create a new option or add a
new parameter to a given option. The new parameter is always the
first argument while the (old) option the optional second one. The
following procedures reside in the \exi{surflet-handler/options}
structure.
\defun{with-surflet-path}{surflet-path [options]}{options}
\begin{desc}
This specifies the path in which the \surflet handler looks for
\surflets. The \var{surflet-path} is a string. This option must be
given for the handler to work.
\end{desc}
\defun{with-session-lifetime}{seconds [options]}{options}
\begin{desc}
This specifies the initial lifetime of a session. The lifetime of a
session is the number of seconds the \surflet handler waits for a
response from a client for that session, before she automatically
finishes it. See below for details on sessions. Defaults to 600,
\ie 10 minutes.
\end{desc}
\defun{with-cache-surflets?}{cache-surflets? [options]}{options}
\begin{desc}
This specifices whether the \surflet handler caches \surflets. The
caching of \surflets is a prerequisite for \surflet wide global
variables. See below for details on the scope of variables in
\surflets. Defaults to \sharpt.
\end{desc}
\defun{with-make-session-timeout-text}{timeout-text-procedure
[options]}{options}
\begin{desc}
This specifies a procedure that generates the timeout text. The
\surflet handler displays the timeout text when she receives a
request for a \surflet session that does not exist, either because
the \surflet finished its session, the session has timed out, or the
URL is illformed. The default is an English text with an
explanation of the possible reasons and a link to a new session of
the requested \surflet. \var{timeout-text-procedure} accepts the
string path to the \surflet that was requested and returns a string.
\end{desc}
Similar to the \ex{httpd} options there exists a procedure to avoid
parenthisis:
\defun{make-surflet-options} {transformer value \ldots}{options}
\begin{desc}
This constructs an options value from an argument list of parameter
transformers and parameter values. The arguments come in pairs,
each an option transformer from the list above, and a value for that
parameter. \ex{make-surflet-options} returns the resulting options
value.
\end{desc}
For example,
\begin{alltt}
(surflet-handler
(make-surflet-options
with-surflet-path "root/surflets"
with-session-timeout 3600))
\end{alltt}
defines the \surflet handler to serve \surflets from the directory
\ex{root/surflets} and to timeout unused session after one hour.
The \surflet handler allows runtime read and write access to her
options:
\defun{options-surflet-path}{options}{string}
\defunx{options-session-lifetime}{options}{integer}
\defunx{options-cache-surflets?}{options}{boolean}
\defunx{options-make-session-timeout-text}{options}{procedure}
\begin{desc}
These procedures return the stored value for the respective option.
See above for the description of the options.
\end{desc}
\defun{set-options-surflet-path!}{options surflet-path}{\undefined}
\defunx{set-options-session-lifetime!}{options seconds}{\undefined}
\defunx{set-options-cache-surflets?!}{options cache-surflets?}{\undefined}
\defunx{set-options-make-session-timeout-text!}
{options timeout-text-procedure} {\undefined}
\begin{desc}
These procedures change the respective option value. See above for
the description of the arguments. Note that changing the
\var{surflet-path} within a \surflet may result in the \surflets
being unreachable. Turning the cache off will not empty the
\surflet cache.
\end{desc}
\subsection{\surflets}
Technically, \surflets are Scheme48 structures, that have the name
\ex{surflet} and export a \ex{main} procedure. The file in which their
definition reside must have the extension \ex{.scm}. The \ex{main}
procedure must accept the initial \ex{surflet-request} as an argument.
She may or may not return, but if she does, she must return either a
\ex{surflet response} or a \ex{redirect response}. For example, this
is a valid \surflet definition:
\begin{alltt}
(define-structure surflet surflet-interface
(open scheme-with-scsh
surflets)
(begin
(define (main req)
(send-html/finish
'(html (body (p "Hello world!")))))
))
\end{alltt}
\ex{surflet-interface} is a predefined interface description that
exports the \ex{main} procedure. It is recommended to use this
interface. \exi{surflets} is a structure that combines the most
commonly used structures to write \surflets. \ex{send-html/finish} is
one of the procedures that sends HTML to the client. More on this
below.
As \surflets are Scheme48 structures, you can use all capabilities of
the Scheme48 module language. See the documentation of Scheme48 for
details.
\surflets should not use the \ex{shift-reset} structure, as this might
confuse the \surflet handler.
\subsection{\surflet management}
Upon an initial client request, the \surflet handler looks for the
requested \surflet, dynamically loads it, installs an error handler
and calls the \ex{main} function of the \surflet with the initial
\ex{surflet-request}. To minimize the time of loading a \surflet,
the \surflet handler caches the structure of the \surflet in a cache,
the \surflet cache. As the \surflet is cached, its global values will
remain unchanged even through times when there are no active sessions
of the \surflet. Changing global \surflet values is a possibility to
exchange data between different sessions of the same \surflet. Note
that you have to take care to serialize the access to commonly shared,
mutated data.
The \surflet handler allows access to its cache via the following
procedures. The access to these procedures is currently
unrestricted but may be restricted in future versions of the
\surflet server.
\defun{get-loaded-surflets}{}{list}
\begin{desc}
This returns a list of the file names of the loaded \surflets.
\end{desc}
\defun{unload-surflet}{surflet}{undefined}
\begin{desc}
This removes the \var{surflet} from the \surflet cache. The
\var{surflet} is identified by its file name, as returned from
\ex{get-loaded-surflets}.
\end{desc}
\defun{reset-surflet-cache!}{}{undefined}
\begin{desc}
This empties the \surflet cache.
\end{desc}
Of course, when a \surflet is removed from the cache, the values in
its sessions remain untouched. However, if the \surflet is newly
loaded into the \surflet cache, the \surflet handler treats it like a
new \surflet, \ie the sessions of the "old" and the "new" \surflet
(though physically the same) do \emph{not} share their global data in
any way.
\subsection{Surflet Request}
\surflets get their input from \ex{surflet-request} objects. The
relevant procedures are the following. They are all exported by the
\exi{surflet-handler/requests} alias \exi{surflet-requests} structure.
\defun{surflet-request?}{object}{boolean}
\defunx{surflet-request-method}{surflet-request}{string}
\defunx{surflet-request-input-port}{surflet-request}{input-port/undefined}
\defunx{surflet-request-uri}{surflet-request}{string}
\defunx{surflet-request-url}{surflet-request}{url}
\defunx{surflet-request-version}{surflet-request}{pair}
\defunx{surflet-request-headers}{surflet-request}{alist}
\begin{desc}
The procedures inspect \ex{surflet-request} values. Most of them
return the values of the underlying \ex{request} object from
\ex{httpd}. \ex{surflet-request?} is a predicate for \ex{surflet
request}s. \ex{surflet-request-method} extracts the method of the
HTTP request; it's a string and either \ex{"GET"} or \ex{"POST"}.
\surflets won't receive requests with other methods.
\ex{surflet-request-input-port} returns an input-port that contains
data from the client on \ex{POST} requests and that the \surflet can
safely read. If the request is no \ex{POST} request, its value is
undefined. \ex{surflet-request-uri} returns the escaped URI string
as read from the request line. \ex{surflet-request-url} returns the
respective HTTP URL value (see the description of the \ex{url}
structure in chapter \ref{cha:url}). \ex{surflet-request-version}
returns a \verb|(major . minor)| integer pair representing the
version specified in the HTTP request. \ex{surflet-request-headers}
returns an association lists of header field names and their values,
each represented by a list of strings, one for each line.
\end{desc}
For some unknown weird cases, there are also these two procedures:
\defun{surflet-request-socket}{surflet-request}{socket}
\defunx{surflet-request-request}{surflet-request}{request}
\begin{desc}
\ex{surflet-request-socket} returns the socket connected to the
client. As with \ex{request}s, \surflets should not perform I/O on
this socket. See section \ref{httpd:requests} for reasoning.
\ex{surflet-request-requst} allows access to the underlying
\ex{request} object from \ex{httpd}. Both procedures should not be
necessary in normal operation and their usage is discouraged.
\end{desc}
\subsection{Surflet Response}
\surflets answer to a request by sending an \ex{surflet-response} to
the \surflet handler. The next section deals with how the surflet
responses are sent to the \surflet handler. The relevant procedures
for \ex{surflet-response} are the following. They are all exported by
the \exi{surflet-handler/responses} alias \exi{surflet-response}
structure.
\defun{make-surflet-response}{status content-type headers
data}{surflet-response}
\begin{desc}
This creates a \ex{surflet-response}. \var{status} is the status
code of the response. See section \ref{sec:http-responses} for
details on this. \var{content-type} is the MIME type of the data,
\eg \ex{"text/html"}. \var{headers} is an association list of
headers to be added to the response, each of which consists of a
symbol representing the field name and a string representing the
field value. \var{data} is the actual data. It must be either a
string or a list of values that will be \ex{display}ed.
\end{desc}
\defun{valid-surflet-response-data?}{object}{boolean}
\begin{desc}
This is a predicate on objects that may be surflet data, \ie a
string or a list (of objects that will be \ex{display}ed).
\end{desc}
\defun{surflet-response?}{object}{boolean}
\defunx{surflet-response-status}{surflet-response}{status-code}
\defunx{surflet-response-content-type}{surflet-response}{string}
\defunx{surflet-response-headers}{surflet-response}{alist}
\defunx{surflet-response-data}{surflet-response}{surflet-data}
\begin{desc}
The procedures return \ex{surflet-response} values.
\ex{surflet-response?} is a predicate for \ex{surflet-response}s.
\ex{surflet-response-status} returns the status code of the
response. See section \ref{sec:http-responses} for details on the
status code. \ex{surflet-response-content-type} returns the MIME
type of the response. \ex{surflet-response-headers} returns the
association list of header field names and their values, each
represented by a list of strings, one for each line.
\ex{surflet-response-data} returns the data of the
\ex{surflet-response}, the actual answer of the \surflet.
\end{desc}
\subsection{Sessions}
A \keyword{session} is a set of web pages that logically belong
together, \eg a user surfing through her webmail. A session starts
with the initial request for a \surflet and ends either explicitly by
the \surflet, or implicitly after a timeout. A third, not so common
case is its deletion from the session table.
The procedures presented in this subsection are all accessible via the
\exi{surflets/sessions} structure.
\subsubsection{Session management}
The \surflet handler automatically manages the sessions for each
\surflet, thus the \surflet does not have to deal with sessions or
state control (as it is the case with most other programming
interfaces for web applications). In particular, a \surflet does not
have to take care of saving the contents of variables before emitting
a web page and restoring the values later upon the next request, or
determining how far the user has proceeded in the application. The
only thing a \surflet may want to do is to tell the \surflet handler
when a session finished by calling \ex{send/finish} or an equivalent
procedure (see below).
Although a \surflet does not have to deal with the management of the
sessions, the \surflet handler allows access to its management
structures.
\defun{instance-session-id}{}{session-id}
\begin{desc}
This returns the session ID for the current session. The current
session is the session from which the function is called.
\end{desc}
\defun{get-session}{session-id}{session}
\begin{desc}
This returns the session for the given session ID.
\end{desc}
\defun{get-sessions}{}{alist}
\begin{desc}
This returns the complete list of all active session of the \surflet
handler. The list is an association list with the session-id as key
and the session as value.
The access to this procedure is currently unrestricted but may be
restricted in future versions of the \surflet server.
\end{desc}
\defun{delete-session!}{session}{undefined}
\begin{desc}
This deletes the specified session from the session table. Future
requests to the session are answered with the timeout text.
\end{desc}
\defun{session-alive?}{session}{boolean}
\begin{desc}
This returns \sharpt, if the specified session is alive, \ie
requests to it will be answered by the appropriate \surflet.
Otherwise, she returns \sharpf.
\end{desc}
\defun{session-surflet-name}{session}{string}
\defunx{session-session-id}{session}{session-id}
\begin{desc}
These procedures inspect values of a session.
\ex{session-surflet-name} returns the name of the \surflet for which
the session was created. \ex{session-session-id} returns the
session ID of the session.
\end{desc}
For each session, the \surflet handler has a counter running. She
resets the counter each time she receives a request for the session.
When the counter reaches a particular number of seconds, the
\keyword{lifetime} of the session, the \surflet handler deletes the
session and removes it from its session table. She will answer all
future requests for the session with the timeout text. The following
procedures deal with the lifetime of a session.
\defun{session-lifetime}{session}{integer}
\defunx{set-session-lifetime!}{session new-lifetime}{undefined}
\begin{desc}
\ex{session-lifetime} returns the number of seconds the \surflet
handler will initially wait before she automatically finishes the
session. \ex{set-session-lifetime!} changes the initial lifetime of
the \var{session} to \var{new-lifetime} and also resets the counter
for that session.
\end{desc}
\defun{session-adjust-timeout!}{session [lifetime]}{undefined}
\defunx{adjust-timeout!}{[lifetime]}{undefined}
\begin{desc}
These reset the counter for the lifetime of either the given
\var{session} (\ex{session-adjust-timeout!}) or the current session
(\ex{adjust-timeout!}). Both procedures give the session a lifetime
of either \var{lifetime} seconds or of the lifetime seconds stored
for the according \var{session}.
\end{desc}
In order to allow easy web programming, the \surflet handler
automatically saves and reifies continuations of a session. This is
totally transparent to the web programmer. For adminstration
purposes, the \surflet handler offers access to the continuation table
of a session via the following procedures.
\defun{session-continuation-table}{session}{table}
\defunx{session-continuation-table-lock}{session}{lock}
\defunx{session-continuation-counter}{session}{thread-safe-counter}
\begin{desc}
These functions the continuation table, the lock for the
continuation table and the counter for the continuations. The
continuation \var{table} is a hash table with the continuation ID as
key and the continuation as value, based on the \ex{tables}
structure of Scheme48. The \var{lock} is based on the \ex{locks}
structure of Scheme48. The \var{thread-safe-counter} is based on
the \ex{thread-safe-counter} structure that is part of the
\surflets.
The access to these functions is currently unrestricted but may be
restricted in future versions of the \surflet server.
\end{desc}
\subsubsection{Session data}
The \surflet handler distinguishes three kinds of \keyword{session
data}: session data that is local to a session of a \surflet and not
mutated, session data that is local to a session of a \surflet and
mutated and session data that is global to all sessions of a \surflet.
Every variable value that is never mutated is automatically local to
the session of the \surflet. Variable values that are mutated are
automatically global to all sessions of the \surflet. Values that
have to be mutated but should be local to a session of the \surflet
must be stored in a special place, the session data field of the
\surflet handler.\footnote{The reason for this distinction is the fact
that the \surflet handler saves and reifies the continuation of a
\surflet to realize the easy programming of web applications.
Mutations of values remain visible after reifying the continuation.}
\defun{get-session-data}{}{object}
\defunx{set-session-data!}{new-value}{undefined}
\begin{desc}
These procedures allow read/write access to the session data field
of the \surflet handler. The \surflet handler installs a session
data field for each session she creates. \ex{get-session-data}
reads the contents of this field, which is initially \sharpf.
\ex{set-session-data!} sets the contents of the field to
\var{new-value}. Mutations to the values, no matter when they
occur, are local to the session from which the procedures are
called, \ie the changes are only visible within a particular session
of the \surflet. The procedures are exported by the
\exi{surflet-handler/session-data} structure.
\end{desc}
\subsection{Basic I/O}
The \surflet communicates with the web client basically with the
following \ex{send} primitives. They are exported by the
\exi{surflet-handler/primitives} structure along with the procedures
from \ex{surflet-handler/requests}, \ex{surflet-handler/responses} and
the \ex{status-code} syntax.
\defun{send}{surflet-response}{\noreturn}
\begin{desc}
This procedure send the data of the \var{surflet-response} to the
client and does not return.
\end{desc}
\defun{send/finish}{surflet-response}{\noreturn}
\begin{desc}
This procedure does the same as \ex{send}, but it also finishes the
session of the \surflet. Future requests to the \surflet will be
answered with the timeout text (see above).
\end{desc}
\defun{send/suspend}{surflet-response-maker}{surflet-request}
\begin{desc}
This procedure suspends the current computation of the \surflet and
calls \var{surflet-response-maker} with the continuation-URL of the
current session to create the actual \ex{surflet-response}. When a
client requests the continuation-URL, the computation of the
\surflet will resume with \ex{send/suspend} returning that request
of the client. See \ref{sec:continuation-url} for details on
continuation-URLs.
\end{desc}
\defun{send/error}{status-code surflet-request [messages]}{\noreturn}
\begin{desc}
This sends an error response to the client. \var{status-code} is
the status code of the error, see section \ref{sec:http-responses} for
details. \var{surflet-request} is the last \ex{surflet-request} the
\surflet received; if unknown this argument may be \sharpf.
\var{messages} may contain some further information about the cause
of the error.
\end{desc}
\subsection{Web I/O}
Most of the time, a \surflet won't send arbitrary data but HTML to the
client. For this purpose, the \surflets provide extensive support.
First of all, they provide procedures specially designed for
submitting HTML.
\defun{send-html}{sxml}{\noreturn}
\defunx{send-html/finish}{sxml}{\noreturn}
\defunx{send-html/suspend}{sxml-maker}{surflet-request}
\begin{desc}
These are the equivalent procedures to the \ex{send} primitives.
\ex{send-html} and \ex{send-html/finish} accept an SXML object (more
on that below), translate it into HTML and send it to the client,
the latter finishing the session. \ex{send-html/suspend} suspends
the current computation, calls \var{sxml-maker} with the
continuation-URL, translates the resulting SXML into HTML and sends
it to the client. When the client requests the continuation-URL,
the computation is resumed and \ex{send-html/suspend} returns with
the \ex{surflet-request}.
\end{desc}
\subsubsection{SXML}
For easy creation of HTML output, the \ex{send-html} procedures
mentioned above represent the HTML in SXML. SXML is a creation of
Oleg Kiselyov. Basically, SXML is a list whose \ex{car} is an SXML
tag, a symbol representing the HTML tag, and the \ex{cdr} are other
SXML elements that will be enclosed by the HTML tag. For example,
\codex{'(h2 "Result")}
represents the tag \ex{h2}, that encloses the text "Result". The
represented HTML is
\codex{<h2>Result</h2>\textnormal{.}}
As in HTML, elements may be nested:
\codex{'(body (h2 "Result") (p "Example"))}
represents
\codex{<body><h2>Result</h2><p>Example</p>\textnormal{.}}
The @ symbol marks HTML attributes. The attributes follow the @
symbol in two element lists, the first element being the name of the
attribute and the last its value. For example, this is a link:
\codex{'(a (@ (href "add-surflet.scm") (name "linklist")) "Make new calculation.")}
representing the following HTML
\codex{<a href="add-surflet.scm"
name="linklist">Make new calculation.</a>\textnormal{.}} The
attributes form will only be recognized as such if it is the second
element of a list, right after the SXML tag.
\defun{sxml-attribute?}{object}{boolean}
\defunx{sxml-attribute-attributes}{sxml-attribute}{list}
\begin{desc}
These are procedures on SXML attribute forms. \ex{sxml-attribute?}
is a predicate for SXML attribute forms.
\ex{sxml-attribute-attributes} returns the list of name-value-lists
of the attributes form. Both procedures are exported by the
\exi{surflets/sxml} structure.
\end{desc}
The translator translates list elements which are numbers and symbols
to their string representation (except for the first element, of
course). She scans strings for the special characters \verb'&',
\verb'"', \verb'>' and \verb'<' and replaces them by their HTML
equivalents, and she ignores \sharpf and the emtpy list. See below
the special SXML tag \ex{plain-html} to see how to insert HTML code
untranslated. Furthermore, the translator accepts \ex{input-fields}
as list elements, which are translated to their HTML representation.
See below for details on input fields.
Using lists to represent HTML allows the programmer to define
operations on it. Most programmers construct their lists dynamically,
often by using \ex{quasiquote} (the symbol \ex{`}) and \ex{unquote}
(the symbol \ex{,}). \Eg
\begin{alltt}
`(html (title ,my-title)
(body (p "Hello, " ,(get-user-full-name))))
\end{alltt}
See below for how to create your own SXML.
\subsubsection{Special SXML tags}
The SXML to HTML translator accepts some special SXML tags that don't
directly translate to an HTML tag.
\newcommand{\defsxmltag} {\par\medskip\defsxmltagx}
\newcommand{\defsxmltagx}[2]%
{\hbox to \linewidth{\ttchars%
{\ttt(#1\testvoid{#2}{}{\ }{\sem{#2}}\testvoid{#2}{}{\/})
%\hskip 1em minus 0.5em$\longrightarrow$\hskip 1em minus 0.5em
%{\sem{#3}}
\hfill\quad\textnormal{SXML-tag}}}\index{#1}}
\defsxmltag{url}{URL [text]}
\begin{desc}
Inserts a link to \var{URL}, named with \var{text}. \var{text}
defaults to \var{URL}. Takes at least one argument. \Eg
\begin{alltt}
(url "/" "Main menu") \ensuremath{\Longrightarrow} <a href="/">Main menu</a>
(url "go.html") \ensuremath{\Longrightarrow} <a href="go.html">go.html</a>
\end{alltt}
\oops{\ex{url} does not accept extra attributes for the `\ex{A}' tag of
HTML. This should be fixed in a future version.}
\end{desc}
\defsxmltag{nbsp}{}
\begin{desc}
Inserts the HTML sequence \ex{"\&nbsp;"}. Takes no arguments.
\end{desc}
\defsxmltag{plain-html}{html \ldots}
\begin{desc}
Inserts \var{html} without any changes, thus it works like a quote.
Takes any number of arguments.
\end{desc}
\defsxmltag{*COMMENT*}{comment \ldots}
\begin{desc}
Inserts a comment, \ie \var{comment} enclosed between \verb|<!--|
and \verb|-->|. Takes any number of arguments.
\end{desc}
\defsxmltag{surflet-form}{k-url [method] [attributes] [SXML \ldots]}
\begin{desc}
Inserts HTML code for a web form. See below for details.
\var{k-url} usually is a continuation-URL. \var{method} is the
method to be used by the client to transfer the webform data.
Possible values are the symbols \ex{GET}, \ex{get}, \ex{POST},
\ex{post}, the first two specifying the GET method, the last two the
POST method. \var{method} defaults to the GET method.
\ex{attributes} are attributes for the created web form, \eg \ex{(@
(enc-type "text/plain"))}. The remaining arguments are taken as
SXML and translated as usually. Takes at least one argument. Note
that the attributes form may come at position three.
\end{desc}
\subsubsection{Do it yourself: your own SXML}
The \ex{send-html} procedures use a standard set of translation rules
to translate from SXML to HTML. However, you may define your own set
of translation rules or extend the given ones as you see fit. For
this, a short introduction to the translation process.
The translation process takes place in two steps. Step one translates
the given SXML to low level SXML, essentially a rough form of HTML in
list notation. Step two takes this low level SXML and prints to a
port. Step one is performed by \ex{sxml->low-level-sxml}, step two by
\ex{display-low-level-sxml}. All procedures and rules presented in
this subsection are exported from \exi{surflets/sxml}.
\defun{sxml->low-level-sxml}{sxml rules}{low-level-sxml}
\begin{desc}
Takes an SXML object (which essentially is a list) and a list of
SXML rules (more on this below) and translates it to low level SXML.
This procedure is an alias to the \ex{pre-post-order} procedure of
Oleg Kiselyov's SSAX module. It is an error if no rule triggers
(see below for when a rule triggers). However, it is no error if
multiple rules trigger; the first rule in the \var{rules} list wins.
\end{desc}
\defun{display-low-level-sxml}{low-level-sxml port}{boolean}
\begin{desc}
Takes low level SXML and \ex{display}s it to a port. She traverses
the list \var{low-level-sxml} depth-first, ignores the empty list
and \sharpf, executes thunks and \ex{display}s all other elements,
usually strings and characters, to \var{port}. Returns \sharpt if
she wrote anything, \sharpf otherwise. This function is basically
the \ex{SRV:send-reply} procedure of Oleg Kiselyov's SSAX module.
\end{desc}
\defun{sxml->string}{sxml rules}{string}
\begin{desc}
Combines step one and two of the translation process and returns the
resulting string, \ie it calls \ex{display-low-level-sxml} with the
result of a call to \ex{sxml->low-level-sxml} and a string port,
returning the content of the string port.
\end{desc}
%\defun{sxml->string/internal}{sxml rules}{list}
%\begin{desc}
% I forgot what this function was good for. I probably used it
% internally in one of the \surflet rules.
%\end{desc}
An \keyword{SXML-rule} consists of a \textit{trigger}, which is a
symbol, and the \textit{handler}, which is a translation procedure.
There are three types of rules, each of which is a dotted list:
\begin{description}
\item[\ex{(\synvar{trigger} *preorder* . \synvar{handler})}] \mbox{}\\
When \ex{sxml->low-level-sxml} sees the \synvar{trigger} as the
first element of a list, she calls \synvar{handler} with the
\emph{whole} list as arguments and replaces the list with the result
of that call (which must be a single value). Note that the arity of
the handler determines how many elements the list with the trigger may
or must contain.
\item[\ex{(\synvar{trigger} . \synvar{handler})}] \mbox{}\\
When \ex{sxml->low-level-sxml} sees the \synvar{trigger} as the first
element of a list, she calls herself on the remaining elements of the
list and then calls the \synvar{handler} with the trigger and the
results of those calls as arguments.
\item[\ex{(\synvar{trigger} \synvar{new-rules} . \synvar{handler})}] \mbox{} \\
When \ex{sxml->low-level-sxml} sees the \synvar{trigger} as the first
element of a list, she temporarily prepends \synvar{new-rules} to the
current rule set while calling herself on the remaining elements of
the list. She then calls the \synvar{handler} with the trigger and
the results of those calls as arguments. As the new rules are
prepended, this rule allows the temporary override of some rules.
\end{description}
There are two special triggers, who may trigger for all elements of
the SXML, not only the first element of a list:
\begin{itemize}
\item \ex{*text*} triggers for atoms in the SXML list, \ie usually
strings and characters. The handler is called with the symbol
\ex{*text*} and the atom as arguments.
\item \ex{*default*} triggers whenever no rule triggered, including
\ex{*text*}. If called for a list whose first element did not trigger
a rule, the handler is called with the whole list. If called for an
atom, the handler is called with the symbol \ex{*text*} and the atom
as arguments.
\end{itemize}
The \exi{surflets/sxml} structure defines some basic rules:
\defvar{default-rule}{SXML-rule}
\defvarx{text-rule}{SXML-rule}
\defvarx{attribute-rule}{SXML-rule}
\begin{desc}
These are the three basic rules exported by the \ex{surflets/sxml}
structure. \ex{default-rule} creates the leading and trailing HTML
tag and encloses the attributes. \ex{text-rule} just inserts the
given text with the special HTML characters \verb'&', \verb'"',
\verb'>' and \verb'<' escaped. \ex{attribute-rule} triggers for the
attributes form and creates attributes like \ex{selected} or
\ex{color="red"}.
\end{desc}
The \exi{surflets/surflet-sxml} add the rules for the special SXML
tags to this list:
\defvar{url-rule}{SXML-rule}
\defvarx{nbsp-rule}{SXML-rule}
\defvarx{plain-html-rule}{SXML-rule}
\defvarx{comment-rule}{SXML-rule}
\defvarx{surflet-form-rule}{SXML-rule}
\begin{desc}
These are the rules for the special SXML tags mentioned above,
namely \ex{url}, \ex{nbsp}, \ex{plain-html}, \ex{*COMMENT*} and
\ex{surflet-form}.
\end{desc}
\defvar{default-rules}{list}
\defvarx{surflet-sxml-rules}{list}
\begin{desc}
These are rule sets. \ex{default-rule} contains the rulese
\ex{default-rule}, \ex{attribute-rule}, \ex{text-rule},
\ex{comment-rule}, \ex{url-rule}, \ex{plain-html-rule} and
\ex{nbsp-rule}. \ex{surflet-sxml-rules} extends this list by
\ex{surflet-form-rule} and a rule for input fields.
\end{desc}
\defun{surflet-sxml->low-level-sxml}{sxml}{low-level-sxml}
\begin{desc}
This uses the \ex{surflet-sxml-rules} to translate \var{sxml} to low
level SXML, performin step one of the translation process.
\end{desc}
\subsection{Continuation-URL}
\label{sec:continuation-url}
The \keyword{continuation-URL} represents the point in the computation
of a session of a \surflet where the computation was halted by the
\surflet handler. When a browser requests a continuation-URL, the
\surflet handler looks up the continuation in its tables and reifies
it, allowing the session of the \surflet to resume its computation.
The procedures to access the continuation-URL are the following. They
are exported by the \exi{surflet-handler/resume-url} structure. Sorry
for the double naming \ex{resume-url} and continuation-URL.
\defun{resume-url?}{string}{boolean}
\defunx{resume-url-ids}{resume-url}{session-id continuation-id}
\defunx{resume-url-session-id}{resume-url}{session-id}
\defunx{resume-url-continuation-id}{resume-url}{continuation-id}
\begin{desc}
These inspect values of a resume url. \ex{resume-url?} is predicate
for resume urls. Note that it only operates on strings.
\ex{resume-url-ids} returns the session- and the continuation-id
that is stored in the \var{resume-url}. \ex{resume-url-session-id}
and \ex{resume-url-continuation-id} return only the session- or the
continuation-id, respectively.
\end{desc}
\subsection{Input fields}
The \surflets support all input fields defined for HTML~2.0 and allow
the creation of own input fields. \ex{input-field}s are first order
values, that represent the actual input field of the web page in the
\surflet. For that, this documentation distinguishes the
\emph{browser} value from the \emph{Scheme} value of an input field.
The browser value is the string representation of the input field
data the browser sends. The Scheme value is the value in the
\surflet the input field reports as its value, which may be of any
type, not only strings.
Here is a short overview, how to use input fields. See also the howto
for more informations. First, you create the \ex{input-field} that
represents the input field you want to use. Then you put this
\ex{input-field} into the SXML of the web page at the place the
input field shall appear. After \ex{send-html/suspend} has returned
with the next \ex{surflet-request}, you call \ex{get-bindings} with
that \ex{surlfet-request} and collect the resulting bindings. Last,
you call \ex{input-field-value} (or \ex{raw-input-field-value}) with
your \ex{input-field} and the collected bindings to get the Scheme
representation of the value the user has entered. Here is a small
example:
\begin{alltt}
(define-structure surflets surflet-interface
(open scheme-with-scsh
surflets)
(begin
(define (main req)
(let* ((text-input (make-text-field))
(req (send-html/suspend
(lambda (k-url)
`(html
(body
(surflet-form
,k-url
(p "Enter some asd text: " ,text-input)
,(make-submit-button)))))))
(bindings (get-bindings req))
(text (input-field-value text-input bindings)))
(send-html/finish
`(html
(body
(p "You've entered `" ,text "'."))))))
))
\end{alltt}
\paragraph{Getting the bindings}
The \exi{surflets/bindings} structures exports the necessary functions
to create bindings and extract values from them:
\defun{get-bindings}{surflet-request}{bindings}
\begin{desc}
This returns an association list representing the data the browser
has sent, usually the content of a webform. The name of the input
fields are the keys, their browser values the values. The values
are already unescaped. \ex{get-bindings} can (currently) only
handle \ex{application/x-www-form-urlencoded} data. You can call
\ex{get-bindings} on both \ex{GET} and \ex{POST} requests, even
multiple times (even on \ex{POST} requests).
\end{desc}
\defun{extract-bindings}{key bindings}{list}
\defunx{extract-single-binding}{key bindings}{string}
\begin{desc}
These extract values from the \var{bindings} as returned by
\ex{get-bindings}. \var{key} may be a string or a symbol which will
be translated to a string before use. \ex{extract-bindings} returns
a list of all values from \var{bindings} whose key is \var{key}.
\ex{extract-single-binding} returns the value from the binding whose
key is \var{key} and raises an error if there more than one such
binding. The two procedures are the same as in PLT's webserver.
\end{desc}
\ex{get-bindings} must acces the "Content-length" header field to
handle \ex{POST} request. \ex{surflets/bindings} therefore also
exports the procedure that does that job:
\defun{get-content-length}{headers}{number}
\begin{desc}
Returns the value of the "Content-length" header as a number, as
present in \var{headers}, \eg from \ex{surflet-request-headers}.
Will raise an error if there is no "Content-length" header or the
header is illformed, \eg contains no number.
\end{desc}
\paragraph{Retrieving the Scheme values}
The \exi{surflets/input-field-value} structure provides the functions
necessary to retrieve the Scheme value of input fields.
\defun{raw-input-field-value}{input-field bindings}{any type}
\defunx{input-field-value}{input-field bindings [error-value]}{any type}
\begin{desc}
These extract the Scheme value of an \var{input-field}, given the
\var{bindings} of the last request. Asking for a Scheme value may
raise an error. Some error conditions are: the input field was not
present in the bindings, the transformer could not generate a Scheme
value for the browser value, or some other error occured in a maybe
malfunctioning transformer. In any case, \ex{raw-input-field-value}
won't catch that error, while \ex{input-field-value} will catch it
and provide \var{error-value} as the \var{input-field}'s Scheme
value, which defaults to \sharpf.
\end{desc}
\defun{input-field-binding}{input-field bindings}{binding}
\begin{desc}
This returns the first binding in \var{bindings} that belongs to the
given \var{input-field} (\ie has \var{input-field}'s name as key).
\end{desc}
\paragraph{Creating and using input fields}
The procedures for the creation of the input fields mentioned in
HTML~2.0 are the following. They are exported by the
\exi{surflets/surflet-input-fields}. Note that most of the time,
you may omit any of the optional arguments, \eg you may only specify
some further attributes to \ex{make-text-field} without specifying a
default value. Keep in mind that \ex{input-field-value} catches the
error that may occur if an \ex{input-field} is asked for its Scheme
value and may return any (previously chosen) value instead.
\defun{make-text-field}{[default] [attributes]}{input-field}
\defunx{make-number-field}{[value] [attributes]}{input-field}
\defunx{make-password-field}{[default] [attributes]}{input-field}
\defunx{make-textarea}{[default] [rows] [columns] [readonly?] [attributes]}{input-field}
\begin{desc}
These create various input field where the user types something in.
\var{default} is the text or the number that the browser initially
displays in the input field. \var{attributes} are some further
attributes for the input field in SXML notation.
\ex{make-text-field} creates a regular text input field. Its Scheme
value is a string. \ex{make-number-field} creates a regular text
input field, whose Scheme value is a number. It is an error if the
input field does not contain a number. \ex{make-password-field}
creates a text input field that will display stars instead of the
typed text. Its Scheme value is a string. \ex{make-textarea}
creates a possibly multi line text input field. \var{rows}
specifies how many rows of the text the browser will display at once
and defaults to 5. \var{columns} specifies how many columns the
browser will display at once and defaults to 20. Note that if you
only supply one number, it will be interpreted as the \var{rows}
argument. \var{readonly?} is a boolean that tells the browser
whether to disallow changes of the displayed text.
\end{desc}
\defun{make-hidden-input-field}{[default] [attributes]}{input-field}
\begin{desc}
Creates a hidden input field, \ie a input field that the browser
won't display but whose value the browser will send. This input
field is provided for completeness; you usually won't need it, as
all values in your \surflet will survive the emission of a web page.
\var{default} is this value the browser will send. Note that
although the argument is marked as optional you usually want to
provide it. \var{attributes} are some further attributes for the
input field in SXML notation.
\end{desc}
\defun{set-text-field-value!}{input-field}{undefined}
\defunx{set-number-field-value!}{input-field}{undefined}
\defunx{set-hidden-field-value!}{input-field}{undefined}
\defunx{set-password-field-value!}{input-field}{undefined}
\defunx{set-textarea-value!}{input-field}{undefined}
\begin{desc}
These set the default value of the according input field after it
has been created. Although the procedure may not complain, it is an
error, if \var{input-field} is not the expected type of
\ex{input-field}, \eg if the argument to \ex{set-text-field-value}
was not created by \ex{make-text-field}.
\end{desc}
\defun{make-submit-button}{[caption] [attributes]}{input-field}
\defunx{make-reset-button}{[caption] [attributes]}{input-field}
\defunx{make-image-button}{image-source [attributes]}{input-field}
\begin{desc}
These create buttons on the web page which the user can click on.
\var{caption} is the text that is displayed on the button. If not
specified, the browser will choose a text, usually depending on the
local language setting on the browser side. \var{attributes} are
some further attributes for the input field in SXML notation.
\ex{make-submit-button} creates the regular button to submit the web
form data. As HTML~2.0 specifies that the value of a submit button
is its caption, its Scheme value is its caption, too.
\ex{make-reset-button} creates the button to reset all input fields
of the web form to their default values. As the browser does not
send data for reset buttons, it does not have a Scheme value, \ie
asking for a value will raise an error. \ex{make-image-button}
creates a picture button. Its Scheme value is a pair indicating the
x- and y-coordinates of the picture where the user has clicked to.
The argument \var{image-source} is not optional and is the string
URL of the displayed picture.
\end{desc}
\defun{make-checkbox}{[checked?] [attributes]}{input-field}
\defunx{make-annotated-checkbox}{value [checked?]
[attributes]}{input-field}
\begin{desc}
These create checkboxes. \var{checked?} says whether the browser
should initially mark the checkbox as checked. \var{attributes} are
some further attributes for the input field in SXML notation. If it
was checked the Scheme value of a checkbox made by
\ex{make-checkbox} is \sharpt. If it was checked, the Scheme value
of a checkbox made by \ex{make-annotated-checkbox} is its
\var{value} provided during its creation where \var{value} may be
chosen arbitrarily. Note that HTML~2.0 specifies that browsers
should not send data for unmarked checkboxes, thus asking for the
Scheme value of an unmarked checkbox will raise an error. It is
recommended to use \ex{input-field-value} to ask for the Scheme
value of a checkbox. This will catch the error and will instead
return \sharpf by default.
\end{desc}
\defun{check-checkbox!}{input-field}{undefined}
\defunx{uncheck-checkbox!}{input-field}{undefined}
\defunx{set-checkbox-checked?!}{input-field checked?}{undefined}
\begin{desc}
These change the \ex{checked?} field of a checkbox that tells the
browser whether it should initially mark the checkbox as checked.
\ex{check-checkbox!} tells the browser to do so,
\ex{uncheck-checkbox!} does not tell the browser to do so, and
\ex{set-checkbox-checked?!} does so depending on \var{checked?}. It
is an error if \var{input-field} was not created by
\ex{make-checkbox} or \ex{make-annotated-checkbox}.
\end{desc}
\defun{make-radio-group}{}{procedure}
\defunx{make-annotated-radio-group}{}{procedure}
\begin{desc}
These return generators for radio buttons. Radio buttons usually
are part of a group of radio buttons of which only one may be
selected at any time. The procedures return a procedure that
creates radio button \ex{input-field}s that belong to the same
group.
The returned procedures accept a \var{value} argument, an optional
\var{checked?} argument and an optional \var{attributes} argument.
They return an \ex{input-field}, the actual radio button. For
\ex{make-radio-group}, \var{value} must be a string, for
\ex{make-annotated-radio-group}, \var{value} may be any Scheme
value. The Scheme value of any member of the group of radio buttons
is the \var{value} of the marked radio button that was provided
during its creation. \var{checked?} determines whether the browser
will initially mark the radio button. Note that you are able to
tell the browser to initially mark more than one radio button, but
in which case the browser's behavior is undefined. \var{attributes}
are some further attributes for the input field in SXML notation.
%The procedure returned by \ex{make-radio-group} is of the
% form of \ex{\textit{radio-generator}} explained below, the one
% returned by \ex{make-annotated-radio-group} is of the form of
% \ex{\textit{annotated-radio-generator}}.
%\defun{\textit{radio-generator}}{text [checked?]
% [attributes]}{input-field}
%\defunx{\textit{annotated-radio-generator}}{value [checked?]
% [attributes]}{input-field}
%\begin{desc}
% A further description.
%\end{desc}
\end{desc}
\defun{check-radio!}{input-field}{undefined}
\defunx{uncheck-radio!}{input-field}{undefined}
\defunx{set-radio-checked?!}{input-field \var{checked?}}{undefined}
\begin{desc}
These change the \ex{checked?} field of a radio button that tells
the browser whether it should initially mark the radio button as
checked. \ex{check-radio!} tells the browser to do so,
\ex{uncheck-radio!} does not tell the browser to do so, and
\ex{set-radio-checked?!} does so depending on \var{checked?}. It is
an error if \var{input-field} was not created by the procedures
returned by \ex{make-radio-group} or
\ex{make-annotated-radio-group}.
\end{desc}
\defun{make-select}{select-options [multiple?]
[attributes]}{input-field}
\begin{desc}
This creates a select boxes. Other names are ``drop down menu'' or
simply ``list''. \var{select-options} is either a list of
\ex{select-options} created with the procedures presented below or a
list of strings. In the latter case the strings are automatically
translated into \ex{select-options}. \var{multiple?} allows
multiple selections in the select box. \var{attributes} are some
further attributes for the input field in SXML notation. Note that
you will only get multiple Scheme values for a select box that
allows multiple selections, if you specify the \var{multiple?}
argument; providing the according attribute in \var{attributes}
won't work (you will get the value of the first selection only).
\end{desc}
\defun{make-simple-select-option}{tag [selected?]
[attributes]}{select-option}
\defunx{make-annotated-select-option}{tag value [selected?]
[attributes]}{select-option}
\begin{desc}
These create the options for a select box, to be used as arguments
to \ex{make-select}. \var{tag} is a string that will be displayed
as an option of a select box. \var{value} is an arbitrary Scheme
value that will be the Scheme value of the select input field
that contains the option. For simple select options this is the same as
\var{tag}. \var{selected?} determines whether the browser should
preselect the option. \var{attributes} are some further attributes
for the input field in SXML notation.
\end{desc}
\defun{select-option?}{object}{boolean}
\begin{desc}
This is a predicate for \ex{select-option}s.
\end{desc}
\defun{select-select-option!}{tag input-field}{undefined}
\defunx{unselect-select-option!}{tag input-field}{undefined}
\defunx{set-select-option-selected?!}{tag input-field
selected?}{undefined}
\begin{desc}
These change the \ex{selected?} field of a select option that tells
the browser to preselect it. \ex{select-select-option!} tells the
browser to preselect it, \ex{unselect-select-option!} does not tell
it to do so and \ex{set-select-option-selected!} does so depending
on \var{selected?}. Note that you access the select option by
providing the \var{tag} and the select \var{input-field} in which
the select option is saved. \var{tag} is either the tag of the
select option or an index with 0 being the first select option of
that select input field. However, the change will affect all select
input fields that use the same select option. If there are
different select options with the same tag in a select input field,
the procedures will only touch one of them.
\oops{Unfortunetaly, the order of the arguments (index, object) is the
opposite of what is usual in Scheme (object, index). This should be
fixed in a future version.}
\end{desc}
\defun{add-select-option!}{input-field select-option}{undefined}
\defunx{delete-select-option!}{input-field select-option}{undefined}
\begin{desc}
These add or remove \var{select-option} to or from the select
\var{input-field}, respectively.
\end{desc}
\subsubsection{Do it yourself: your own input fields}
The \surflets library allows the creation of arbitrary own input
fields. The relevant procedures are exported by the
\exi{surflets/my-input-fields} structure.
\defun{make-input-field}{name type transformer attributes
html-tree-maker}{input-field}
\defunx{make-multi-input-field}{name type transformer attributes
html-tree-maker}{input-field}
\begin{desc}
These are the two constructors for \ex{input-field}s.
\var{name} is the name of the input field as used in the HTML. You
have to make sure that this name is unique across your web page, \eg
by using \ex{generate-input-field-name} presented below.
\var{type} is the type of the input field and mainly meant as a
label for debugging. You may choose an arbitrary value for it.
\var{transformer} is a procedure that accepts the created
\ex{input-field} and some other value as arguments and returns the
(single) Scheme value of the input field. For
\ex{make-input-field}, the other value is the string representation
of the value the user has entered in the represented input field, as
sent by the browser. For \ex{make-multi-input-field}, the other
value is an association list of all data the browser has sent, the
names being the key and the entered data being the value. This is
the very same list as returned by \ex{get-bindings}, see above.
When the \var{transformer} cannot create a Scheme value for the
\ex{input-field}, she should raise an error.
\var{attributes} takes some extra information you want to store
along with the \ex{input-field}. You may choose an arbitrary value
for it.
\var{html-tree-maker} is a procedure that takes the created
\ex{input-field} as argument and returns its representation in SXML.
\end{desc}
\defun{generate-input-field-name}{prefix}{string}
\begin{desc}
This generates a pseudo unique name based on prefix. Subsequent
calls with the same prefix are guaranteed to never return the same
string.\footnote{Well, never say never: if the structure is
reloaded, the counter is reset and \ex{generate-input-field-name}
will return the same names again.}
\end{desc}
\defun{input-field-name}{input-field}{string}
\defunx{input-field-type}{input-field}{any type}
\defunx{input-field-transformer}{input-field}{procedure}
\defunx{input-field-attributes }{input-field}{any type}
\defunx{input-field-html-tree-maker}{input-field}{procedure}
\defunx{input-field-html-tree}{input-field}{sxml}
\defunx{input-field-multi?}{input-field}{boolean}
\begin{desc}
These inspect input field values. \ex{input-field-name} returns the
name of the input field as used in its HTML representation.
\ex{input-field-type} returns a string indicating the type of the
input field, \eg "\ex{radio}" or "\ex{text}". For individual input
fields it may return a value of any type.
\ex{input-field-transformer} returns the transformer procedure that
is used the transform the browser value of the input field to a
Scheme value. \ex{input-field-attributes} returns the attributes
that were stored along with the input field.
\ex{input-field-html-tree-maker} returns the procedure that creates
the SXML representation of the input field.
\ex{input-field-html-tree} returns the SXML representation of the
input field. \ex{input-field-multi?} returns \sharpt if the input
field was created with \ex{make-multi-input-field}, \sharpf
otherwise. The transformer of an multi-\ex{input-field} gets the
browser bindings as second argument while the transformer of a
normal (non-multi) \ex{input-field} gets the string representation
of the entered data as second argument.
\end{desc}
\defun{set-input-field-attributes!}{input-field
new-attributes}{undefined}
\begin{desc}
This allows the mutation of the attributes of the \var{input-field}
to \var{new-attributes}.
\end{desc}
\defun{touch-input-field!}{input-field}{undefined}
\begin{desc}
This forces the recalculation of the SXML representation of the
\var{input-field} using its html-tree-maker procedure.
\end{desc}
\subsection{Web addresses}
The \surflets library allow you to determine which link or button a
user used to leave a page. The links are called evaluatable web
addresses. The \exi{surflets/returned-via} structure provides
procedures and syntax for this.
\defun{returned-via}{return-object bindings}{any value}
\defunx{returned-via?}{return-object bindings}{any value}
\begin{desc}
Determines, whether the user left the web page using
\var{return-object}. \var{bindings} are the bindings as returned by
\ex{get-bindings}. If \var{return-object} is an \ex{input-field},
\ex{returned-via} returns its Scheme value as reported by
\ex{input-field-value}. The input field usually can only be a
submit or an image button. If \var{return-object} is not an
\ex{input-field}, \ex{returned-via} assumes it is an evaluatable web
address. If the user did not use the evaluatable web address to
leave the web page, \ex{returned-via} returns \sharpf. Otherwise,
when the evaluatable web address is annotated, \ex{returned-via}
returns its annotation, otherwise just \sharpt. \ex{returned-via?}
is an alias for \ex{returned-via}. The type of the return value
depends on the type of \var{return-object}.
\end{desc}
% There is \defsyn or \defsyntax command yet, so I hack it on my own.
{\par\medskip{\index{case-returned-via}
\hbox to \linewidth{\ttchars{{\ttt{(case-returned-via \synvar{key} \synvar{clause} \ldots)}} \hfill syntax}}}%
% This said:
% (case-returned-via \synvar{key} \synvar{clause} \ldots) syntax
%\defvar{(case-returned-via \synvar{key} \synvar{clause} \ldots)}{syntax}
\begin{desc}
This works like \ex{case} with some flavor of \ex{cond}. Instead of
\ex{eq?} it uses \ex{returned-via} to determine which
\synvar{clause} applies. \synvar{key} is the \var{bindings}
argument to \ex{returned-via} (see above for the arguments of
\ex{returned-via}).
A clause is of the form
\codex{((\synvar{datum} \ldots) \synvar{expression} \ldots)}
where each \synvar{datum} is the \var{return-object} argument of
\ex{returned-via}. If for any of the \synvar{datum}
\ex{returned-via} returns a true value, the \synvar{expression}s are
evaluated.
Alternatively, a clause may be of the form
\codex{((\synvar{datum} \ldots) => \synvar{procedure} \ldots)}
If for any of the \synvar{datum} \ex{returned-via} returns a true
value, \synvar{proc} is called with that value.
The last possible clause is an "else" clause of the form
\codex{(else \synvar{expression} \ldots)}
which applies when the previous clauses don't apply.
\ex{case-returned-via} returns the value(s) of the
\synvar{expression} that was evaluated last.
\end{desc}
\subsubsection{Evaluatable web addresses}
The \exi{surflets/addresses} structure provides procedures to create
evaluatable web addresses. Evaluatable web addresses are used just
like web addresses with the difference that \ex{returned-via} can
tell whether the user used this web address to leave the web page.
\defun{make-address}{}{address-procedure}
\begin{desc}
This creates an evaluatable web address. \var{address-procedure} is
a procedure that accepts messages. If the message is a string,
\var{address-procedure} will assume it is a continuation URL and
will return a web address that can be used as a link. If the
message is the symbol \ex{address}, \var{address-procedure} will
return the real \ex{address} object.
\end{desc}
\defun{make-annotated-address}{}{address-procedure}
\begin{desc}
This creates an annotated evaluatable wewb address.
\var{address-procedure} is a procedure that accepts messages. The
procedure accepts either a string and an optional annotation which
may be any Scheme value, or it accepts only the symbol \ex{address}.
In the first case, it will assume the string is a continuation URL
and will return a web address that can be used as a link. In the
latter case, it will return the real \ex{address} object.
\end{desc}
\oops{Evaluatable web address cannot be used as the action URL of web
forms.}
\defun{address-name}{address}{string}
\defunx{address-annotated?}{address}{boolean}
\defunx{address-annotation}{address}{any type}
\begin{desc}
These inspect real \ex{address} objects as returned by the
evaluatable web addresses when given the symbol \ex{address}.
\ex{address-name} returns the name of the \var{address} as used in
the browser data. \ex{address-annotated?} indicates whether
\var{address} is annotated. \ex{address-annotation} returns the
annotation of \var{address}. If \var{address} is not annotated, it
returns \sharpf.
\end{desc}
\subsection{Callbacks}
The \surflets library allows to add a callback to a link. When the
user of a web page clicks on the link, the callback will be
executed. \ex{send-html/suspend} (usually) won't return in that
case.
\defun{make-callback}{callback-procedure}{continuation-URL}
\begin{desc}
This creates a callback. When a user clicks on a link to the
continuation URL \ex{make-callback} has returned,
\var{callback-procedure} will be called with the according
\ex{surflet-request}. \var{callback-procedure} should not return.
\ex{make-callback} works with continuations. Therefore, it is not
sensible to create callbacks on toplevel, nor is it sensible to
reuse callbacks. Instead, create your callback every time and right
before you need it.
If \var{callback-procedure} returns, \ex{make-callback} will return
\emph{again}, this time with the value returned by
\var{callback-procedure}. Note that in this case the continuation
that was active at the time of the call to \ex{make-callback} is
restored. Or, in short, don't let \var{callback-procedure} return
if you want to avoid headaches.
\end{desc}
\defun{make-annotated-callback}{callback-procedure}{procedure}
\begin{desc}
This creates a callback generator. The returned procedure accepts
any number of arguments \var{args} and returns a continuation URL.
When the user clicks on a link to the continuation URL,
\var{callback-procedure} will be called with the arguments
\var{args} previously provided.
It is an error, if \var{callback-procedure} returns. You should
create a fresh annotated callback every time and right before you
need it, as the continuation that was active at the time of the call
to \ex{make-annotated-callback} is restored.
\end{desc}
\defvar{callback-function}{procedure}
\begin{desc}
Use this procedure as the \var{callback-procedure} argument to
\ex{make-annotated-callback} to call arbitrary procedures with
arbitrary arguments.
\end{desc}
Here are some examples. The first example shows how you can use an
annotated callback. Note that it does not need to use
\ex{send-html/suspend}.
\begin{alltt}
(define-structure surflet surflet-interface
(open surflets
surflets/callbacks
scheme-with-scsh)
(begin
(define (main req)
(let ((language (make-annotated-callback result-page)))
(send-html
`(html
(head (title "Multi-lingual"))
(body
(h2 "Select your language:")
(ul
(li (url ,(language "Hello, how are you?")
"English")
(li (url ,(language "Hallo, wie geht es Ihnen?")
"Deutsch")))))))))
(define (result-page req text)
(send-html/finish
`(html
(head (title "Greeting"))
(body
(h2 ,text)))))
))
\end{alltt}
Replacing the \ex{main} procedure with the following definition will
have the same result:
\begin{alltt}
(define (main req)
(let ((language (make-annotated-callback callback-function)))
(send-html
`(html
(head (title "Multi-lingual"))
(body
(h2 "Select your language:")
(ul
(li (url ,(language result-page "Hello, how are you?")
"English")
(li (url ,(language result-page "Hallo, wie geht es Ihnen?")
"Deutsch")))))))))
\end{alltt}