Held Friday, April 27, 2001
Today we consider how the various things we've learned this semester
can be put together to build a simple Web service.
- Are there questions on homework 6?
- Does anyone want a partner and lack one?
- No reading for Monday. (Never assign readings on Waltz Weekend.)
- Today is the last day Mr. Walker is visitng class. Ms. Moore may
also visit today.
- You can find the code for my plans service at
- The goals of the service
- The standard service architecture
- Some limits from our server
- Since it's been awhile since we've built CGI scripts, I'll
remind you of the "Scheme-CGI Cycle".
- A Web page sends a request to
- Input to that program comes from a form.
- The form tag's action attribute says which CGI script to use.
- For example,
- The CGI script needs to tell the Scheme-CGI program to run a
certain Scheme program.
- Finally, the Scheme program must contain a procedure named
page that generates a string that represents
an HTML page
(body (paragraph "An Example")))))
- Of course, that page can contain another form, which sets the
cycle in motion again.
- Today we will look at the design of a popular Web service: plans.
- We'll begin by listing some of the capabilities we want to provide
and restrictions that we might want to enforce.
- Note that I may have made different decisions in my sample
- Capabilities: What can people do?
- Restrictions: Who can do what?
- We're building a program that interacts with users.
- Key strategy for interactive programs:
Separate computation from interaction.
- That is, build procedures that manipulate the data separately
from the procedures that interact with the user.
- Consider the problem of looking up someone's plan.
- We write one procedure, perhaps
that will look up someone's plan entry.
- We write a separate procedure that gets the request from the user,
lookup, and displays the result.
- Why separate the two?
- We can test the two parts separately.
- For example, we can execute
lookup in a DrScheme window.
- Similarly, we can have the interface call a special procedure
that returns a
- We can provide multiple interfaces
- For example, we can provide a command-line interface in addition
to the Web-based interface.
- We could also provide different Web pages in the Web-based
- Before we start to write any code, we should think about some
overall strategies for implementing the service.
- Note that it's a very good idea to document these design
decisions so that the next person can come back and fix
- If we have some values that we use repeatedly throughout
the program (e.g., the name of the file or directory used for plans),
we're better off naming it with a
define at the
beginning of the program.
- That way, if we change our mind, we only have to update one
line of our program.
- For example,
- In almost every design for plans, we will need to store information
between invocations of the CGI script. How should we store that
information? Options include:
- A plain text file which we need to turn into something
that Scheme can understand.
- A text file which contains a sequence of lists, with
one list per account.
- A text file which contains a list of lists, with one list
- Similar solutions using vectors (for the collection of
entries, for each entry, or both)
- A solution in which the vector is sorted.
- Many text files, one per account
- I've chosen the list-of-lists because it's easy to read and
(let* ((port (open-input-file plans-file))
(plans (read port)))
; Bye bye old plans!
; We can only write to a port, so open one
(let ((port (open-output-file plans-file)))
; Write the new plans in one lump.
(write newplans port)
; Put in a newline to be nice.
; That's it, we're done.
- We need to decide how to arrange the capabilities in CGI scripts
- We could have one CGI script per capability.
- We could have one CGI script that does different things depending
on how it's called.
- I've chosen the latter because that seemed more interesting.
- We may also want to decide on what the pages should look
like (I've chosen minimal (i.e., ugly) pages).
- We need to decide what data we store for each person
- Also how we store that data.
- If we're going to be changing files (and we are), we need to set
up an environment in which it is legal to change files (danger!)
database file should be writeable by everyone. You
set this in a shell window.
- The directory in which the file resides should be writeable by
- Read the list of plans
search to find the matching plan.
- I'll assume that someone has already read the list of plans
and wants to pass it along.
- The code:
(lambda (account plans)
(search (lambda (entry) (equal? (car entry) account))
- Read the list of plans.
- Make sure that a plan does not yet exist for that account.
- Cons the new plan to the list.
- Write it to a file.
- One problem: How do we indicate success and failure?
- We could return
- That doesn't let us say why things failed, so I return
a list whose first element is
and whose remaining elements are notes about success or
(lambda (plan plans)
; Get the old plan
(let ((oldplan (get-plan (car plan) plans)))
; If the user already has a plan ...
; Give up, it's not legal to add a new one.
(list #f "You cannot add an account that already exists.")
; Otherwise ...
; Update the plans file.
(write-plans (cons plan plans))
; Return the appropriate result.
- The strategy for updating a plan is essentially the same.
- The basic interface is relatively easy:
- Get the kind of operation desired.
- Get the account and password, if appropriate.
- Call the core operation.
- If that operation succeeded, display the result.
- If that operation failed, display an error page.
- For example,
(lambda (plans account password email fullname plan)
(let ((result (add-plan
(list account password email fullname plan)
(if (car result)
(head (string-append "[Plans] Added: " account))
(plans-intro (string-append "Added entry for " account))
(plan->html (list account password email fullname plan))
(standard-buttons account password))))
(error-page (string-append "Creation error: " (cadr result))
- If there's time, we'll look at the overall structure of the
Friday, 12 January 2001
- Created generic outline format for class.
Friday, 28 April 2001