Automatic high-level user management tool for PLT Scheme Web Server

This package provides two files:

  _resume.ss_: user management tools
  _resume-unit.ss_: unit version of

NOTICE: the resume package requires a very recent version of DrScheme
to work due to a change in the web server code that was made only very
recently.  If you do not have a version of the web server checked out
from SVN after August 25, 2005, you'll need to upgrade.



The resume package encapsulates the idea of web sites that have users
who can start a session, wander off, log back in, and resume their
session right where they left off.

More concretely, it provides two new primitives for PLT Web Server
servlet authors.  The first,

  send/suspend-to-user : response user -> request

works just like send/suspend but takes an additional user argument
(which can be any representation of users you'd like) and remembers
that this is the most recent page that user has seen. The second,

  resume : user -> ...

takes a user and checks to see if that user has already made some
progress in the servlet. If it hasn't, it returns false and the
servlet keeps going.  If it has, resume never returns and instead it
resends the most recent page sent to that user using
send/suspend-to-user and continues on right where the user left off.

These resume points do not expire on the same schedule as normal
web-server-generated URLs. In fact, they live forever unless you
explicitly remove a user's resume point from the table using

  log-out! : user -> void

or clear the table out completely using 

  clear-resume-table! : -> void


An example

Here's an example of resume in action:

(module resume-example-servlet mzscheme
  (require (lib "" "web-server")
           (planet "" ("jacobm" "resume.plt" 1)))
  (provide interface-version timeout start)
  (define interface-version 'v1)
  (define timeout 10)

  ; start : request -> response
  (define (start initial-request)  
    (let ((uname (get-uname)))
      (resume uname)
      (send/suspend-to-user (get-page "page 1") uname)
      (send/suspend-to-user (get-page "page 2") uname)
      (send/suspend-to-user (get-page "page 3") uname)
      (send/finish `(html (p "That's the end.")))))
  ;; get-uname : -> string
  ;; gets a user name
  (define (get-uname)
    (let ((req
            (lambda (k)
                 (form ((action ,k))
                       (p "What's your name?")
                       (input ((type "text") (name "name")))
                       (input ((type "submit"))))))))))
      (extract-binding/single 'name (request-bindings req))))
  ;; get-page : string -> void
  ;; makes a page with the given message
  (define (get-page str)
    (lambda (k)
        (head (title "a page"))
         (form ((action ,k))
               (p ,str)
               (input ((type "submit") (value "Continue >>")))))))))

Let's say Alice visits this web site. She will be asked to type in her
name, and then she'll see a page called "page 1", then one called
"page 2", then a third called "page 3", and finally a page called
"That's the end." Nothing surprising. But now let's say Bob visits the
page. He types in his name and sees page 1 clicks submit to see page
2, but then the phone rings and he wanders off to answer it. Three
days later he remembers about the web site, but by this point he's
closed his browser and doesn't have any bookmarks.  He goes back to
the site and types his name in again, and the site doesn't show him
page 1; this time it shows him page 2 instead because that's the last
page he got to his first time around.

That's what the resume library does. It's a simple abstraction, but
one that would be very hard to do without the power of PLT Scheme and
the PLT Scheme Web Server.

Function reference
------------------ provides the following functions, divided into the basic API
and the advanced API:

In the following signatures, any Scheme value is a legal user? value;
it is used only to point out that the argument will be interpreted as
a user. In all cases users are tested for equality using equal?.

Basic API functions

> send/suspend-to-user : (string? -> response?) user? -> request?

Acts just like send/suspend but additionally sets this response as the
current resume point for the given user. Note that
send/suspend-to-user evaluates its function argument every time a user
resumes to that point.

> send/finish-to-user : response? user? -> TST

Acts just like send/finish but additionally sets this response as the
current resume point for the given user.

> send/forward-to-user : (string? -> response?) user? -> request?

Acts just like send/forward but additionally sets this response as the
current resume point for the given user.

> send/back-to-user : response? user? -> TST

Acts just like send/back but additionally sets this response as the
current resume point for the given user.

> resume : user? -> #f

If the given user has a resume point, continues there. Otherwise
returns false.

> log-out! : user? -> void

Removes any resume point the given user may have.

Advanced API functions

> resume : user? TST -> #f

Like the one-argument form of resume above, but if the user has a
resume point, sends the given value to it.

> set-resume-point! : user -> #t (or TST)

Sets the user's resume point to the context of the invocation of this
function. Returns #f afterwards when the resume point is set
initially; if the resume point is ever returned to, returns the second
argument that was given to the resume invocation that caused the
return, or #t if that resume was called in its one-argument form.

> clear-resume-table! : -> void?

Clears all resume points in the entire system.
Using resume.plt with a unit-based servlet

The resume.plt package also comes packaged as a unit with the same
provided functions as the module version so that authors of unit-based
servlets can use it. The file provides resume@, the
unit, and resume^, that unit's signature. To use it in your unit-based
servlet, use the following pattern:

(require (lib "")
         (lib "" "web-server")
         (lib "" "resume"))

(define my-servlet@
  (unit/sig ()
    (import servlet^ resume^)
    ;; your servlet code 

  (import (SERVLET : servlet^))
  (link (RESUME : resume^ (resume@ SERVLET))
        (MY-SERVLET : () (my-servlet@ SERVLET RESUME)))