1203 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			TeX
		
	
	
	
			
		
		
	
	
			1203 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			TeX
		
	
	
	
| \documentclass{article}
 | |
| 
 | |
| \usepackage[latin1]{inputenc}
 | |
| \usepackage{fontenc}
 | |
| \usepackage{alltt}
 | |
| \usepackage{url,xspace}
 | |
| \usepackage{tabularx}
 | |
| \usepackage{theorem,ulem,float,afterpage}
 | |
| \normalem			%usually, don't use ulem
 | |
| 
 | |
| 
 | |
| \input{decls}
 | |
| 
 | |
| \title{Howto write \surflets}
 | |
| \author{Andreas Bernauer}
 | |
| 
 | |
| \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
 | |
| \surflet API in detail to give you instant succes in running your own
 | |
| 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
 | |
| of the SUnet webserver.  \surflets ease the implementation of web
 | |
| applications in two ways, compared to other server-side scripting
 | |
| tools like Java\texttrademark Servlets or Microsoft\textregistered
 | |
| Active Server Pages or PHP:
 | |
| 
 | |
| \begin{enumerate}
 | |
| \item  \surflets have an automatic program flow control like any
 | |
| other usual program, \ie the web designer doesn't have to care about
 | |
| session management at all. The sequence of the web pages result from
 | |
| their appearance in the program like the print statements in any other
 | |
| usual program.
 | |
| 
 | |
| \item  \surflets come along with a library for robust user
 | |
| interaction.  \surflets represent interaction elements of the web page
 | |
| like text input fields or dropdown lists in the \surflet program by
 | |
| specific objects.  A web designer can plug in these objects into a
 | |
| website and use them to read out the user input.
 | |
| \end{enumerate}
 | |
| 
 | |
| The following sections probably assume that you have basic knowledge
 | |
| of the SUnet webserver and scsh.  The environment variable
 | |
| \code{\$sunet} refers to the top level directory of your sunet
 | |
| installation.  On my system this is \name{/home/andreas/sw/sunet}.
 | |
| 
 | |
| \section{How to run the SUnet webserver that handles \surflets}
 | |
| 
 | |
| The following sections will show pieces of \surflet code you might
 | |
| want to try out.  Therefore you need the SUnet webserver running with
 | |
| the ability to serve \surflets.  This section tells you how to do it.
 | |
| 
 | |
| \begin{enumerate}
 | |
| \item You need Oleg's SSAX package (for scsh), to be able to use
 | |
| \surflets:
 | |
| 
 | |
| \begin{itemize}
 | |
|   \item Download Oleg's SSAX package from
 | |
|   \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
 | |
|   \code{\$SSAX}.
 | |
| 
 | |
|   \item Unfortunately, this distribution (plt200-4.9) has a typo in
 | |
|   the package definition for scsh.  Apply the patch that comes with
 | |
|   the \surflets distribution to get rid of this typo:
 | |
|     \begin{itemize}
 | |
|       \item \code{cd \$SSAX}
 | |
|       \item \code{patch -p1 < \$sunet/httpd/surflets/SSAX-goodhtml-patch}
 | |
|     \end{itemize}
 | |
| \end{itemize}
 | |
| 
 | |
| \item You can start the SUnet webserver along with the
 | |
| \surflet-handler now.  The \surflets distribution comes with a script that
 | |
| does this for you:
 | |
| 
 | |
| \begin{itemize}
 | |
|   \item \code{cd \$sunet/httpd/surflets}
 | |
|   \item \code{SSAX=\$SSAX ./start-surflet-server}
 | |
| \end{itemize}
 | |
| 
 | |
| 
 | |
| Please be patient, scsh has to load a lot of libraries.  If the
 | |
| loading succeeds you will see something like this:
 | |
| 
 | |
| \begin{alltt}
 | |
| [andreas@hgt surflets]\$ ssax=/home/andreas/sw/SSAX ./start-surflet-server
 | |
| Loading...
 | |
| reading options: ()
 | |
| Going to run SUrflet server with:
 | |
|  htdocs-dir:    /home/andreas/sw/sunet/httpd/surflets/web-server/root/htdocs
 | |
|  surflet-dir:   /home/andreas/sw/sunet/httpd/surflets/web-server/root/surflets
 | |
|  images-dir:    /home/andreas/sw/sunet/httpd/surflets/web-server/root/img
 | |
|  port:          8008
 | |
