-*- outline -*- * Quick (or quasi-quick) start for a UnCommon Web (and Lisp) developer * (release 0.3 - 07-Dec-2005) ** Abstract This document describes how to develop a web application with the UnCommon Web framework. This document is NOT an introduction to Lisp language. It is a quick tutorial to understand how to build a web application with UnCommon Web framework. ** Introduction The author of UnCommon Web (UCW) is Marco Baringer. The homepage of UCW is http://common-lisp.net/project/ucw/. You can download the development branch of UCW using darcs: darcs get http://common-lisp.net/project/ucw/repos/ucw_dev Remember, please, to configure UCW correctly before to begin the development. You can read docs/README or docs/QUICKSTART. There are two movies (sic!) about Slime and UCW: http://common-lisp.net/torrents/slime.torrent (~150Mb/~60mins) http://common-lisp.net/torrents/hello-world.torrent (~50Mb/~20mins) You need a Lisp implementation and an interface development environment. I use OpenMCL 1.0, GNU Emacs and Slime on Mac OS X with Tiger 10.4.3. OpenMCL http://openmcl.clozure.com/ Emacs Official http://www.gnu.org/software/emacs/emacs.html Emacs for Mac http://bluebeard.org/software/emacs_osx/ Slime http://common-lisp.net/project/slime/ *** Emacs and Slime configuration Please, before to start the UCW adventure, you have to have a Lisp environment working: I mean, when you are in Emacs, pressing 'M-x slime', it displays the REPL (Read Eval Prompt Loop) window. A good introduction about Slime and Emacs are the Baringer's movies. ** Why Lisp for web application The technical advantages Lisp brings to the table are numerous. Image based development and remote debugging. Often, when a client discovers an error, you hook up slime to the remote image, the debugger pops up, and you can debug the error, and often fix it, while on the phone with the user. Having the entire app contained in one image, rather than a series of source files that are interpreted, has many advanteges for development, debugging, and deployment. UCW provides a CPS framework and YACLML for generating HTML, it's TAL template mechanism for working with those pesky designers. When using UCW you never worry about CGI POST parameters and all the usual web dev stuff, but focus on the application itself, assigning INPUTS to slots and letting UCW work magic. A plus for the web development are the continuations, and UCW's "component" model. Lisp (compiled) is faster than scripting languages. For any type of development, macros are a boon. For web development, a necessity. Web development is really a pain. Macro can abstract out all the silly details so you can focus on the code. Using some nifty Common Lisp Object System, CLOS, method combinations and some lexical magic you was able to turn a 'normal' web component into an AJAX based one with only one line of code changed (change the href into an onclick). Lisp is able to absorb new paradigms as soon as they come up. In the fast paced world of web development, you wouldn't want to live without that ability. Parenscript is a sexp syntax for javascript, and clsql is an sql library with and object-relational mapping and a sexp syntax for queries. Web development is a mess of different langauges (html, xml, javascript, sql, css etc). By using these Lisp based tools, you have a consitent syntax, and can use the same toolchain for every language you work in. Macros that generate javascript and html are almost as cool as macros that generate lisp code. ** Anatomy of a web application In UCW there are the following main elements: the application instance, the entry points of the application, the components of the application and the methods to render the components. The framework is CLOS based, the Common Lisp Object System. CLOS is a set of operators for doing object-oriented programming. *** Use of the application package A package, you know, is a Lisp object that maps names to symbols. The current package is always stored in the global variable *package*. The packages are useful to divide larger programs in small parts and to use a symbol as the name of a function or variable without worrying that the name is already used elsewhere. So we write, for instance, our application in the package it.bese.ucw-user: (in-package :it.bese.ucw-user) You can use another package, for instance it.domain.yourpackage: (in-package :cl-user) (defpackage :your.domain.yourpackage (:use :common-lisp :it.bese.ucw :it.bese.yaclml)) (in-package :your.domain.yourpackage) When you are in REPL with Slime, you can press ',' (comma) to enter in command mode. The default package is COMMON-LISP-USER. To change the package you enter in command mode and write in mini-buffer '!p' and insert the new package it.bese.ucw-user. You can use the auto-completion, pressing, as usual, TAB. Now you can access functions or variables of that package without the package qualifier '::'. The package it.bese.ucw-user is the package containing two default applications in UCW: examples app and admin app. *** Definition of the application instance Now we set the package, we can create the application instance. It's a global variable and the name is *hello-world*. The asteriks, you know, is the convention to identify better the global variables. To make instances of a class, instead of calling a specific function, we call the general make-instance with the class name as the first argument, cookie-session-application. This class is for applications which use cookies for session tracking. Cookie session applications work exactly like standard-applications except that when the session is not found using the standard mechanisms the id is looked for in a cookie. (defvar *hello-world* (make-instance 'standard-application :url-prefix "/hello/")) You can define the root of your web document (CSS, HTML, JavaScript, etc.), for instance: (defvar *hello-world* (make-instance 'standard-application :url-prefix "/chessweb/" :www-roots (list (merge-pathnames "../ucw_apps/hello/")))) The relative path in www-roots starts from UCW directory. You can check if there is your application asking it to the server on the REPL prompt: (server.applications *default-server*) *** Registration of the application Now we have to register the application in the server. (register-application *default-server* *hello-world*) Another way to add the application to the server during the creation of the server contained in 'bin/start.lisp' file. For instance (ucw:create-server :backend :aserve ... :applications (list it.bese.ucw-user::*example-application* it.bese.ucw-user::*hello-world* ucw::*admin-application*) ... *** Entry point You need to define an entry point bound to an url. (defentry-point "index.ucw" (:application *hello-world*) () (call 'hello-world-home-page)) In this case UCW builds the url of the application with the name of the server, the port listening, the url prefix and the entry point. For instance: http://localhost:8080/hello/index.ucw APPLICATION specifies the application object this entry point will be attached to. If NIL *default-application* will be used, otherwise it must be the name of an existing application. Of course we can have different entry points for an application and in an entry point we can call a list of components, as in a chain. *** Component The component represents the page we want to model with our widgets. (defcomponent hello-world-home-page (simple-window-component) () (:default-initargs :title "Hello, World!")) If the page contains, for instance, a javascript, you can add it using the key ':javascript': (defcomponent hello-world-home-page (simple-window-component) () (:default-initargs :title "Hello, World!" :javascript "foo.js")) The file 'foo.js' will be searched in 'www-roots' path. The component creates an html page and calls the rendering method. *** Rendering of the component The method render allows to add to the empyty page, built by the component, subclassing the simple-window-component class, everything we want. (defmethod render ((hello hello-world-home-page)) (<:p "Hello, World!")) In our simple case the famous string "Hello, World!". ** Troubleshooting or debugging *** UCW not properly configured venus:~/dev/lisp/ucw_dev venus$ openmcl -l bin/start.lisp Error in process listener(1): Error component "arnesi" not found While executing: ASDF:FIND-SYSTEM Type :GO to continue, :POP to abort. If continued: Skip loading "bin/start.lisp" Type :? for other options. asdf doesn't know how to find arnesi. Get arnesi from here: darcs get http://common-lisp.net/project/bese/repos/arnesi_dev Then create a symlink from arnesi.asd to whatever systems directory you have on asdf:*central-registry*. ** Exercises a. Install the hello app in UCW server (read the quickstart companion). b. Create an application with a login form, search form and result list, logout component. c. Create a file foreach component of the application in point b). d. Interface a database (cl-sql) for the app in point b). e. Use TAL to separate the graphic layout and the business logic in the app in point b). ** Conclusions Of course, this document is not exaustive about the UCW development. The audience target is the people with very little experience of Lisp, downloading for the first time UCW, after the installation of the hello application asking "Well, and now?". UCW is a wonderful and powerful framework. That's all, folks! ** Getting help You can get help on IRC too. You can use ERC, an IRC implementation for Emacs. You can download it at the url http://sourceforge.net/projects/erc/ And you can find help at the url http://www.emacswiki.org/cgi-bin/wiki/EmacsIRCClient You can join to the channel #ucw or #lisp (or #lisp-it in italian language) on irc.freenode.net server. Or you can subscribe to the mailing list. ** Resources - darcs, http://www.darcs.net/ ** References [1] ANSI Common Lisp, P. Graham, 1996 Prentice Hall, ISBN 0-13-370875-6 ** Thanks - Marco Baringer (UCW author) - Luigi Panzeri (matley on #ucw) - Drew Crampsie (drewc on #ucw) ** History - 07-Dec-2005 rel. 0.3 added how to define package, www-roots, js - 13-Nov-2005 rel. 0.2 added Why Lisp for web applications - 12-Nov-2005 rel. 0.1 initial release ** Disclaimer ;; THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ;; "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ;; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ;; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ;; OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ;; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ;; LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ;; OF THIS DOCUMENT, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ** About me Alberto Santini, http://www.albertosantini.it/