#UncommonWeb #UCW #lisp #commonLisp #tutorial
Introduction
As is usual the first application I build using UCW is a simple "Hello World!" program. UnCommon Web (UCW) lets you accomplish this task in many ways, you can see that for yourself if you take a look at all the UCW examples available on the Internet, this may led you into confusion, as it did to me.
I strongly suggest you first to read the tutorial writed for Drew Crampsie which you can found in:
http://common-lisp.net/project/ucw/repos/ucw-core/doc/getting-started.txt
Here I want to use the UCW way of doing things, so I try to use the distinctive features of UCW in building this "hello World" program.
Since I don't already explain all the server and application definition stuff, i do it in the first part of this writing, and after that we enter into the application itself.
Server and application initialization
ucw-user or package
You can write your code directly in the ucw-user REPL, or if you prefer you can use a package for your code, here I use a package, so wen at the REPL I need to enter in that package.
;;Package stuff
(defpackage :com.lispsoft.hello
(:use :common-lisp
:ucw-core
:ucw-standard))
(in-package :com.lispsoft.hello)
I defined te com.lispsoft.package, which uses the ucw-core and ucw-standard UCW packages.
Then change to that package.
Server initialization
UCW uses backends and servers to run the applications:
(defclass hello-server (standard-server)
())
(defun make-hello-backend ()
(make-backend
:httpd
:host "localhost"
:port 8081))
(defun make-hello-server ()
(make-instance
'hello-server
:backend (make-hello-backend)))
(defparameter *hello-ucw-server* (make-hello-server))
I defined the make-hello-backend functions which creates a httpd UCW backend, listening on localhost in the port 8081.
Then, the function make-hello-server let us create a server using that backend.
The parameter \*hello-ucw-server\* is for easy the process of create and maintain a reference to the server instantiated.
Application definition
UCW let us run many applications into a single server, and each application can have their own characteristics:
(defclass hello-application (basic-application)
()
(:default-initargs
:url-prefix "/hello/"
))
(defparameter *hello-ucw-application* (make-instance 'hello-application))
;;Let the server know about application and enable app to run
(register-application *hello-ucw-server* *hello-ucw-application*)
Here created the hello-application application, which, according to the specifications of the hello-ucw-server and the url-prefix should be reached using the following url:
After that we created a parameter for easy the process of dealing with the application.
And, finally in this part, we register the application in the server.
Stop and start functions
Here two, optional, simple functions for start and stop our server.
(defun startup-hello ()
(startup-server *hello-ucw-server*))
(defun shutdown-hello ()
(shutdown-server *hello-ucw-server*))
The Hello world!
With all the precedent things we had a server and one application ready, that's necessary, but we don't have yet anything visible for the user, here we create the UCW Hello world application visible for the user.
But first some basic notions.
Components
UCW defines components as it's web user interface building blocks, they are in charge of the display or view part.
"Components are pieces of your interface, similar to 'widgets' or 'gadgets' in desktop GUI toolkits. One composes any number of components inside a top-level WINDOW-COMPONENT, and this component tree is then rendered to html and presented to the user.
UCW components are instances of CLOS classes with the STANDARD-COMPONENT-CLASS metaclass."1
The UCW components could be used in two ways:
1. Explicit metaclass
(defclass example-message ()
((message
:accessor message
:initarg :message
:initform "World!"))
(:metaclass standard-component-class))
2. Using the defcomponent macro from UCW
(defcomponent example-message ()
((message
:accessor message
:initarg :message
:initform "World!")))
I prefer the defcomponent way.
Window components
The window-components are the ones which are visible on the web for the user, they can, and should, contain more components for conform a UCW application useful for the user.
"WINDOW-COMPONENTS are the top-level wrappers. Generally, they print the html header and then delegate to another component for the rest. When nesting components, the slot that contains the component should be marked ":component t".1
The inheritance chain of the window-components family is:
standard-window-component -> basic-window-component -> basic-window-features-mixin + window-component
Most of the times all we need to use is standard-window-component since it provides you with more features four your windows.
If you want to know more about window-component you can dive into the UCW standard-components.lisp file, or read the "Component Oriented UI"2 post of Felideon.
The window-component for our Hello World!
Here is the window-component for our Hello World!:
(defcomponent hello-window (standard-window-component)
()
(:default-initargs
:body (make-instance 'hello-component)
:title "Hello UCW World!"
))
Here we created the hello-window component which, in this case, makes use of the :default-initargs, specifically sets a window title, and sets the :body slot to create a instance of the hello-component.
Thus, the hello-component looks this way:
(defcomponent hello-component ())
Pretty simple, but where is the content?, where is the "Hello World!"?
We need the render methods.
Rendering components
UCW has a protocol for rendering components, and their entry point is the generic function render. UCW provides render methods for some components, but we can specialize the render methods so we can get the output we want.
For the purposes of the "Hello World!" we have:
(defmethod render ((self hello-component))
(<:H1 "UCW - Hello World!"))
This renders the desired message.
Entry points
For expose the application to the web, we need to use another UCW tool, the entry-point. A entry-point is the tool for create a static and exposed url, so the user can enter into the application.
The entry-point for our application is:
(defentry-point "hello01.ucw" (:application *hello-ucw-application*) ()
(call 'hello-window))
With this the user can point their browser to the url:
http://localhost:8081/hello/hello01.ucw
And get the "Hello World!"
As you can see, the entry point call's the hello-window component and it uses their render method.
The call is one of the control flow operators which UCW provides.
Runing the UCW Hello World! application
For run this code you need to load it first, so put it into a single file, then load that file from ucw-user REPL, with C-c C-L, then call (startup-hello) and point your browser to the application url.
Enjoy!
Comments?
If you have comments or suggestions please write a comment here and I'll try to respond as soon as possible.
References
1. Crampsie, Drew, "Getting Started with UncommonWeb", (2016, Sept 06), [On line], Available: http://common-lisp.net/project/ucw/repos/ucw-core/doc/getting-started.txt