Caveman - A micro web framework for Common Lisp
Caveman is a micro web framework for Common Lisp, based on Clack.
@url GET "/hi" (defun say-hi (params) "Hello, World!")
Caveman is a micro web framework on Clack.
Why we should use "Framework" or something even if we already have Clack. You know Clack provides a very extensible environment for web application. We can build applications from isolated parts of Clack like kneading dough clay.
But Clack isn't a real framework. If you say that Clack is a collection of cells, Caveman is a newborn baby. Caveman provides a minimum set for building web applications. You can decorate the baby as you like, of course, and also you can replace any parts in it.
Caveman has following features:
- Easy to understand
Caveman is available on Quicklisp.
First, you have to generate a skeleton project.
Then a project skeleton is generated at
lib/myapp/. The new project can be loaded and runs on this state.
(ql:quickload :myapp) (myapp:start)
Now you can access to http://localhost:5000/ and then Caveman may show you "Hello, Caveman!".
Caveman provides an useful annotation "@url" to define a controller (You don't already know the meaning of "annotation"? Check cl-annot out). It has same rules to Clack.App.Route, it is an HTTP method paired with URL-matching pattern.
@url GET "/" (defun index (params) ...) @url POST "/" (defun index (params) ...) @url PUT "/" (defun index (params) ...) @url DELETE "/" (defun index (params) ...) @url OPTIONS "/" (defun index (params) ...) ;; For all methods @url ANY "/" (defun index (params) ...)
Route pattern may contain "keyword" to put the value into the argument.
@url GET "/hello/:name" (defun hello (params) (format nil "Hello, ~A" (getf params :name)))
The above controller will be invoked when you access to "/hello/Eitarow" and "/hello/Tomohiro", and then
(getf params :name) will be "Eitarow" and "Tomohiro".
Route patterns may also contain "wildcard" parameters. They are accessible to run
(getf params :splat).
@url GET "/say/*/to/*" (defun say (params) ; matches /say/hello/to/world (getf params :splat) ;=> ("hello" "world") ) @url GET "/download/*.*" (defun download () ; matches /download/path/to/file.xml (getf params :splat) ;=> ("path/to/file" "xml") )
Multiple values in params
If there are multiple values for the same key in query parameters (ex. ?item-id=1&item-id=2), the
param would be like
(:|item-id| 1 :|item-id| 2). However,
getf will return only the first one.
(getf '(:|item-id| 1 :|item-id| 2) :|item-id|) ;=> 1
For getting both of them as a list, multival-plist will help you.
(import 'multival-plist:getf-all) (getf-all '(:|item-id| 1 :|item-id| 2) :|item-id|) ;=> (1 2)
Normally, routes are matched in the order they are defined. Only the first route matched is invoked and rest of them just will be ignored. But, a route can punt processing to the next matching route using
@url GET "/guess/:who" (defun guess-me (params) (if (string= (getf params :who) "Eitarow") "You got me!" (next-route))) @url GET "/guess/*" (defun guess-anyone (params) "You missed!")
You can return following format as the result in actions.
- Clack's response list (containing Status, Headers and Body)
Caveman adopt CL-EMB as the default template engine. A package, named
myapp.view.emb, will be generated in your project which has one function
render. It is simply execute
emb:execute-emb and return the result as a string.
Of course, you can use other template engines, such as "cl-markup".
Caveman uses ".lisp" file as configuration file in
#p"config/" directory. When a project is just generated, you might be able to find
dev.lisp in it. It will be used when "start" the project application with "dev" mode.
;; config/dev.lisp `(:static-path #p"static/" :log-path #p"log/" :template-path #p"templates/" :application-root ,(asdf:component-pathname (asdf:find-system :myapp)) :server :hunchentoot :port 5000 :database-type :sqlite3 :database-connection-spec (,(namestring (asdf:system-relative-pathname :myapp "sqlite3.db"))))
Obviously, this is just a plist. You can use following keys in there.
:application-root(Pathname): Pathname of the application root.
:static-path(Pathname): Relative pathname of a static files directory from the root.
:log-path(Pathname): Relative pathname of a log files directory from the root.
:template-path(Pathname): Relative pathname of a template directory from the root.
:port(Integer): Server port.
:server(Keyword): Clack.Handler's server type. (ex.
And following stuffs will be used by Clack.Middleware.Clsql for integrating CLSQL.
You can access to the configuration plist anywhere, by using
(caveman:config) ;;=> (:static-path #p"public/" :template-path ...) (caveman:config :server) ;;=> :hunchentoot
caveman:*session* is a hash table which represents a session for the current user.
Extend the Context
- Eitarow Fukamachi (email@example.com)
- Tomohiro Matsuyama (firstname.lastname@example.org)
Copyright (c) 2011 Eitarow Fukamachi
Licensed under the LLGPL License.