Allegro Webactions is a Lisp-based framework for creating web sites. It is distributed with Franz Lisp Allegro Lisp and the open-source Portable Aserve library.
To test your installation
- download vote-demo.zip
- extract its files into your Lisp code directory
- start Lisp
- require Aserve
- load clp-exts.lisp
- load vote-demo/webapp.lisp
- start the web server with (net.aserve:start :port 8000)
- go to http://localhost:8000/vote-demo/
If you see a login page, things are working. Enter any name you want and your password. (Psst! The password is your name reversed.) Try voting for some food, signing in as someone else, voting for another food, etc. See how the drop-down menu appears after the first vote and changes from that point on.
CLP Expression Language (CEL)
Critical to all the extensions is the CLP Expression Language (CEL).
A CEL expression is a string containing Lisp code.
The string is read, using normal Lisp rules. Any symbols
of the form
are replaced by the corresponding query, request, or session scope value
of the CLP variable name, without the dollar sign and in lower case.
For example, the CEL expression
"(vote:get-favorite $name)" on a page with
the URL test.clp?name=John
would have the value of
Since many CEL expressions are primarily retrieving data from objects, there's a special syntax to support some common ways to extract information. If a CEL expression has the form "($var key ...)" and the value of var is value, then the value of the CEL expression will depend on the type of value. If it is:
- nil, the CEL expression value is nil
- a list, the CEL expression value is (cdr (assoc key value :test 'equal))
- an array, the CEL expression value is (aref value key ...)
- an instance of a structure or class, the CEL expression value is (slot-value value key)
In the last case, the package of the key will be the same as the package of the class or structure.
Keep CEL expressions short and simple! They should mostly just call functions defined in your Lisp application that you can unit test easily.
<clp_eval [var="var-name"] value="cel-code" [package="pkg-name"]/>
This tag evaluates the CEL code. If a variable is specified, the result is stored in it, and nothing is written to the HTML. If a variable is not specified, and the result is not NIL, the result is written directly to the output HTML. If a package is given, the CEL code is read using that package, otherwise it is read using the COMMON-LISP package.
Here are some examples.
<p>This site is <clp_eval value="(machine-instance)"/>.</p>
This inserts into the HTML the string returned by the Common Lisp function machine-instance. Note the closing slash.
<p>This date is <clp_eval value="(time:get-date-string)"/>.</p>
This inserts into the HTML the date string returned by a function get-date-string defined in the package time. This is an alternative to defining a tag like clp_date. Defining a tag would be clearer if this was commonly needed.
<clp_eval var="site" value="(machine-instance)"/>
This sets the CLP request variable site to the string returned the Common Lisp function machine-instance. Nothing is printed to the HTML stream.
<clp_eval var="voters" value="(vote:get-voters)"/>
This sets the request variable voters to the result returned by (get-voters) in the package vote. Nothing is printed to the HTML stream.
Look in the
vote-demo CLP files for
more examples of
Caution: If you insert a CLP tag inside an attribute value, you must use single quotes on the attribute value, so that the Webactions parser doesn't get confused with the double quotes the CLP tag requires. For example, this code won't work:<input type="text" name="user" value="<clp_eval value="$user"/>">You have to write this instead:<input type="text" name="user" value='<clp_eval value="$user"/>'>
One of the most common things to do in dynamic HTML pages is to display a list of data, such as
- A list of options in an HTML menu based on an internal list of names
- Rows in a table based on data retrieved or calculated from an internal database
Webactions doesn't provide a tag for this so I defined clp_foreach. The syntax is:
<clp_foreach var="var-name" [status="status-var"] items="cel-code" [package="pkg-name"]> body </clp_foreach>
cel-code is read and evaluated as with clp_eval. The result should be a list. clp_foreach will set the request variable var-name to each element of the list and then insert a copy of body in the HTML. body may contain more HTML and/or CLP forms. The effect of this is to replicate the body for each element of this list.
If status="status-var" is given, then on each iteration status-var will be bound to a dotted pair whose CAR is the loop count (one-based) and whose CDR is the length of the list. This can be used to print an item's position in a list. It can also be used in conjunction with clp_when to do something different with the first or last element of a list.
Here's an example of using clp_foreach to build a simple set of options for an HTML
select menu. This is appears in
<select> <clp_foreach var="food" items="(vote:get-choices)"> <option value="<clp_value name="food"/>"> <clp_value name="food"/> </option> </clp_foreach> </select>
Sometimes you want some HTML to appear only if some data is present or some condition is true. Webactions has a plethora of IF tags but they're very specialized and only work with numbers. Therefore, I've defined a general clp_when tag. The syntax is:
<clp_when test="cel-code" [package="pkg-name"]> body </clp_when>
cel-code is read and evaluated as with clp_eval. The body will be inserted into the HTML if and only if the the result is not NIL. body may contain more HTML and/or CLP forms.
Here's an example of using clp_when in conjunction with a clp_foreach status variable to insert horizontal rules after every item in a list except the last.
<clp_foreach var="voter" status="status" items="(vote:get-voters)"> <clp_value name="voter"/> <clp_when test="(< (car $status) (cdr $status))"><hr></clp_when> </clp_foreach>
If you want to select one of several alternatives, nest clp_when elements inside a clp_choose like this:
<p> <clp_choose> <clp_when test="(null $favorite)"> Nothing selected yet. </clp_when> <clp_otherwise> You picked <clp_value name="favorite" />. </clp_otherwise> </clp_choose> </p>
The first clp_when that is true will be the only HTML generated. Use the optional clp_otherwise for a final default case.
CLP form elements
The CLP extensions include several tags for HTML forms to make it easy to populate a form with existing data. This is a common addition to web frameworks to make it easy for users to update information.
The clp_input tag works just like the standard HTML input tag, except that the value attribute can be a CEL expression. This can be used to populate a text or hidden field with prior data, e.g.,
<clp_input type="text" name="user" value="$user"/>
Note that clp_input needs to be closed with />.
clp_menu and clp_option
One of the more annoying bits of code for populating HTML forms is setting the currently selected item of a menu. To make this a lot less hassle, use clp_menu instead of select and clp_option instead of option.
- In clp_menu, use the value attribute to specify the currently selected option value, if any. The value attribute can be a CEL expression.
- Specify the options with clp_option. Be sure to give explicit value attributes (always a good idea, even with input.
A clp_option will be marked as selected if its value equals the value of the clp_menu. For example,
<clp_menu name="favorite" value="($user :favorite)"> <clp_option value="apple">Apple</clp_option> <clp_option value="banana">Banana</clp_option> <clp_option value="orange">Orange</clp_option> </clp_menu>