|  log-file-name: /home/andreas/sw/sunet/httpd/surflets/web-server/httpd.log
 | |
|  a maximum of 5 simultaneous requests, syslogging activated, 
 | |
|  and home-dir-handler (public_html) activated.
 | |
| 
 | |
|  NOTE: This is the SUrflet server. It does not support cgi.
 | |
| \end{alltt}
 | |
| 
 | |
| This means the server is up and running.  Try to connect to
 | |
| \url{http://localhost:8008} with your browser and you will see the
 | |
| welcome page of the \surflets.  You can also already try out some of
 | |
| \surflets that come with the distribution.
 | |
| 
 | |
| You will probably notice a long response time the first time you load
 | |
| the first \surflet.  This is because the server has to load the
 | |
| \surflet libraries.  The server handles further requests to \surflets
 | |
| faster.
 | |
| 
 | |
| If the port the \surflet server tries to use is occupied use, you will
 | |
| see an error message similar to this one:
 | |
| 
 | |
| \begin{alltt}
 | |
| Error: 98
 | |
|        "Address already in use"
 | |
|        #{Procedure 11701 (\%bind in scsh-level-0)}
 | |
|        4
 | |
|        2
 | |
|        (0 . 8008)
 | |
| \end{alltt}
 | |
| 
 | |
| In this case, pass another port number to the script, \eg 8000:
 | |
| \codex{ssax=\$SSAX ./start-surflet-server -p 8000}
 | |
| 
 | |
| The \code{--help} option will show you more parameters that you can
 | |
| adjust, but you won't need them for this howto.
 | |
| \end{enumerate}
 | |
| 
 | |
| 
 | |
| 
 | |
| \section{How to send web pages}
 | |
| 
 | |
| This section will discuss some of the various ways in which you can
 | |
| send a web page to a browser that contacted your \surflet.
 | |
| 
 | |
| \subsection{My first \surflet}
 | |
| \label{sec:first-surflet}
 | |
| 
 | |
| Traditionally,  your first program in any programming language prints
 | |
| something like ``Hello, World!''.  We follow this tradition:
 | |
| 
 | |
| \begin{listing}
 | |
| (define-structure surflet surflet-interface
 | |
|   (open surflets
 | |
|         scheme-with-scsh)
 | |
|   (begin
 | |
|     (define (main req)
 | |
|       (send-html/finish
 | |
|        '(html (body (h1 "Hello, world!")))))
 | |
|     ))
 | |
| \end{listing}
 | |
| 
 | |
| You can either save a file with that content in the \surflets
 | |
| directory the server mentioned at startup or you can use the file
 | |
| \name{howto/hello.scm} that comes along with the \surflets
 | |
| distribution and which is located in the server's standard \surflets
 | |
| directory.  Let's go through the small script step by step:
 | |
| 
 | |
| \begin{alltt}
 | |
| (define-structure surflet surflet-interface
 | |
| \end{alltt}
 | |
| 
 | |
| This defines a module named \name{surflet} which implements the
 | |
| interface \name{surflet-interface}.  \name{surflet-interface} just
 | |
| states that the module exports a function named \name{main} to which
 | |
| we will come shortly.  For those of you who know about the scsh module
 | |
| system: Yes, \surflets are basically scsh modules that are loaded
 | |
| dynamically during run time.
 | |
| 
 | |
| \begin{alltt}
 | |
|   (open surflets
 | |
|         scheme-with-scsh)
 | |
| \end{alltt}
 | |
| 
 | |
| The \name{open} form lists all the modules the \surflet needs.  You
 | |
| will probably always need the two modules that are stated here
 | |
| (namely \name{surflets} and \name{scheme-with-scsh}).  If you need
 | |
| other modules, like \name{srfi-13} for string manipulation, this is
 | |
| the place where you want to state it.
 | |
| 
 | |
| \begin{alltt}
 | |
|   (begin
 | |
| \end{alltt}
 | |
| 
 | |
| This just opens the body of the \surflet.  All your \surflet code goes
 | |
| here.\footnote{If you know about scsh modules, you probably also know
 | |
| that there is a \name{file} clause that you could use to place the
 | |
| code in a file instead or along with the \name{begin} clause.}
 | |
| 
 | |
| \begin{alltt}
 | |
|     (define (main req)
 | |
| \end{alltt}
 | |
| 
 | |
| Here is the \name{main} function that the interface declared this
 | |
| \surflet will implement.  The \name{main} function is the entry point
 | |
| to your \surflet:  The server calls this function every time a user
 | |
| browses to your \surflet the first time.  The server calls \name{main}
 | |
| with one argument: a representation of the inital request of the
 | |
| browser.  We don't have to worry about that at this point.
 | |
| 
 | |
| \begin{alltt}
 | |
|       (send-html/finish
 | |
|        '(html (body (h1 "Hello, world!")))))
 | |
|     ))
 | |
| \end{alltt}
 | |
| 
 | |
| \name{send-html/finish} is one of three function you will regularly
 | |
| use to send web pages to the browser.  The other two functions are
 | |
| \name{send-html} and \name{send-html/suspend}.
 | |
| \name{send-html/finish} -- as the name already suggests -- sends a
 | |
| HTML page to the browser and finishes the \surflet.  \name{send-html}
 | |
| just sends the HTML page and does not return and
 | |
| \name{send-html/suspend} sends the HTML page and suspends the
 | |
| \surflet, \ie it waits until the user continues with the \surflet, 
 | |
| \eg by submitting a webform.  We will discuss \name{send-html} and
 | |
| \name{send-html/suspend} in detail later.  I will refer to these three
 | |
| functions as the \emph{sending functions}.
 | |
| 
 | |
| In a \surflet, HTML pages are represented as lists, or, to be more
 | |
| precise, as SXML (S-expression based XML).  The first element of a
 | |
| SXML list is a symbol stating the HTML tag.  The other elements of a
 | |
| SXML list are the contents that are enclosed by this HTML tag.  The
 | |
| contents can be other SXML list, too.  Here are some examples of SXML
 | |
| lists and how they translate to HTML:
 | |
| 
 | |
| \newcommand{\tag}[1]{$\mathtt{<}$#1$\mathtt{>}$}
 | |
| \begin{tabbing}
 | |
| HTML: \medskip\=\kill
 | |
| SXML: \> \texttt{'(p "A paragraph.")}  \\
 | |
| HTML: \> \texttt{\tag{p}A paragraph.\tag{/p}}\\
 | |
| \\
 | |
| SXML: \> \texttt{'(p "A paragraph." (br) "With break line.")} \\
 | |
| HTML: \> \texttt{\tag{p}A paragraph.\tag{br}With break line.\tag{/p}}\\
 | |
| \\
 | |
| SXML: \> \texttt{'(p "Nested" (p "paragraphs"))}\\
 | |
| HTML: \> \texttt{\tag{p}Nested\tag{p}paragraphs\tag{/p}\tag{/p}}\\
 | |
| \end{tabbing}
 | |
| 
 | |
| Attributes are stated by a special list whose first element is the
 | |
| at-symbol.  The attribute list must be the second element in the list:
 | |
| 
 | |
| \begin{tabbing}
 | |
| HTML: \medskip\=\kill
 | |
| SXML: \> \texttt{'(a (@ (href "attr.html")) "Attributed HTML tags.")} \\
 | |
| HTML: \> \texttt{\tag{a href="attr.html"}Attributed HTML tags.\tag{/a}}\\
 | |
| \\
 | |
| SXML: \> \texttt{'(a (@ (href "attr2.html") (target "\_blank")) "2
 | |
| attributes.")} \\ 
 | |
| HTML: \> \texttt{\tag{a href="attr2.html" target="\_blank"}2
 | |
| attributes.\tag{/a}}
 | |
| \end{tabbing}
 | |
| 
 | |
| As you see from the \surflet example, \name{send-html/finish} expects
 | |
| as an argument SXML.  In the example, the SXML translates to the
 | |
| following HTML code:
 | |
| \begin{alltt}
 | |
| <html><body><h1>Hello, world!</h1>
 | |
| </body>
 | |
| </html>
 | |
| \end{alltt}
 | |
| 
 | |
| Please note, that there is no check for valid HTML or even XHTML
 | |
| here.  The only thing the translation process takes care of are
 | |
| special characters in strings like the ampersand (\code{\&}).  The
 | |
| translation process replaces them by their HTML representation (\eg,
 | |
| \code{\&}) so you don't have to worry about that when you use
 | |
| strings.  Everything else like using valid HTML tags or valid
 | |
| attributes is your responsibility.
 | |
| 
 | |
| 
 | |
| \subsection{Dynamic content}
 | |
| 
 | |
| Let's extend our first \surflet example by some dynamic content, \eg
 | |
| by displaying the current time using scsh's \name{format-date}
 | |
| function.  As the HTML page is basically represented as a list, this
 | |
| can be done like this:
 | |
| 
 | |
| \begin{listing}
 | |
| (define-structure surflet surflet-interface
 | |
|   (open surflets
 | |
|         scheme-with-scsh)
 | |
|   (begin
 | |
|     (define (main req)
 | |
|       (send-html/finish
 | |
|        `(html (body (h1 "Hello, world!")
 | |
|                     (p "The current date and time is "
 | |
|                        ,(format-date "~H:~M:~S ~p ~m/~d/~Y" 
 | |
|                                      (date)))))))
 | |
|     ))
 | |
| \end{listing}
 | |
| 
 | |
| This \surflet can be found in \name{howto/hello-date.scm}.  The
 | |
| beginning of this \surflet is the same as in the previous example.
 | |
| The difference lies in the argument to \name{send-html/finish}.  Note
 | |
| that the argument starts with a backquote (\code{`}) rather than with
 | |
| a regular quote (\code{'}) as in the previous example.
 | |
| 
 | |
| Instead of passing a ``static'' list, \ie a list whose contents are
 | |
| given before execution, this \surflet uses the quasiquote and unquote
 | |
| feature of Scheme to create a ``dynamic'' list, \ie list whose
 | |
| contents are given only during execution.  A ``dynamic'' list is
 | |
| introduced by a backquote (\code{`}) and its dynamic contents are
 | |
| noted by commata (\code{,}).  Thus, if the \surflet is executed while
 | |
| I am writing this howto, the argument to \name{send-html/finish} above
 | |
| is translated to
 | |
| 
 | |
| \begin{alltt}
 | |
|        '(html (body (h1 "Hello, world!")
 | |
|                     (p "The current date and time is "
 | |
|                        "13:09:03 PM 11/18/2003")))))
 | |
| \end{alltt}
 | |
| 
 | |
| \noindent\emph{before} it is passed to \name{send-html/finish}.  Thus, using
 | |
| dynamic content can be easily done with Scheme's quasiquote and
 | |
| unquote feature.  Of course, you can build your list in any way you
 | |
| want; the quasiquote notation is just a convenient way to do it.
 | |
| 
 | |
| 
 | |
| \subsection{Several web pages in a row}
 | |
| 
 | |
| The previous example \surflets only showed one page and finished
 | |
| afterwards.  Here, we want to present to web pages in a row.  We use
 | |
| the previously mentioned function \name{send-html/suspend}, which
 | |
| suspends after it has send the page and continues when the user
 | |
| clicked for the next page.  In contrast to \name{send-html/finish},
 | |
| that expected SXML, \name{send-html/suspend} expects a function that
 | |
| takes an argument and returns SXML.  The parameter the function gets
 | |
| (here: \name{k-url} is the URL that points to the next
 | |
| page:\footnote{In the API this URL is called the \emph{continuation
 | |
| URL}.}
 | |
| 
 | |
| \begin{listing}
 | |
| (define-structure surflet surflet-interface
 | |
|   (open surflets
 | |
|         scheme-with-scsh)
 | |
|   (begin
 | |
|     
 | |
|     (define (main req)
 | |
|       (send-html/suspend
 | |
|        (lambda (k-url)
 | |
|          `(html (body (h1 "Hello, world!")
 | |
|                       (p (a (@ (href ,k-url)) "Next page -->"))))))
 | |
|       (send-html/finish
 | |
|        '(html (body (h1 "Hello, again!")))))
 | |
|     ))
 | |
| \end{listing}
 | |
| 
 | |
| This \surflet can be found in \name{howto/hello-twice.scm}.  This
 | |
| example first displays a web page with the message ``Hello, world!''
 | |
| and a link to the next page labeled with ``Next page --$>$''.  When the
 | |
| user clicks on the provided link, \name{send-html/suspend} returns and
 | |
| the the next statement after the call to \name{send-html/suspend} is
 | |
| executed.  Here it is \name{send-html/finish} which shows a web page
 | |
| with the message ``Hello, again!''.
 | |
| 
 | |
| When \name{send-html/suspend} returns, (almost) the complete context
 | |
| of the running \surflet is restored.  Thus, every variable in the
 | |
| \surflet will retain its value during suspension.  The consequence is
 | |
| that you don't have to worry about sessions, sesssion variables and
 | |
| alike.  The user can freely use the back button of her browser or
 | |
| clone a window while the \surflet will keep on responding in the
 | |
| expected way.  This is all automatically managed by the
 | |
| \surflet-handler.  
 | |
| 
 | |
| The only exception are variables whose values are changed by side
 | |
| effects, \eg if you change a variable via \name{set!}.  These
 | |
| variables keep their modified values, allowing communication between
 | |
| sessions of the same \surflet.\footnote{If you want to change a
 | |
| variable via side effects but you don't want to interfere with other
 | |
| session, you can use \name{set-session-data!} and
 | |
| \name{get-session-data}.  See the API documentation for further
 | |
| information.}
 | |
| 
 | |
| 
 | |
| \subsection{Begin and end of sessions}
 | |
| 
 | |
| So far I don't have mentioned too much details about sessions.  The
 | |
| reason is, as mentioned before, that the \surflet handler takes of the
 | |
| session automatically as described in the previous paragraph.
 | |
| %, \ie it starts the session automatically when an
 | |
| %instance of your \surflet starts and takes care of the saving and
 | |
| %restoring of all variable values during suspensions of your \surflet
 | |
| %instance, except for \code{set!}ed values.
 | |
| 
 | |
| The only thing you have to worry about is when your session
 | |
| \emph{ends}.  As long as your session hasn't been finished by
 | |
| \name{send-html/finish}, the user can move freely between the web
 | |
| pages your \surflet provides.  Once you've finished the session via
 | |
| \name{send-html/finish}, this freedom ends.  As the session is over,
 | |
| the user will get an error message when he tries to recall some web
 | |
| page from the server.  The server will tell the user about the
 | |
| possible reasons for the error (namely that most likely the session
 | |
| was finished) and provides a link to the beginning of a new session.
 | |
| 
 | |
| Thus, \name{send-html/suspend} suspends the current execution of a
 | |
| \surflet, returning with the request for the next web page of your
 | |
| \surflet and \name{send/finish} finishes the session.  The third
 | |
| sending function is \name{send-html} which just sends a web page.
 | |
| \name{send-html} does not return and does not touch the session of
 | |
| 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
 | |
| websites contain a lot of links, the sending functions (like
 | |
| \name{send-html/finish}) allow an abbreviation.  The following SXML
 | |
| snippets are equivalent:
 | |
| 
 | |
| \begin{alltt}
 | |
| (a (@ (href ,k-url)) "Next page -->")
 | |
| (url ,k-url "Next page -->")
 | |
| \end{alltt}
 | |
| 
 | |
| \name{url} expects the target address as the next element and includes
 | |
| every text afterwards as part of the link. 
 | |
| 
 | |
| There are also some other abbreviations.  \code{(nbsp)} inserts
 | |
| `\code{\ }' into the HTML, \code{(*COMMENT* \dots)} inserts a
 | |
| comment, and with \code{(plain-html \dots)} you can insert arbitrary
 | |
| HTML code (\ie strings) directly , without any string conversions.
 | |
| The last abbreviation, \name{surflet-form}, is discussed in the next
 | |
| 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 and how to get
 | |
| the data the user has entered.
 | |
| 
 | |
| 
 | |
| \subsection{Simple web forms}
 | |
| 
 | |
| Let's write a \surflet that reads user input and prints it out on the
 | |
| next page:
 | |
| 
 | |
| \begin{listing}
 | |
| (define-structure surflet surflet-interface
 | |
|   (open surflets
 | |
|         scheme-with-scsh)
 | |
|   (begin
 | |
|     (define (main req)
 | |
|       (let* ((text-input (make-text-field))
 | |
|              (submit-button (make-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)))))))
 | |
|              (bindings (get-bindings req))
 | |
|              (user-input (input-field-value text-input bindings)))
 | |
|         (send-html/finish
 | |
|          `(html (body
 | |
|                  (h1 "Echo result")
 | |
|                  (p "You've entered: '" ,user-input "'."))))))
 | |
| ))
 | |
| \end{listing}
 | |
| 
 | |
| Here are the details to the code in \name{main}:
 | |
| 
 | |
| \begin{alltt}
 | |
|     (define (main req)
 | |
|       (let* ((text-input (make-text-field))
 | |
|              (submit-button (make-submit-button))
 | |
| \end{alltt}
 | |
| 
 | |
| \name{make-text-field} and \name{make-submit-button} define two user
 | |
| 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, \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)))))))
 | |
| \end{alltt}
 | |
| 
 | |
| Instead of discarding the return value of \name{send-html/suspend} as
 | |
| in the examples of the previous section, this time we'll save the
 | |
| return value, as it will contain the data the user has entered in our
 | |
| text input field.
 | |
| 
 | |
| The definition of the website is as described in the previous section
 | |
| except for the new abbreviation \name{surflet-form}.
 | |
| \name{surflet-form} creates the HTML code for a web form and expects
 | |
| as its next value the URL to the next webpage as provided by
 | |
| \name{send-html/suspend}, here named
 | |
| \name{k-url}.  The remaining arguments constitute the content of the
 | |
| web form.  Thus, the code above is equal to the following SXML:
 | |
| 
 | |
| \begin{alltt}
 | |
| (form (@ (action ,k-url) (method "GET"))
 | |
|   (p "Please enter something:"
 | |
|      ,text-input
 | |
|      ,submit-button))
 | |
| \end{alltt}
 | |
| 
 | |
| If you want to use the POST method instead of the default GET method,
 | |
| add the symbol \name{'POST} after the URL:
 | |
| 
 | |
| \begin{alltt}
 | |
| (surflet-form ,k-url
 | |
|               POST
 | |
|               (p "Please enter something:"
 | |
|                  ,text-input
 | |
|                  ,submit-button))
 | |
| \end{alltt}
 | |
| 
 | |
| The web page \name{send-html/suspend} sends to the browser looks like
 | |
| in figure \ref{fig:user1-1}.  After the user has entered his data into
 | |
| the web form, \name{send-html/suspend} returns with the request object
 | |
| of the browser for the next page.  This request object contains the
 | |
| data the user has entered.
 | |
| 
 | |
| \begin{alltt}
 | |
|              (bindings (get-bindings req))
 | |
| \end{alltt}
 | |
| 
 | |
| With the function \name{get-bindings} we pull out the user data of the
 | |
| request object.  Here we save the user data into the variable
 | |
| \name{bindings}.  \name{get-bindings} works for both request methods
 | |
| \code{GET} and \code{POST}.
 | |
| 
 | |
| \begin{alltt}
 | |
|            (user-input (input-field-value text-input bindings)))
 | |
| \end{alltt}
 | |
| 
 | |
| With the function \name{input-field-value} and the extracted user data
 | |
| we can read the value for an \name{input-field}.   Here, we want to
 | |
| know what the user has entered into the \name{text-input-field}.
 | |
| 
 | |
| \begin{alltt}
 | |
|         (send-html/finish
 | |
|          `(html (body
 | |
|                  (h1 "Echo result")
 | |
|                  (p "You've entered: '" ,user-input "'."))))))
 | |
| \end{alltt}
 | |
| 
 | |
| After we have extracted what the user has entered into the text field,
 | |
| we can show the final page of our \surflet and echo her input.
 | |
| 
 | |
| The scheme for user interaction is thus about the following:
 | |
| 
 | |
| \begin{itemize}
 | |
| \item Create the user interaction elements, \name{input-field}s, you
 | |
| want to use in your web page.
 | |
| \item Send the web page with \name{send-html/suspend} to the browser.
 | |
| Plug in the \name{input-field}s in the web page as if they were usual
 | |
| values.  Save the return value of \name{send-html/suspend}.
 | |
| \item Extract the user data from the return value of
 | |
| \name{send-html/suspend}.
 | |
| \item Read the values of each \name{input-field} out of the extracted
 | |
| user data with \name{input-field-value}.
 | |
| \end{itemize}
 | |
| 
 | |
| 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
 | |
| 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 raised, you can use \name{raw-input-field-value}
 | |
| instead.
 | |
| 
 | |
| \subsubsection{Annotated input fields}
 | |
| 
 | |
| 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:
 | |
| 
 | |
| \begin{listing}
 | |
| (define-structure surflet surflet-interface
 | |
|   (open surflets
 | |
| 	handle-fatal-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))
 | |
|               (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{listing}
 | |
| 
 | |
| Let's go through the important part of this \surflet:
 | |
| 
 | |
| \begin{alltt}
 | |
|       (let* ((select-input-field 
 | |
|               (make-select
 | |
|                (map make-annotated-select-option
 | |
|                     '("Icecream" "Chocolate" "Candy")
 | |
|                     '(1.5 2.0 0.5))))
 | |
| \end{alltt}
 | |
| 
 | |
| 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 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).
 | |
| 
 | |
| \subsection{Sending error messages}
 | |
| 
 | |
| 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
 |