More howto. Draft like version.
This commit is contained in:
parent
f231780bf0
commit
721fee4e07
|
@ -3,12 +3,14 @@
|
||||||
|
|
||||||
\def\surflet{SUrflet\xspace}
|
\def\surflet{SUrflet\xspace}
|
||||||
\def\surflets{SUrflets\xspace}
|
\def\surflets{SUrflets\xspace}
|
||||||
|
\def\scsh{scsh\xspace}
|
||||||
|
\def\sunet{SUnet\xspace}
|
||||||
|
|
||||||
%From sunet/decls.tex
|
%From sunet/decls.tex
|
||||||
\def\ie{\mbox{\emph{i.e.}} } % \mbox keeps the last period from
|
\def\ie{\mbox{\emph{i.e.}\xspace}} % \mbox keeps the last period from
|
||||||
\def\Ie{\mbox{\emph{I.e.}} } % looking like an end-of-sentence.
|
\def\Ie{\mbox{\emph{I.e.}\xspace}} % looking like an end-of-sentence.
|
||||||
\def\eg{\mbox{\emph{e.g.}} }
|
\def\eg{\mbox{\emph{e.g.}\xspace}}
|
||||||
\def\Eg{\mbox{\emph{E.g.}} }
|
\def\Eg{\mbox{\emph{E.g.}\xspace}}
|
||||||
|
|
||||||
%From surflet/decls.tex
|
%From surflet/decls.tex
|
||||||
{\theoremstyle{break}
|
{\theoremstyle{break}
|
||||||
|
@ -17,13 +19,15 @@
|
||||||
\setlength{\theorempreskipamount}{1.5ex plus0.2ex minus0.2ex}
|
\setlength{\theorempreskipamount}{1.5ex plus0.2ex minus0.2ex}
|
||||||
\setlength{\theorempostskipamount}{2ex plus0.5ex minus0.2ex}
|
\setlength{\theorempostskipamount}{2ex plus0.5ex minus0.2ex}
|
||||||
|
|
||||||
|
% These environments differ from the other definition by the
|
||||||
|
% positioning of \normalem
|
||||||
\newenvironment{listing}
|
\newenvironment{listing}
|
||||||
{\ULforem\begin{proglist}\begin{alltt}\small}
|
{\ULforem\begin{proglist}\begin{alltt}\small\normalem}
|
||||||
{\end{alltt}\end{proglist}\normalem}
|
{\end{alltt}\end{proglist}}
|
||||||
|
|
||||||
\newenvironment{reflisting}[1]
|
\newenvironment{reflisting}[1]
|
||||||
{\ULforem\begin{proglist}[\refinlisting{#1}]\begin{alltt}\small}
|
{\ULforem\begin{proglist}[\refinlisting{#1}]\begin{alltt}\small\normalem}
|
||||||
{\end{alltt}\end{proglist}\normalem}
|
{\end{alltt}\end{proglist}}
|
||||||
|
|
||||||
\newcommand{\contatlisting}[1]{%
|
\newcommand{\contatlisting}[1]{%
|
||||||
{\normalfont\textit{$<$wird fortgesetzt in Listing~\ref{#1}\/$>$}}}
|
{\normalfont\textit{$<$wird fortgesetzt in Listing~\ref{#1}\/$>$}}}
|
||||||
|
@ -62,5 +66,8 @@
|
||||||
\newcommand{\name}[1]{\breakfuntt{#1}}
|
\newcommand{\name}[1]{\breakfuntt{#1}}
|
||||||
\newcommand{\object}[1]{\breakfuntt{#1}}
|
\newcommand{\object}[1]{\breakfuntt{#1}}
|
||||||
\newcommand{\file}[1]{\breakfuntt{#1}}
|
\newcommand{\file}[1]{\breakfuntt{#1}}
|
||||||
|
\def\sharpf{\textnormal{\texttt{\#f}}}
|
||||||
|
\def\sharpt{\textnormal{\texttt{\#t}}}
|
||||||
|
\newcommand{\codemph}[1]{\emph{#1}}
|
||||||
|
|
||||||
\makeatother
|
\makeatother
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
\begin{document}
|
\begin{document}
|
||||||
\maketitle
|
\maketitle
|
||||||
|
|
||||||
|
\tableofcontents
|
||||||
|
\sloppy
|
||||||
|
|
||||||
\section{Introduction}
|
\section{Introduction}
|
||||||
This report gives a short introduction in how to write a \surflet. It
|
This report gives a short introduction in how to write a \surflet. It
|
||||||
is concentrated on the practical side rather on describing the
|
is concentrated on the practical side rather on describing the
|
||||||
|
@ -24,6 +27,7 @@ is concentrated on the practical side rather on describing the
|
||||||
surflets. The \surflet API will be described in the SUnet
|
surflets. The \surflet API will be described in the SUnet
|
||||||
documentation eventually.
|
documentation eventually.
|
||||||
|
|
||||||
|
%\marginpar{\surflets are pieces of code for web site scripting.}
|
||||||
For those who don't know it already, \surflets are pieces of code that
|
For those who don't know it already, \surflets are pieces of code that
|
||||||
can be executed interactively trough a website. There is a \surflet
|
can be executed interactively trough a website. There is a \surflet
|
||||||
handler who administrates their execution and suspension and as part
|
handler who administrates their execution and suspension and as part
|
||||||
|
@ -63,7 +67,7 @@ the ability to serve \surflets. This section tells you how to do it.
|
||||||
|
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item Download Oleg's SSAX package from
|
\item Download Oleg's SSAX package from
|
||||||
\url{http://prdownloads.sourceforge.net/ssax/ssax-sr5rs-plt200-4.9.tar.gz?download}.
|
\url{http://prdownloads.sf.net/ssax/ssax-sr5rs-plt200-4.9.tar.gz?download}.
|
||||||
|
|
||||||
\item Uncompress and untar it to any directory. This will create a
|
\item Uncompress and untar it to any directory. This will create a
|
||||||
directory called \name{SSAX}, to which I will refer to as
|
directory called \name{SSAX}, to which I will refer to as
|
||||||
|
@ -410,6 +414,7 @@ sending function is \name{send-html} which just sends a web page.
|
||||||
your \surflet instance.
|
your \surflet instance.
|
||||||
|
|
||||||
\subsection{Abbreviations in SXML}
|
\subsection{Abbreviations in SXML}
|
||||||
|
\label{sxml-abbrvs}
|
||||||
|
|
||||||
The example in subsection ``Several web pages in a row'' wrote down
|
The example in subsection ``Several web pages in a row'' wrote down
|
||||||
the link to the next web page explicitly via the ``a''-tag. As
|
the link to the next web page explicitly via the ``a''-tag. As
|
||||||
|
@ -436,7 +441,8 @@ section.
|
||||||
\section{How to write web forms}
|
\section{How to write web forms}
|
||||||
|
|
||||||
The \surflets come along with a libary for easy user interaction. The
|
The \surflets come along with a libary for easy user interaction. The
|
||||||
following subsections will show how to write web forms.
|
following subsections will show how to write web forms and how to get
|
||||||
|
the data the user has entered.
|
||||||
|
|
||||||
|
|
||||||
\subsection{Simple web forms}
|
\subsection{Simple web forms}
|
||||||
|
@ -483,8 +489,10 @@ interaction elements: a text input field and a submit button.
|
||||||
\surflets represent user interaction elements by \name{Input-field}
|
\surflets represent user interaction elements by \name{Input-field}
|
||||||
objects. Thus, user interaction elements are first class values in
|
objects. Thus, user interaction elements are first class values in
|
||||||
\surflet, unlike in many other web scripting languages, \eg Java
|
\surflet, unlike in many other web scripting languages, \eg Java
|
||||||
surflets, PHP or Microsoft Active Server Pages. You'll see soon what
|
surflets, PHP or Microsoft Active Server Pages, \ie you have a
|
||||||
the advantages of this approach are.
|
representation of a user interaction element in your program that you
|
||||||
|
can pass to functions, receive them as return values, etc. You'll see
|
||||||
|
soon the advantages of this approach.
|
||||||
|
|
||||||
\begin{alltt}
|
\begin{alltt}
|
||||||
(req (send-html/suspend
|
(req (send-html/suspend
|
||||||
|
@ -580,6 +588,7 @@ The complete list of functions that create \name{input-fields} can be
|
||||||
found in the API.
|
found in the API.
|
||||||
|
|
||||||
\subsection{Return types other than strings}
|
\subsection{Return types other than strings}
|
||||||
|
\label{subsec:input-return}
|
||||||
|
|
||||||
As the user interaction elements are first class values in a \surflet,
|
As the user interaction elements are first class values in a \surflet,
|
||||||
they can return other types than strings. For example the \surflets
|
they can return other types than strings. For example the \surflets
|
||||||
|
@ -587,7 +596,7 @@ come with a number input field, \ie a input field that accepts only
|
||||||
text that can be interpreted as a number. If the user enters
|
text that can be interpreted as a number. If the user enters
|
||||||
something that is not a number, \name{input-field-value} will return
|
something that is not a number, \name{input-field-value} will return
|
||||||
\sharpf as the value of the number input field. If you'd rather want
|
\sharpf as the value of the number input field. If you'd rather want
|
||||||
an error to be raise, you can use \name{raw-input-field-value}
|
an error to be raised, you can use \name{raw-input-field-value}
|
||||||
instead.
|
instead.
|
||||||
|
|
||||||
\subsubsection{Annotated input fields}
|
\subsubsection{Annotated input fields}
|
||||||
|
@ -595,11 +604,12 @@ instead.
|
||||||
The return value of an input field need not even be a primitive
|
The return value of an input field need not even be a primitive
|
||||||
value. The \surflets library allows you to ``annotate'' your input
|
value. The \surflets library allows you to ``annotate'' your input
|
||||||
fields with values which should be returned indicated by the user's
|
fields with values which should be returned indicated by the user's
|
||||||
input. \Eg, Consider this \surflet:
|
input. \Eg, consider this \surflet:
|
||||||
|
|
||||||
\begin{alltt}
|
\begin{listing}
|
||||||
(define-structure surflet surflet-interface
|
(define-structure surflet surflet-interface
|
||||||
(open surflets
|
(open surflets
|
||||||
|
handle-fatal-error
|
||||||
scheme-with-scsh)
|
scheme-with-scsh)
|
||||||
(begin
|
(begin
|
||||||
(define (main req)
|
(define (main req)
|
||||||
|
@ -620,14 +630,17 @@ input. \Eg, Consider this \surflet:
|
||||||
,select-input-field)
|
,select-input-field)
|
||||||
,(make-submit-button)))))))
|
,(make-submit-button)))))))
|
||||||
(bindings (get-bindings req))
|
(bindings (get-bindings req))
|
||||||
(price (input-field-value select-input-field bindings)))
|
(price (input-field-value select-input-field
|
||||||
|
bindings)))
|
||||||
(send-html/finish
|
(send-html/finish
|
||||||
`(html (head (title "Receipt"))
|
`(html (head (title "Receipt"))
|
||||||
(body
|
(body
|
||||||
(h2 "Your receipt:")
|
(h2 "Your receipt:")
|
||||||
(p "This costs you \$" ,price "."))))))
|
(p "This costs you \$" ,price "."))))))
|
||||||
))
|
))
|
||||||
\end{alltt}
|
\end{listing}
|
||||||
|
|
||||||
|
Let's go through the important part of this \surflet:
|
||||||
|
|
||||||
\begin{alltt}
|
\begin{alltt}
|
||||||
(let* ((select-input-field
|
(let* ((select-input-field
|
||||||
|
@ -640,13 +653,550 @@ input. \Eg, Consider this \surflet:
|
||||||
Here we define a select input field (a dropdown list). Instead of
|
Here we define a select input field (a dropdown list). Instead of
|
||||||
only providing a list of values that shall show up in the dropdown
|
only providing a list of values that shall show up in the dropdown
|
||||||
list and later examining which one was select and looking up the price
|
list and later examining which one was select and looking up the price
|
||||||
for the sweet, we bind the values in the list with price while we
|
for the sweet, we bind the values in the list with the price while we
|
||||||
create the select input field. When the select input field is shown
|
create the select input field. When the select input field is shown
|
||||||
in the browser, it will show the names of the sweets. When we lookup
|
in the browser, it will show the names of the sweets. When we lookup
|
||||||
the user's input, we will get the associated price for the sweet.
|
the user's input, we will get the associated price for the sweet.
|
||||||
Again, this works not only with numbers, but with any arbitrary Scheme
|
Again, this works not only with numbers, but with any arbitrary Scheme
|
||||||
value (\eg functions or records).
|
value (\eg functions or records).
|
||||||
|
|
||||||
More to come soon.
|
\subsection{Sending error messages}
|
||||||
|
|
||||||
\end{document}
|
If a user tries to forge a \surflet-URL (\eg by extracting the
|
||||||
|
continuation URL from the HTML source and editing it), your \surflet
|
||||||
|
has to deal with unexpected values. Usually, a forged \surflet-URL
|
||||||
|
will result in an error that is raised in one of the \surflet library
|
||||||
|
functions. If you don't catch this error, the \surflet handler will
|
||||||
|
catch it for you, send an error message to the user
|
||||||
|
\emph{and terminating the current session} as your \surflet obviously
|
||||||
|
encountered an unexpected error and might be in an invalid state. If
|
||||||
|
you don't want this behavior, you can catch this error (like any other
|
||||||
|
error that is raised by \scsh) and send your own error message with
|
||||||
|
\name{send-error} which is located in the \name{surflets/error}
|
||||||
|
package. The \name{handle-fatal-error} package can be useful in this
|
||||||
|
context. Here's an example, that modifies the example from the
|
||||||
|
previous subsection (modifications emphasized):
|
||||||
|
|
||||||
|
\begin{listing}
|
||||||
|
(define-structure surflet surflet-interface
|
||||||
|
(open surflets
|
||||||
|
\codemph{ handle-fatal-error
|
||||||
|
surflets/error}
|
||||||
|
scheme-with-scsh)
|
||||||
|
(begin
|
||||||
|
(define (main req)
|
||||||
|
(let* ((select-input-field
|
||||||
|
(make-select
|
||||||
|
(map make-annotated-select-option
|
||||||
|
'("Icecream" "Chocolate" "Candy")
|
||||||
|
'(1.5 2.0 0.5))))
|
||||||
|
(req (send-html/suspend
|
||||||
|
(lambda (k-url)
|
||||||
|
`(html
|
||||||
|
(head (title "Sweet Store"))
|
||||||
|
(body
|
||||||
|
(h1 "Your choice")
|
||||||
|
(surflet-form
|
||||||
|
,k-url
|
||||||
|
(p "Select the sweet you want:"
|
||||||
|
,select-input-field)
|
||||||
|
,(make-submit-button)))))))
|
||||||
|
(bindings (get-bindings req))
|
||||||
|
\codemph{ (cost (with-fatal-error-handler
|
||||||
|
(lambda (condition decline)
|
||||||
|
(send-error (status-code bad-request)
|
||||||
|
req
|
||||||
|
"No such option or internal
|
||||||
|
error. Please try again."))
|
||||||
|
(raw-input-field-value select-input-field
|
||||||
|
bindings))))}
|
||||||
|
(send-html/finish
|
||||||
|
`(html (head (title "Receipt"))
|
||||||
|
(body
|
||||||
|
(h2 "Your receipt:")
|
||||||
|
(p "This costs you \$" ,cost "."))))))
|
||||||
|
))
|
||||||
|
\end{listing}
|
||||||
|
|
||||||
|
Let's examine the important part of this example:
|
||||||
|
|
||||||
|
\begin{alltt}
|
||||||
|
(cost (with-fatal-error-handler
|
||||||
|
(lambda (condition decline)
|
||||||
|
(send-error (status-code bad-request)
|
||||||
|
req
|
||||||
|
"No such option or internal
|
||||||
|
error. Please try again."))
|
||||||
|
(raw-input-field-value select-input-field
|
||||||
|
bindings))))
|
||||||
|
\end{alltt}
|
||||||
|
|
||||||
|
As mentioned in \ref{subsec:input-return}, this \surflet uses
|
||||||
|
\name{raw-input-field-value} instead of \name{input-field-value}
|
||||||
|
because the former raises an error while the latter returns \sharpf in
|
||||||
|
case of an error.
|
||||||
|
|
||||||
|
If a user forges a continuation URL, \name{raw-input-field-value}
|
||||||
|
might not be able to find a valid value for the
|
||||||
|
\name{select-input-field} and raises an error. This error is catched
|
||||||
|
by the error handler which was installed by
|
||||||
|
\name{with-fatal-error-handler}. The error handler uses
|
||||||
|
\name{send-error} to send an error message to the browser. Its first
|
||||||
|
argument is the status code of the error message. See the
|
||||||
|
documentation of the \sunet webserver for different status codes. The
|
||||||
|
second argument is the request which was processed while the error
|
||||||
|
occured. The last argument is a free message text to explain the
|
||||||
|
cause of the error to the user.
|
||||||
|
|
||||||
|
While in the original \surflet the user will still see the resulting
|
||||||
|
receipt web page with an empty dollar amount and has her session
|
||||||
|
finished, this modified version will show an error message and won't
|
||||||
|
finish the session.
|
||||||
|
|
||||||
|
It is your choice, which version you choose, \ie if you let the
|
||||||
|
\surflet handler handle the occuring error automatically or if you
|
||||||
|
install your own error handlers and use \name{raw-input-field-value}.
|
||||||
|
However, be careful if you use \name{raw-input-field-value} along with
|
||||||
|
check boxes. The HTML standard dictates that an unchecked check box
|
||||||
|
does not appear in the data the browser sends to the server. Thus,
|
||||||
|
\name{raw-input-field-value} won't find the check box in the data and
|
||||||
|
raise an error which is not a ``real'' error as you migh expect it.
|
||||||
|
|
||||||
|
|
||||||
|
\subsection{Your own input fields}
|
||||||
|
|
||||||
|
The \surflet library contains constructors for all input fields that
|
||||||
|
are described in the HTML~2.0 standard. See the \surflet API for a
|
||||||
|
complete list. The \surflet library also allows you to create your
|
||||||
|
own input fields, \eg an input field that only accepts valid dates as
|
||||||
|
its input. This subsection gives you a short overview how to do
|
||||||
|
this. You will find the details in the \surflet API.
|
||||||
|
|
||||||
|
Let's have a look at an \surflet that uses its own input field. The
|
||||||
|
``input field'', called nibble input field, consists of eight check
|
||||||
|
boxes which represent bits of a nibble (half a byte). The value of
|
||||||
|
the input field is the number that the check boxes represent. \Eg, if
|
||||||
|
the user checks the last two checkboxes, the value of the nibble input
|
||||||
|
field is 3.
|
||||||
|
|
||||||
|
\begin{listing}
|
||||||
|
(define-structure surflet surflet-interface
|
||||||
|
(open surflets
|
||||||
|
surflets/my-input-fields
|
||||||
|
scheme-with-scsh)
|
||||||
|
(begin
|
||||||
|
|
||||||
|
(define (make-nibble-input-fields)
|
||||||
|
(let ((checkboxes (list (make-annotated-checkbox 8)
|
||||||
|
(make-annotated-checkbox 4)
|
||||||
|
(make-annotated-checkbox 2)
|
||||||
|
(make-annotated-checkbox 1))))
|
||||||
|
(make-multi-input-field
|
||||||
|
#f "nibble-input"
|
||||||
|
(lambda (input-field bindings)
|
||||||
|
(let loop ((sum 0)
|
||||||
|
(checkboxes checkboxes))
|
||||||
|
(if (null? checkboxes)
|
||||||
|
sum
|
||||||
|
(loop (+ sum (or (input-field-value (car checkboxes)
|
||||||
|
bindings)
|
||||||
|
0))
|
||||||
|
(cdr checkboxes)))))
|
||||||
|
'()
|
||||||
|
(lambda (ignore)
|
||||||
|
checkboxes))))
|
||||||
|
|
||||||
|
(define nibble-input-field (make-nibble-input-fields))
|
||||||
|
|
||||||
|
(define (main req)
|
||||||
|
(let* ((req (send-html/suspend
|
||||||
|
(lambda (new-url)
|
||||||
|
`(html (title "Nibble Input Widget")
|
||||||
|
(body
|
||||||
|
(h1 "Nibble Input Widget")
|
||||||
|
(p "Enter your nibble (msb left):")
|
||||||
|
(surflet-form ,new-url
|
||||||
|
,nibble-input-field
|
||||||
|
,(make-submit-button)))))))
|
||||||
|
(bindings (get-bindings req))
|
||||||
|
(number (input-field-value nibble-input-field bindings)))
|
||||||
|
(send-html
|
||||||
|
`(html (title "Result")
|
||||||
|
(body
|
||||||
|
(h2 "Result")
|
||||||
|
(p "You've entered " ,number "."))))))
|
||||||
|
))
|
||||||
|
\end{listing}
|
||||||
|
|
||||||
|
Let's go through this \surflet step by step.
|
||||||
|
|
||||||
|
\begin{alltt}
|
||||||
|
(define-structure surflet surflet-interface
|
||||||
|
(open surflets
|
||||||
|
surflets/my-input-fields
|
||||||
|
scheme-with-scsh)
|
||||||
|
\end{alltt}
|
||||||
|
|
||||||
|
If you want to create your own input fields, you have to open the
|
||||||
|
\name{surflets/my-input-fields} package.
|
||||||
|
|
||||||
|
\begin{alltt}
|
||||||
|
(begin
|
||||||
|
(define (make-nibble-input-fields)
|
||||||
|
(let ((checkboxes (list (make-annotated-checkbox 8)
|
||||||
|
(make-annotated-checkbox 4)
|
||||||
|
(make-annotated-checkbox 2)
|
||||||
|
(make-annotated-checkbox 1))))
|
||||||
|
\end{alltt}
|
||||||
|
|
||||||
|
\name{make-nibble-input-fields} is the constructor for our new type of
|
||||||
|
input field. As mentioned before, we use check boxes to let the user
|
||||||
|
enter the nibble. We use annotated checkboxes for this purpose whose
|
||||||
|
value is the value in the nibble.
|
||||||
|
|
||||||
|
\begin{alltt}
|
||||||
|
(make-multi-input-field
|
||||||
|
#f "nibble-input"
|
||||||
|
\end{alltt}
|
||||||
|
|
||||||
|
The value of our new input field will depend on the value of several
|
||||||
|
real input fields. Thus we create a multi input field. If the value
|
||||||
|
depended only on the browser data that is associated to the name of
|
||||||
|
our input field, we would use \name{make-input-field} instead, which
|
||||||
|
creates a usual input field. \Eg, if we wanted to create a date input
|
||||||
|
field that accepts only valid dates as input and used a text input
|
||||||
|
field for this purpose, we would use \name{make-input-field}.
|
||||||
|
|
||||||
|
The first two parameters is the name of the input field and its type.
|
||||||
|
As we use checkboxes to represent our input field, we don't need the
|
||||||
|
name field. The type field is meant for debugging purposes, so you
|
||||||
|
can identify the type of the input field during a debugging session.
|
||||||
|
|
||||||
|
\begin{alltt}
|
||||||
|
(lambda (input-field bindings)
|
||||||
|
(let loop ((sum 0)
|
||||||
|
(checkboxes checkboxes))
|
||||||
|
(if (null? checkboxes)
|
||||||
|
sum
|
||||||
|
(loop (+ sum (or (input-field-value (car checkboxes)
|
||||||
|
bindings)
|
||||||
|
0))
|
||||||
|
(cdr checkboxes)))))
|
||||||
|
\end{alltt}
|
||||||
|
|
||||||
|
The next parameter is the so called transformer function.
|
||||||
|
\name{raw-input-field-value} calls the transformer function to
|
||||||
|
determine the value of the input field depending on the given
|
||||||
|
bindings. The transformer function of a multi input field (which our
|
||||||
|
nibble input field is) gets the input field and the bindings as
|
||||||
|
parameters. A usual input field would only get the data that is
|
||||||
|
associated to its name.
|
||||||
|
|
||||||
|
The transformer function of our nibble input field goes over each
|
||||||
|
check box, looks it up in the bindings and adds its value to a sum, if
|
||||||
|
\name{input-field-value} can find it. If it can't find it, a zero is
|
||||||
|
added instead. The value of our nibble input field is the resulting
|
||||||
|
sum.
|
||||||
|
|
||||||
|
The rest of the \surflet is straight forward and not repeated here
|
||||||
|
again. We create, use and evaluate the nibble input field as we do
|
||||||
|
with every other input field.
|
||||||
|
|
||||||
|
|
||||||
|
\section{Program flow control}
|
||||||
|
|
||||||
|
With the techniques shown so far it is rather difficult to create a
|
||||||
|
web page that has several different successor webpages rather than
|
||||||
|
only one web page. This section will show you how to do this with the
|
||||||
|
\surflets. Basically, there are two different methods how to perform
|
||||||
|
this task. One method is to mark each link in some way and evaluate
|
||||||
|
the mark after \name{send-html/suspend} has returned. The other
|
||||||
|
method is to bind a callback function to each link that is called when
|
||||||
|
the user selects the link. This section shows both methods.
|
||||||
|
|
||||||
|
\subsection{Dispatching to more than one successor web page}
|
||||||
|
|
||||||
|
The basic idea of dispatching is to add a mark to a link and evaluate
|
||||||
|
it after the user has clicked on a link and \name{send-html/suspend}
|
||||||
|
returned. Let's have a look at an example. It shows an entry page at
|
||||||
|
which the user states the language in which she wants to be greeted:
|
||||||
|
|
||||||
|
\begin{listing}
|
||||||
|
(define-structure surflet surflet-interface
|
||||||
|
(open surflets
|
||||||
|
scheme-with-scsh)
|
||||||
|
(begin
|
||||||
|
|
||||||
|
(define (main req)
|
||||||
|
(let* ((english (make-address))
|
||||||
|
(german (make-address))
|
||||||
|
(req (send-html/suspend
|
||||||
|
(lambda (k-url)
|
||||||
|
`(html
|
||||||
|
(head (title "Multi-lingual"))
|
||||||
|
(body
|
||||||
|
(h2 "Select your language:")
|
||||||
|
(ul
|
||||||
|
(li (url ,(english k-url) "English")
|
||||||
|
(li (url ,(german k-url) "Deutsch")))))))))
|
||||||
|
(bindings (get-bindings req)))
|
||||||
|
(case-returned-via bindings
|
||||||
|
((english) (result-page "Hello, how are you?"))
|
||||||
|
((german) (result-page "Hallo, wie geht es Ihnen?")))))
|
||||||
|
|
||||||
|
(define (result-page text)
|
||||||
|
(send-html/finish
|
||||||
|
`(html
|
||||||
|
(head (title "Greeting"))
|
||||||
|
(body
|
||||||
|
(h2 ,text)))))
|
||||||
|
))
|
||||||
|
\end{listing}
|
||||||
|
|
||||||
|
Let's see how \name{main} presents the different options:
|
||||||
|
|
||||||
|
\begin{alltt}
|
||||||
|
(define (main req)
|
||||||
|
(let* ((english (make-address))
|
||||||
|
(german (make-address))
|
||||||
|
\end{alltt}
|
||||||
|
|
||||||
|
Of course you don't have to worry about adding the mark to the links.
|
||||||
|
Instead, we create the links with \name{make-address}.
|
||||||
|
|
||||||
|
\begin{alltt}
|
||||||
|
(req (send-html/suspend
|
||||||
|
(lambda (k-url)
|
||||||
|
`(html
|
||||||
|
(head (title "Multi-lingual"))
|
||||||
|
(body
|
||||||
|
(h2 "Select your language:")
|
||||||
|
(ul
|
||||||
|
(li (url ,(english k-url) "English")
|
||||||
|
(li (url ,(german k-url) "Deutsch")))))))))
|
||||||
|
\end{alltt}
|
||||||
|
|
||||||
|
\name{make-address} returns a function you can call to create the
|
||||||
|
link as we did here with
|
||||||
|
|
||||||
|
\begin{alltt}
|
||||||
|
(li (url ,(english k-url) "English")
|
||||||
|
\end{alltt}
|
||||||
|
|
||||||
|
This creates a list item which contains a hyperlink labeled
|
||||||
|
``English''. The hyperlink is created by the SXML abbreviation
|
||||||
|
\name{url} as shown in \ref{sxml-abbrvs}. Instead of just passing
|
||||||
|
the continuation URL \name{k-url} to \name{url}, we create the marked
|
||||||
|
link by calling the function \name{make-adddress} gave us.
|
||||||
|
|
||||||
|
\begin{alltt}
|
||||||
|
(bindings (get-bindings req)))
|
||||||
|
(case-returned-via bindings
|
||||||
|
((english) (result-page "Hello, how are you?"))
|
||||||
|
((german) (result-page "Hallo, wie geht es Ihnen?")))))
|
||||||
|
\end{alltt}
|
||||||
|
|
||||||
|
After \name{send-html/suspend} has returned, we can evaluate which
|
||||||
|
link the user has clicked by using \name{case-returned-via}.
|
||||||
|
\name{case-returned-via} works similar to the regular \name{case} of
|
||||||
|
Scheme. It evaluates the body of the form whose initial list contains
|
||||||
|
the address that the user used to leave the website. \Eg, if the user
|
||||||
|
has selected ``German'' as her preferred language and thus clicked on
|
||||||
|
the link we have named \name{german} in our \surflet,
|
||||||
|
\name{case-returned-via} will evaluate its second form and the
|
||||||
|
\surflet will display the greeting in German.
|
||||||
|
|
||||||
|
\name{case-returned-via} is syntactic sugar like the regular
|
||||||
|
\name{case}. However, instead of \name{equal?} it uses
|
||||||
|
\name{returned-via}. \name{returned-via} takes the bindings and
|
||||||
|
and an address and returns \sharpt, if the user left the web page via
|
||||||
|
this address (\ie, via the link that is represented by this address)
|
||||||
|
and \sharpf otherwise. \name{returned-via} does not end with a
|
||||||
|
question mark as it might return other values as well as we will see
|
||||||
|
shortly. Of course, it is your choice if you want to use
|
||||||
|
\name{case-returned-via} or explicitly \name{returned-via}.
|
||||||
|
|
||||||
|
|
||||||
|
\subsection{Annotated dispatching}
|
||||||
|
|
||||||
|
The approach shown in the previous subsection has one major drawback:
|
||||||
|
the meaning of an address becomes clear only when you look at the
|
||||||
|
dispatching section of \name{case-returned-via}. This subsection
|
||||||
|
shows you how to link the meaning and the representation of an address
|
||||||
|
closer together.
|
||||||
|
|
||||||
|
We modify the previous code example slightly to this \surflet
|
||||||
|
(differences emphasized):
|
||||||
|
|
||||||
|
\begin{listing}
|
||||||
|
(define-structure surflet surflet-interface
|
||||||
|
(open surflets
|
||||||
|
scheme-with-scsh)
|
||||||
|
(begin
|
||||||
|
|
||||||
|
(define (main req)
|
||||||
|
(let* (\codemph{(language (make-annotated-address))}
|
||||||
|
(req (send-html/suspend
|
||||||
|
(lambda (k-url)
|
||||||
|
`(html
|
||||||
|
(head (title "Multi-lingual"))
|
||||||
|
(body
|
||||||
|
(h2 "Select your language:")
|
||||||
|
(ul
|
||||||
|
(li (url ,\codemph{(language k-url
|
||||||
|
"Hello, how are you?")}
|
||||||
|
"English")
|
||||||
|
(li (url ,\codemph{(language k-url
|
||||||
|
"Hallo, wie geht es Ihnen?")}
|
||||||
|
"Deutsch")))))))))
|
||||||
|
(bindings (get-bindings req)))
|
||||||
|
(case-returned-via bindings
|
||||||
|
\codemph{ ((language) => result-page))))}
|
||||||
|
|
||||||
|
(define (result-page text)
|
||||||
|
(send-html/finish
|
||||||
|
`(html
|
||||||
|
(head (title "Greeting"))
|
||||||
|
(body
|
||||||
|
(h2 ,text)))))
|
||||||
|
))
|
||||||
|
\end{listing}
|
||||||
|
|
||||||
|
Let's look at the differing parts:
|
||||||
|
|
||||||
|
\begin{alltt}
|
||||||
|
(let* ((language (make-annotated-address))
|
||||||
|
\end{alltt}
|
||||||
|
|
||||||
|
To link the meaning with the address itself, we use an annotated
|
||||||
|
address. As we can annotate the address now, we don't need two
|
||||||
|
distinct addresses anymore.
|
||||||
|
|
||||||
|
\begin{alltt}
|
||||||
|
(li (url ,(language k-url
|
||||||
|
"Hello, how are you?")
|
||||||
|
"English")
|
||||||
|
(li (url ,(language k-url
|
||||||
|
"Hallo, wie geht es Ihnen?")
|
||||||
|
"Deutsch")))))))))
|
||||||
|
\end{alltt}
|
||||||
|
|
||||||
|
In addition to the continuation URL \name{k-url} we also annotate the
|
||||||
|
address with a value. Here we use the different greetings as the
|
||||||
|
annotation. The address can be annotated with any arbitrary Scheme
|
||||||
|
value, \eg functions or records.
|
||||||
|
|
||||||
|
\begin{alltt}
|
||||||
|
(case-returned-via bindings
|
||||||
|
((language) => result-page))))
|
||||||
|
\end{alltt}
|
||||||
|
|
||||||
|
\name{case-returned-via} has an extended syntax similar to \name{cond}
|
||||||
|
that it useful with annotated address. The arrow \name{=>} calls the
|
||||||
|
following function with the annotation of the address via which the
|
||||||
|
user has left the web page. You can extract the annotation yourself
|
||||||
|
with \name{returned-via} like this:
|
||||||
|
|
||||||
|
\begin{alltt}
|
||||||
|
(result-page (returned-via language bindings))
|
||||||
|
\end{alltt}
|
||||||
|
|
||||||
|
This will call \name{result-page} with the annotation of the address
|
||||||
|
via which the user has left the web page. \name{returned-via} returns
|
||||||
|
\sharpf, if the user didn't leave the web page via one of the links
|
||||||
|
created with this address (which is not really possible in this
|
||||||
|
example).
|
||||||
|
|
||||||
|
\subsection{Callbacks}
|
||||||
|
|
||||||
|
The other method to lead to different successor web pages is using
|
||||||
|
callbacks. A callback is a function that is called if the user leaves
|
||||||
|
the web page via an associated link. This is different from the
|
||||||
|
dispatch method where \name{send-html/suspend} returns. You can
|
||||||
|
create a web page that only uses callbacks to lead to successor web
|
||||||
|
page and thus you don't have to use \name{send-html/suspend}.
|
||||||
|
Instead, you can use \name{send-html}.
|
||||||
|
|
||||||
|
Although it is possible to use several different callbacks in a single
|
||||||
|
web page, this is not recommended. The reason lies in the
|
||||||
|
implementation of a callback, which saves the current continuation.
|
||||||
|
Several different callbacks would result in the storage of the several
|
||||||
|
slightly different continuations. This is unnecessary, as you can
|
||||||
|
annotate the callbacks with the arguments for the callback function.
|
||||||
|
Let's see an example which is a variation of the previous examples
|
||||||
|
(important parts emphasized):
|
||||||
|
|
||||||
|
\begin{listing}
|
||||||
|
(define-structure surflet surflet-interface
|
||||||
|
(open surflets
|
||||||
|
\codemph{surflets/callbacks}
|
||||||
|
scheme-with-scsh)
|
||||||
|
(begin
|
||||||
|
|
||||||
|
(define (main req)
|
||||||
|
(let ((language \codemph{(make-annotated-callback result-page)}))
|
||||||
|
(\codemph{send-html}
|
||||||
|
`(html
|
||||||
|
(head (title "Multi-lingual"))
|
||||||
|
(body
|
||||||
|
(h2 "Select your language:")
|
||||||
|
(ul
|
||||||
|
(li (url ,\codemph{(language "Hello, how are you?")}
|
||||||
|
"English")
|
||||||
|
(li (url ,\codemph{(language "Hallo, wie geht es Ihnen?")}
|
||||||
|
"Deutsch")))))))))
|
||||||
|
|
||||||
|
(define \codemph{(result-page req text)}
|
||||||
|
(send-html/finish
|
||||||
|
`(html
|
||||||
|
(head (title "Greeting"))
|
||||||
|
(body
|
||||||
|
(h2 ,text)))))
|
||||||
|
))
|
||||||
|
\end{listing}
|
||||||
|
|
||||||
|
The differences to the dispatch method are the following: you have to
|
||||||
|
open the \name{surflets/callbacks} package to use callbacks, you don't
|
||||||
|
use the continuation URL to create the callback link, and the
|
||||||
|
callbacked function must accept the request from the browser as the
|
||||||
|
first argument. Furthermore, you don't have to use
|
||||||
|
\name{send-html/suspend}, if a user can only leave your web page via
|
||||||
|
callbacks. However, it can be sensible to combine the dispatch and
|
||||||
|
the callback method, so you have to use \name{send-html/suspend}.
|
||||||
|
|
||||||
|
\section{Outlook}
|
||||||
|
|
||||||
|
More to come soon about data management, \surflets containing of
|
||||||
|
different parts and individual SXML.
|
||||||
|
|
||||||
|
|
||||||
|
%\section{Data management}
|
||||||
|
%
|
||||||
|
%There are different kinds of data that float around in a \surflet,
|
||||||
|
%actually in any serve side scripted program: There is local data that
|
||||||
|
%is associated to each session, \ie each session has its own copy of
|
||||||
|
%this data and if one session modifies this data it does not change the
|
||||||
|
%value of the data in other sessions. There is also global data, \ie
|
||||||
|
%data that is shared among sessions and modification of this data in
|
||||||
|
%one session is visible in other sessions. Both types of data are
|
||||||
|
%realizable in a \surflet.
|
||||||
|
%
|
||||||
|
%Local data is managed automatically by the \surflet library. When you
|
||||||
|
%call \name{send-html/suspend}, your local data is saved in a
|
||||||
|
%continuation. After the user has proceeded in your web site, this
|
||||||
|
%continuation and thus your local data is restored.
|
||||||
|
%
|
||||||
|
%As all data is stored in a continuation, there are no copies of your
|
||||||
|
%data saved. This means that if you change data via side effects, \eg
|
||||||
|
%via \name{set!}, this changes remain effective after the continuation
|
||||||
|
%has been restored. Thus, using side effects to change data, for
|
||||||
|
%example with \name{set!} is a way to realize global data.
|
||||||
|
%
|
||||||
|
%Sometimes it is desirable to be able to change local data via side
|
||||||
|
%effects.
|
||||||
|
%
|
||||||
|
%\end{document}
|
||||||
|
|
||||||
|
%session data
|
||||||
|
%surflets containing of different parts
|
||||||
|
%my sxml
|
||||||
|
|
Loading…
Reference in New Issue