More howto. Draft like version.
This commit is contained in:
parent
f231780bf0
commit
721fee4e07
|
@ -3,12 +3,14 @@
|
|||
|
||||
\def\surflet{SUrflet\xspace}
|
||||
\def\surflets{SUrflets\xspace}
|
||||
\def\scsh{scsh\xspace}
|
||||
\def\sunet{SUnet\xspace}
|
||||
|
||||
%From sunet/decls.tex
|
||||
\def\ie{\mbox{\emph{i.e.}} } % \mbox keeps the last period from
|
||||
\def\Ie{\mbox{\emph{I.e.}} } % looking like an end-of-sentence.
|
||||
\def\eg{\mbox{\emph{e.g.}} }
|
||||
\def\Eg{\mbox{\emph{E.g.}} }
|
||||
\def\ie{\mbox{\emph{i.e.}\xspace}} % \mbox keeps the last period from
|
||||
\def\Ie{\mbox{\emph{I.e.}\xspace}} % looking like an end-of-sentence.
|
||||
\def\eg{\mbox{\emph{e.g.}\xspace}}
|
||||
\def\Eg{\mbox{\emph{E.g.}\xspace}}
|
||||
|
||||
%From surflet/decls.tex
|
||||
{\theoremstyle{break}
|
||||
|
@ -17,13 +19,15 @@
|
|||
\setlength{\theorempreskipamount}{1.5ex plus0.2ex minus0.2ex}
|
||||
\setlength{\theorempostskipamount}{2ex plus0.5ex minus0.2ex}
|
||||
|
||||
% These environments differ from the other definition by the
|
||||
% positioning of \normalem
|
||||
\newenvironment{listing}
|
||||
{\ULforem\begin{proglist}\begin{alltt}\small}
|
||||
{\end{alltt}\end{proglist}\normalem}
|
||||
{\ULforem\begin{proglist}\begin{alltt}\small\normalem}
|
||||
{\end{alltt}\end{proglist}}
|
||||
|
||||
\newenvironment{reflisting}[1]
|
||||
{\ULforem\begin{proglist}[\refinlisting{#1}]\begin{alltt}\small}
|
||||
{\end{alltt}\end{proglist}\normalem}
|
||||
{\ULforem\begin{proglist}[\refinlisting{#1}]\begin{alltt}\small\normalem}
|
||||
{\end{alltt}\end{proglist}}
|
||||
|
||||
\newcommand{\contatlisting}[1]{%
|
||||
{\normalfont\textit{$<$wird fortgesetzt in Listing~\ref{#1}\/$>$}}}
|
||||
|
@ -62,5 +66,8 @@
|
|||
\newcommand{\name}[1]{\breakfuntt{#1}}
|
||||
\newcommand{\object}[1]{\breakfuntt{#1}}
|
||||
\newcommand{\file}[1]{\breakfuntt{#1}}
|
||||
\def\sharpf{\textnormal{\texttt{\#f}}}
|
||||
\def\sharpt{\textnormal{\texttt{\#t}}}
|
||||
\newcommand{\codemph}[1]{\emph{#1}}
|
||||
|
||||
\makeatother
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
\begin{document}
|
||||
\maketitle
|
||||
|
||||
\tableofcontents
|
||||
\sloppy
|
||||
|
||||
\section{Introduction}
|
||||
This report gives a short introduction in how to write a \surflet. It
|
||||
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
|
||||
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
|
||||
can be executed interactively trough a website. There is a \surflet
|
||||
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}
|
||||
\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
|
||||
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.
|
||||
|
||||
\subsection{Abbreviations in SXML}
|
||||
\label{sxml-abbrvs}
|
||||
|
||||
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
|
||||
|
@ -436,7 +441,8 @@ section.
|
|||
\section{How to write web forms}
|
||||
|
||||
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}
|
||||
|
@ -483,19 +489,21 @@ interaction elements: a text input field and a submit button.
|
|||
\surflets represent user interaction elements by \name{Input-field}
|
||||
objects. Thus, user interaction elements are first class values in
|
||||
\surflet, unlike in many other web scripting languages, \eg Java
|
||||
surflets, PHP or Microsoft Active Server Pages. You'll see soon what
|
||||
the advantages of this approach are.
|
||||
surflets, PHP or Microsoft Active Server Pages, \ie you have a
|
||||
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}
|
||||
(req (send-html/suspend
|
||||
(lambda (k-url)
|
||||
`(html
|
||||
(body
|
||||
(h1 "Echo")
|
||||
(surflet-form ,k-url
|
||||
(p "Please enter something:"
|
||||
,text-input
|
||||
,submit-button)))))))
|
||||
(req (send-html/suspend
|
||||
(lambda (k-url)
|
||||
`(html
|
||||
(body
|
||||
(h1 "Echo")
|
||||
(surflet-form ,k-url
|
||||
(p "Please enter something:"
|
||||
,text-input
|
||||
,submit-button)))))))
|
||||
\end{alltt}
|
||||
|
||||
Instead of discarding the return value of \name{send-html/suspend} as
|
||||
|
@ -545,7 +553,7 @@ request object. Here we save the user data into the variable
|
|||
\code{GET} and \code{POST}.
|
||||
|
||||
\begin{alltt}
|
||||
(user-input (input-field-value text-input bindings)))
|
||||
(user-input (input-field-value text-input bindings)))
|
||||
\end{alltt}
|
||||
|
||||
With the function \name{input-field-value} and the extracted user data
|
||||
|
@ -580,6 +588,7 @@ The complete list of functions that create \name{input-fields} can be
|
|||
found in the API.
|
||||
|
||||
\subsection{Return types other than strings}
|
||||
\label{subsec:input-return}
|
||||
|
||||
As the user interaction elements are first class values in a \surflet,
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
\subsubsection{Annotated input fields}
|
||||
|
@ -595,11 +604,12 @@ instead.
|
|||
The return value of an input field need not even be a primitive
|
||||
value. The \surflets library allows you to ``annotate'' your input
|
||||
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
|
||||
(open surflets
|
||||
handle-fatal-error
|
||||
scheme-with-scsh)
|
||||
(begin
|
||||
(define (main req)
|
||||
|
@ -620,14 +630,17 @@ input. \Eg, Consider this \surflet:
|
|||
,select-input-field)
|
||||
,(make-submit-button)))))))
|
||||
(bindings (get-bindings req))
|
||||
(price (input-field-value select-input-field bindings)))
|
||||
(price (input-field-value select-input-field
|
||||
bindings)))
|
||||
(send-html/finish
|
||||
`(html (head (title "Receipt"))
|
||||
(body
|
||||
(h2 "Your receipt:")
|
||||
(p "This costs you \$" ,price "."))))))
|
||||
))
|
||||
\end{alltt}
|
||||
\end{listing}
|
||||
|
||||
Let's go through the important part of this \surflet:
|
||||
|
||||
\begin{alltt}
|
||||
(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
|
||||
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
|
||||
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
|
||||
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.
|
||||
Again, this works not only with numbers, but with any arbitrary Scheme
|
||||
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