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