Initial version of How To Use SUrflets.
Needs still some additions.
This commit is contained in:
parent
52e0a557e2
commit
8baad2666f
|
@ -0,0 +1,652 @@
|
||||||
|
\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
|
||||||
|
|
||||||
|
\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.
|
||||||
|
|
||||||
|
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.sourceforge.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}
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
\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. You'll see soon what
|
||||||
|
the advantages of this approach are.
|
||||||
|
|
||||||
|
\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}
|
||||||
|
|
||||||
|
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 raise, 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{alltt}
|
||||||
|
(define-structure surflet surflet-interface
|
||||||
|
(open surflets
|
||||||
|
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{alltt}
|
||||||
|
|
||||||
|
\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 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.
|
||||||
|
|
||||||
|
\end{document}
|
Loading…
Reference in New Issue