The caveman2 Reference Manual

Next: , Previous: , Up: (dir)   [Contents][Index]

The caveman2 Reference Manual

This is the caveman2 Reference Manual, version 2.4.0, generated automatically by Declt version 4.0 beta 2 "William Riker" on Wed Jun 15 03:23:17 2022 GMT+0.

Table of Contents


1 Introduction

Caveman2 - Lightweight web application framework

Build Status

Usage

(defparameter *web* (make-instance '<app>))

@route GET "/"
(defun index ()
  (render #P"index.tmpl"))

@route GET "/hello"
(defun say-hello (&key (|name| "Guest"))
  (format nil "Hello, ~A" |name|))

About Caveman2

What's different from Caveman "1"?

Everything. Caveman2 was written from scratch.

These are noteworthy points.

The reason I wrote it from scratch:

One of the most frequently asked questions was "Which should I use: ningle or Caveman? What are the differences?" I think these were asked so frequently because Caveman and ningle were too similar. Both of them are called "micro", and had no database support.

With Caveman2, Caveman is no longer a "micro" web application framework. It supports CL-DBI, and has database connection management by default. Caveman has started growing up.

Design Goal

Caveman is intended to be a collection of common parts of web applications. With Caveman2, I use three rules to make decisions:

Quickstart

You came here because you're interested in living like a caveman, right? This isn't Disneyland, but we can start here. Let's get into a cave!

Installation

Caveman2 is now available on Quicklisp.

(ql:quickload :caveman2)

Generating a project skeleton

(caveman2:make-project #P"/path/to/myapp/"
                       :author "<Your full name>")
;-> writing /path/to/myapp/.gitignore
;   writing /path/to/myapp/README.markdown
;   writing /path/to/myapp/app.lisp
;   writing /path/to/myapp/db/schema.sql
;   writing /path/to/myapp/shlyfile.lisp
;   writing /path/to/myapp/myapp-test.asd
;   writing /path/to/myapp/myapp.asd
;   writing /path/to/myapp/src/config.lisp
;   writing /path/to/myapp/src/db.lisp
;   writing /path/to/myapp/src/main.lisp
;   writing /path/to/myapp/src/view.lisp
;   writing /path/to/myapp/src/web.lisp
;   writing /path/to/myapp/static/css/main.css
;   writing /path/to/myapp/t/myapp.lisp
;   writing /path/to/myapp/templates/_errors/404.html
;   writing /path/to/myapp/templates/index.tmpl
;   writing /path/to/myapp/templates/layout/default.tmpl

Start a server

This is an example that assumes that the name of your application is "myapp". Before starting the server, you must first load your app.

(ql:quickload :myapp)

Your application has functions named start and stop to start/stop your web application.

(myapp:start :port 8080)

As Caveman is based on Clack/Lack, you can choose which server to run on -- Hunchentoot, Woo or Wookie, etc.

(myapp:start :server :hunchentoot :port 8080)
(myapp:start :server :fcgi :port 8080)

I recommend you use Hunchentoot on a local machine, and use Woo in a production environment.

You can also start your application by using clackup command.

$ ros install clack
$ which clackup
/Users/nitro_idiot/.roswell/bin/clackup

$ APP_ENV=development clackup --server :fcgi --port 8080 app.lisp

Routing

Caveman2 provides 2 ways to define a route -- @route and defroute. You can use either.

@route is an annotation macro, defined by using cl-annot. It takes a method, a URL-string, and a function.

@route GET "/"
(defun index ()
  ...)

;; A route with no name.
@route GET "/welcome"
(lambda (&key (|name| "Guest"))
  (format nil "Welcome, ~A" |name|))

This is similar to Caveman1's @url except for its argument list. You don't have to specify an argument when it is not required.

defroute is just a macro. It provides the same functionality as @route.

(defroute index "/" ()
  ...)

;; A route with no name.
(defroute "/welcome" (&key (|name| "Guest"))
  (format nil "Welcome, ~A" |name|))

Since Caveman bases on ningle, Caveman also has the Sinatra-like routing system.

;; GET request (default)
@route GET "/" (lambda () ...)
(defroute ("/" :method :GET) () ...)

;; POST request
@route POST "/" (lambda () ...)
(defroute ("/" :method :POST) () ...)

;; PUT request
@route PUT "/" (lambda () ...)
(defroute ("/" :method :PUT) () ...)

;; DELETE request
@route DELETE "/" (lambda () ...)
(defroute ("/" :method :DELETE) () ...)

;; OPTIONS request
@route OPTIONS "/" (lambda () ...)
(defroute ("/" :method :OPTIONS) () ...)

;; For all methods
@route ANY "/" (lambda () ...)
(defroute ("/" :method :ANY) () ...)

Route patterns may contain "keywords" to put the value into the argument.

(defroute "/hello/:name" (&key name)
  (format nil "Hello, ~A" name))

The above controller will be invoked when you access "/hello/Eitaro" or "/hello/Tomohiro", and name will be "Eitaro" or "Tomohiro", as appropriate.

(&key name) is almost same as a lambda list of Common Lisp, except it always allows other keys.

(defroute "/hello/:name" (&rest params &key name)
  ;; ...
  )

Route patterns may also contain "wildcard" parameters. They are accessible by using splat.

(defroute "/say/*/to/*" (&key splat)
  ; matches /say/hello/to/world
  (format nil "~A" splat))
;=> (hello world)

(defroute "/download/*.*" (&key splat)
  ; matches /download/path/to/file.xml
  (format nil "~A" splat)) 
;=> (path/to/file xml)

If you'd like to write use a regular expression in a URL rule, :regexp t should work.

(defroute ("/hello/([\\w]+)" :regexp t) (&key captures)
  (format nil "Hello, ~A!" (first captures)))

Normally, routes are tested for a match in the order they are defined, and only the first route matched is invoked, with the following routes being ignored. However, a route can continue testing for matches in the list, by including next-route.

(defroute "/guess/:who" (&key who)
  (if (string= who "Eitaro")
      "You got me!"
      (next-route)))

(defroute "/guess/*" ()
  "You missed!")

You can return following formats as the result of defroute.

Redirection

Redirect to another route with(redirect "url"). A second optional argument is the status code, 302 by default.

Reverse URLs

When you defined routes with names, you can find the URL from a name with (url-for route-name &rest params).

The function will throw an error if no route is found.

More helper functions

See also:

Structured query/post parameters

Parameter keys containing square brackets ("[" & "]") will be parsed as structured parameters. You can access the parsed parameters as _parsed in routers.

<form action="/edit">
  <input type="name" name="person[name]" />
  <input type="name" name="person[email]" />
  <input type="name" name="person[birth][year]" />
  <input type="name" name="person[birth][month]" />
  <input type="name" name="person[birth][day]" />
</form>
(defroute "/edit" (&key _parsed)
  (format nil "~S" (cdr (assoc "person" _parsed :test #'string=))))
;=> "((\"name\" . \"Eitaro\") (\"email\" . \"e.arrows@gmail.com\") (\"birth\" . ((\"year\" . 2000) (\"month\" . 1) (\"day\" . 1))))"

Blank keys mean they have multiple values.

<form action="/add">
  <input type="text" name="items[][name]" />
  <input type="text" name="items[][price]" />

  <input type="text" name="items[][name]" />
  <input type="text" name="items[][price]" />

  <input type="submit" value="Add" />
</form>
(defroute "/add" (&key _parsed)
  (format nil "~S" (assoc "items" _parsed :test #'string=)))
;=> "(((\"name\" . \"WiiU\") (\"price\" . \"30000\")) ((\"name\" . \"PS4\") (\"price\" . \"69000\")))"

Templates

Caveman uses Djula as its default templating engine.

{% extends "layouts/default.html" %}
{% block title %}Users | MyApp{% endblock %}
{% block content %}
<div id="main">
  <ul>
  {% for user in users %}
    <li><a href="{{ user.url }}">{{ user.name }}</a></li>
  {% endfor %}
  </ul>
</div>
{% endblock %}
(import 'myapp.view:render)

(render #P"users.html"
        '(:users ((:url "/id/1"
                   :name "nitro_idiot")
                  (:url "/id/2"
                   :name "meymao"))
          :has-next-page T))

If you want to get something from a database or execute a function using Djula you must explicity call list when passing the arguments to render so that the code executes.

(import 'myapp.view:render)

(render #P"users.html"
        (list :users (get-users-from-db)))

JSON API

This is an example of a JSON API.

(defroute "/user.json" (&key |id|)
  (let ((person (find-person-from-db |id|)))
    ;; person => (:|name| "Eitaro Fukamachi" :|email| "e.arrows@gmail.com")
    (render-json person)))

;=> {"name":"Eitaro Fukamachi","email":"e.arrows@gmail.com"}

render-json is a part of a skeleton project. You can find its code in "src/view.lisp".

Static file

Images, CSS, JS, favicon.ico and robot.txt in "static/" directory will be served by default.

/images/logo.png => {PROJECT_ROOT}/static/images/logo.png
/css/main.css    => {PROJECT_ROOT}/static/css/main.css
/js/app/index.js => {PROJECT_ROOT}/static/js/app/index.js
/robot.txt       => {PROJECT_ROOT}/static/robot.txt
/favicon.ico     => {PROJECT_ROOT}/static/favicon.ico

You can change these rules by rewriting "PROJECT_ROOT/app.lisp". See Clack.Middleware.Static for detail.

Configuration

Caveman adopts Envy as a configuration switcher. This allows definition of multiple configurations and switching between them according to an environment variable.

This is a typical example:

(defpackage :myapp.config
  (:use :cl
        :envy))
(in-package :myapp.config)

(setf (config-env-var) "APP_ENV")

(defconfig :common
  `(:application-root ,(asdf:component-pathname (asdf:find-system :myapp))))

(defconfig |development|
  `(:debug T
    :databases
    ((:maindb :sqlite3 :database-name ,(merge-pathnames #P"test.db"
                                                        *application-root*)))))

(defconfig |production|
  '(:databases
    ((:maindb :mysql :database-name "myapp" :username "whoami" :password "1234")
     (:workerdb :mysql :database-name "jobs" :username "whoami" :password "1234"))))

(defconfig |staging|
  `(:debug T
    ,@|production|))

Every configuration is a property list. You can choose the configuration which to use by setting APP_ENV.

To get a value from the current configuration, call myapp.config:config with the key you want.

(import 'myapp.config:config)

(setf (osicat:environment-variable "APP_ENV") "development")
(config :debug)
;=> T

Database

When you add :databases to the configuration, Caveman enables database support. :databases is an association list of database settings.

(defconfig |production|
  '(:databases
    ((:maindb :mysql :database-name "myapp" :username "whoami" :password "1234")
     (:workerdb :mysql :database-name "jobs" :username "whoami" :password "1234"))))

db in a package myapp.db is a function for connecting to each databases configured the above. Here is an example.

(use-package '(:myapp.db :sxql :datafly))

(defun search-adults ()
  (with-connection (db)
    (retrieve-all
      (select :*
        (from :person)
        (where (:>= :age 20))))))

The connection is alive during the Lisp session, and will be reused in every HTTP request.

retrieve-all and the query language came from datafly and SxQL. See those sets of documentation for more information.

Set HTTP headers or HTTP status

There are several special variables available during a HTTP request. *request* and *response* represent a request and a response. If you are familiar with Clack, these are instances of subclasses of Clack.Request and Clack.Response.

(use-package :caveman2)

;; Get a value of Referer header.
(http-referer *request*)

;; Set Content-Type header.
(setf (getf (response-headers *response*) :content-type) "application/json")

;; Set HTTP status.
(setf (status *response*) 304)

If you would like to set Content-Type "application/json" for all "*.json" requests, next-route can be used.

(defroute "/*.json" ()
  (setf (getf (response-headers *response*) :content-type) "application/json")
  (next-route))

(defroute "/user.json" () ...)
(defroute "/search.json" () ...)
(defroute ("/new.json" :method :POST) () ...)

Using session

Session data is for memorizing user-specific data. *session* is a hash table that stores session data.

This example increments :counter in the session, and displays it for each visitor.

(defroute "/counter" ()
  (format nil "You came here ~A times."
          (incf (gethash :counter *session* 0))))

Caveman2 stores session data in-memory by default. To change this, specify :store to :session in "PROJECT_ROOT/app.lisp".

This example uses RDBMS to store session data.

      '(:backtrace
        :output (getf (config) :error-log))
      nil)
- :session
+ (:session
+  :store (make-dbi-store :connector (lambda ()
+                                      (apply #'dbi:connect
+                                             (myapp.db:connection-settings)))))
  (if (productionp)
      nil
      (lambda (app)

NOTE: Don't forget to add :lack-session-store-dbi as :depends-on of your app. It is not a part of Clack/Lack.

See the source code of Lack.Session.Store.DBi for more information.

Throw an HTTP status code

(import 'caveman2:throw-code)

(defroute ("/auth" :method :POST) (&key |name| |password|)
  (unless (authorize |name| |password|)
    (throw-code 403)))

Specify error pages

To specify error pages for 404, 500 or such, define a method on-exception of your app.

(defmethod on-exception ((app <web>) (code (eql 404)))
  (declare (ignore app code))
  (merge-pathnames #P"_errors/404.html"
                   *template-directory*))

Hot Deployment

Though Caveman doesn't have a feature for hot deployment, Server::Starter -- a Perl module -- makes it easy.

$ APP_ENV=production start_server --port 8080 -- clackup --server :fcgi app.lisp

NOTE: Server::Starter requires the server to support binding on a specific fd, which means only :fcgi and :woo are the ones work with start_server command.

To restart the server, send HUP signal (kill -HUP <pid>) to the start_server process.

Error Log

Caveman outputs error backtraces to a file which is specified at :error-log in your configuration.

(defconfig |default|
  `(:error-log #P"/var/log/apps/myapp_error.log"
    :databases
    ((:maindb :sqlite3 :database-name ,(merge-pathnames #P"myapp.db"
                                                        *application-root*)))))

Use another templating library

CL-WHO

(import 'cl-who:with-html-output-to-string)

(defroute "/" ()
  (with-html-output-to-string (output nil :prologue t)
    (:html
      (:head (:title "Welcome to Caveman!"))
      (:body "Blah blah blah."))))
;=> "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">
;   <html><head><title>Welcome to Caveman!</title></head><body>Blah blah blah.</body></html>"

CL-Markup

(import 'cl-markup:xhtml)

(defroute "/" ()
  (xhtml
    (:head (:title "Welcome to Caveman!"))
    (:body "Blah blah blah.")))
;=> "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html><head><title>Welcome to Caveman!</title></head><body>Blah blah blah.</body></html>"

cl-closure-template

{namespace myapp.view}

{template renderIndex}
<!DOCTYPE html>
<html>
<head>
  <title>"Welcome to Caveman!</title>
</head>
<body>
  Blah blah blah.
</body>
</html>
{/template}
(import 'myapp.config:*template-directory*)

(closure-template:compile-cl-templates (merge-pathnames #P"index.tmpl"
                                                        *template-directory*))

(defroute "/" ()
  (myapp.view:render-index))

See Also

Author

License

Licensed under the LLGPL License.


2 Systems

The main system appears first, followed by any subsystem dependency.


Previous: , Up: Systems   [Contents][Index]

2.1 caveman2

Lightweight web application framework

Author

Eitaro Fukamachi

License

LLGPL

Long Description

# Caveman2 - Lightweight web application framework

[![Build Status](https://travis-ci.org/fukamachi/caveman.svg?branch=master)](https://travis-ci.org/fukamachi/caveman)

## Usage

“‘common-lisp
(defparameter *web* (make-instance ’<app>))

@route GET "/"
(defun index ()
(render #P"index.tmpl"))

@route GET "/hello"
(defun say-hello (&key (|name| "Guest"))
(format nil "Hello, ~A" |name|))
“‘

## About Caveman2

### What’s different from Caveman "1"?

Everything. Caveman2 was written from scratch.

These are noteworthy points.

* Is based on [ningle](http://8arrow.org/ningle/)
* Has database integration
* Uses new, separate configuration system ([Envy](https://github.com/fukamachi/envy))
* Has new routing macro

### The reason I wrote it from scratch:

One of the most frequently asked questions was "Which should I use: ningle or Caveman? What are the differences?" I think these were asked so frequently because Caveman and ningle were too similar. Both of them are called "micro", and had no database support.

With Caveman2, Caveman is no longer a "micro" web application framework. It supports CL-DBI, and has database connection management by default. Caveman has started growing up.

## Design Goal

Caveman is intended to be a collection of common parts of web applications. With Caveman2, I use three rules to make decisions:

* Be extensible.
* Be practical.
* Don’t force anything.

## Quickstart

You came here because you’re interested in living like a caveman, right? This isn’t Disneyland, but we can start here. Let’s get into a cave!

### Installation

Caveman2 is now available on [Quicklisp](https://www.quicklisp.org/beta/).

“‘common-lisp
(ql:quickload :caveman2)
“‘

### Generating a project skeleton

“‘common-lisp
(caveman2:make-project #P"/path/to/myapp/"
:author "<Your full name>")
;-> writing /path/to/myapp/.gitignore
; writing /path/to/myapp/README.markdown
; writing /path/to/myapp/app.lisp
; writing /path/to/myapp/db/schema.sql
; writing /path/to/myapp/shlyfile.lisp
; writing /path/to/myapp/myapp-test.asd
; writing /path/to/myapp/myapp.asd
; writing /path/to/myapp/src/config.lisp
; writing /path/to/myapp/src/db.lisp
; writing /path/to/myapp/src/main.lisp
; writing /path/to/myapp/src/view.lisp
; writing /path/to/myapp/src/web.lisp
; writing /path/to/myapp/static/css/main.css
; writing /path/to/myapp/t/myapp.lisp
; writing /path/to/myapp/templates/_errors/404.html
; writing /path/to/myapp/templates/index.tmpl
; writing /path/to/myapp/templates/layout/default.tmpl
“‘

### Start a server
This is an example that assumes that the name of your application is "myapp".
Before starting the server, you must first load your app.

“‘common-lisp
(ql:quickload :myapp)
“‘

Your application has functions named ‘start‘ and ‘stop‘ to start/stop your web application.

“‘common-lisp
(myapp:start :port 8080)
“‘

As Caveman is based on Clack/Lack, you can choose which server to run on – Hunchentoot, Woo or Wookie, etc.

“‘common-lisp
(myapp:start :server :hunchentoot :port 8080)
(myapp:start :server :fcgi :port 8080)
“‘

I recommend you use Hunchentoot on a local machine, and use Woo in a production environment.

You can also start your application by using [clackup command](https://github.com/fukamachi/clack/blob/master/roswell/clackup.ros).

$ ros install clack
$ which clackup
/Users/nitro_idiot/.roswell/bin/clackup

$ APP_ENV=development clackup –server :fcgi –port 8080 app.lisp

### Routing

Caveman2 provides 2 ways to define a route – ‘@route‘ and ‘defroute‘. You can use either.

‘@route‘ is an annotation macro, defined by using [cl-annot](https://github.com/arielnetworks/cl-annot). It takes a method, a URL-string, and a function.

“‘common-lisp
@route GET "/"
(defun index ()
...)

;; A route with no name.
@route GET "/welcome"
(lambda (&key (|name| "Guest"))
(format nil "Welcome, ~A" |name|))
“‘

This is similar to Caveman1’s ‘@url‘ except for its argument list. You don’t have to specify an argument when it is not required.

‘defroute‘ is just a macro. It provides the same functionality as ‘@route‘.

“‘common-lisp
(defroute index "/" ()
...)

;; A route with no name.
(defroute "/welcome" (&key (|name| "Guest"))
(format nil "Welcome, ~A" |name|))
“‘

Since Caveman bases on ningle, Caveman also has the [Sinatra](http://www.sinatrarb.com/)-like routing system.

“‘common-lisp
;; GET request (default)
@route GET "/" (lambda () ...)
(defroute ("/" :method :GET) () ...)

;; POST request
@route POST "/" (lambda () ...)
(defroute ("/" :method :POST) () ...)

;; PUT request
@route PUT "/" (lambda () ...)
(defroute ("/" :method :PUT) () ...)

;; DELETE request
@route DELETE "/" (lambda () ...)
(defroute ("/" :method :DELETE) () ...)

;; OPTIONS request
@route OPTIONS "/" (lambda () ...)
(defroute ("/" :method :OPTIONS) () ...)

;; For all methods
@route ANY "/" (lambda () ...)
(defroute ("/" :method :ANY) () ...)
“‘

Route patterns may contain "keywords" to put the value into the argument.

“‘common-lisp
(defroute "/hello/:name" (&key name)
(format nil "Hello, ~A" name))
“‘

The above controller will be invoked when you access "/hello/Eitaro" or "/hello/Tomohiro", and ‘name‘ will be "Eitaro" or "Tomohiro", as appropriate.

‘(&key name)‘ is almost same as a lambda list of Common Lisp, except it always allows other keys.

“‘common-lisp
(defroute "/hello/:name" (&rest params &key name)
;; ...
)
“‘

Route patterns may also contain "wildcard" parameters. They are accessible by using ‘splat‘.

“‘common-lisp
(defroute "/say/*/to/*" (&key splat)
; matches /say/hello/to/world
(format nil "~A" splat))
;=> (hello world)

(defroute "/download/*.*" (&key splat)
; matches /download/path/to/file.xml
(format nil "~A" splat))
;=> (path/to/file xml)
“‘

If you’d like to write use a regular expression in a URL rule, ‘:regexp t‘ should work.

“‘common-lisp
(defroute ("/hello/([\\w]+)" :regexp t) (&key captures)
(format nil "Hello, ~A!" (first captures)))
“‘

Normally, routes are tested for a match in the order they are defined, and only the first route matched is invoked, with the following routes being ignored. However, a route can continue testing for matches in the list, by including ‘next-route‘.

“‘common-lisp
(defroute "/guess/:who" (&key who)
(if (string= who "Eitaro")
"You got me!"
(next-route)))

(defroute "/guess/*" ()
"You missed!")
“‘

You can return following formats as the result of ‘defroute‘.

* String
* Pathname
* Clack’s response list (containing Status, Headers and Body)

### Redirection

Redirect to another route with‘(redirect "url")‘. A second optional argument is the status code, 302 by default.

### Reverse URLs

When you defined routes with names, you can find the URL from a name with ‘(url-for route-name &rest params)‘.

The function will throw an error if no route is found.

### More helper functions

See also:

- ‘add-query-parameters base-url params‘

### Structured query/post parameters

Parameter keys containing square brackets ("[" & "]") will be parsed as structured parameters. You can access the parsed parameters as ‘_parsed‘ in routers.

“‘html
<form action="/edit">
<input type="name" name="person[name]" />
<input type="name" name="person[email]" />
<input type="name" name="person[birth][year]" />
<input type="name" name="person[birth][month]" />
<input type="name" name="person[birth][day]" />
</form>
“‘

“‘common-lisp
(defroute "/edit" (&key _parsed)
(format nil "~S" (cdr (assoc "person" _parsed :test #’string=))))
;=> "((\"name\" . \"Eitaro\") (\"email\" . \"e.arrows@gmail.com\") (\"birth\" . ((\"year\" . 2000) (\"month\" . 1) (\"day\" . 1))))"
“‘

Blank keys mean they have multiple values.

“‘html
<form action="/add">
<input type="text" name="items[][name]" />
<input type="text" name="items[][price]" />

<input type="text" name="items[][name]" />
<input type="text" name="items[][price]" />

<input type="submit" value="Add" />
</form>
“‘

“‘common-lisp
(defroute "/add" (&key _parsed)
(format nil "~S" (assoc "items" _parsed :test #’string=)))
;=> "(((\"name\" . \"WiiU\") (\"price\" . \"30000\")) ((\"name\" . \"PS4\") (\"price\" . \"69000\")))"
“‘

### Templates

Caveman uses [Djula](http://mmontone.github.io/djula/djula/) as its default templating engine.

“‘html
{% extends "layouts/default.html" %}
{% block title %}Users | MyApp{% endblock %}
{% block content %}
<div id="main">
<ul>
{% for user in users %}
<li><a href="{{ user.url }}">{{ user.name }}</a></li>
{% endfor %}
</ul>
</div>
{% endblock %}
“‘

“‘common-lisp
(import ’myapp.view:render)

(render #P"users.html"
’(:users ((:url "/id/1"
:name "nitro_idiot")
(:url "/id/2"
:name "meymao"))
:has-next-page T))
“‘

If you want to get something from a database or execute a function using [Djula](http://mmontone.github.io/djula/) you must explicity call ‘list‘ when passing the arguments to render so that the code executes.

“‘common-lisp
(import ’myapp.view:render)

(render #P"users.html"
(list :users (get-users-from-db)))
“‘

### JSON API

This is an example of a JSON API.

“‘common-lisp
(defroute "/user.json" (&key |id|)
(let ((person (find-person-from-db |id|)))
;; person => (:|name| "Eitaro Fukamachi" :|email| "e.arrows@gmail.com")
(render-json person)))

;=> {"name":"Eitaro Fukamachi","email":"e.arrows@gmail.com"}
“‘

‘render-json‘ is a part of a skeleton project. You can find its code in "src/view.lisp".

### Static file

Images, CSS, JS, favicon.ico and robot.txt in "static/" directory will be served by default.

“‘
/images/logo.png => {PROJECT_ROOT}/static/images/logo.png
/css/main.css => {PROJECT_ROOT}/static/css/main.css
/js/app/index.js => {PROJECT_ROOT}/static/js/app/index.js
/robot.txt => {PROJECT_ROOT}/static/robot.txt
/favicon.ico => {PROJECT_ROOT}/static/favicon.ico
“‘

You can change these rules by rewriting "PROJECT_ROOT/app.lisp". See [Clack.Middleware.Static](http://quickdocs.org/clack/api#package-CLACK.MIDDLEWARE.STATIC) for detail.

### Configuration

Caveman adopts [Envy](https://github.com/fukamachi/envy) as a configuration switcher. This allows definition of multiple configurations and switching between them according to an environment variable.

This is a typical example:

“‘common-lisp
(defpackage :myapp.config
(:use :cl
:envy))
(in-package :myapp.config)

(setf (config-env-var) "APP_ENV")

(defconfig :common
‘(:application-root ,(asdf:component-pathname (asdf:find-system :myapp))))

(defconfig |development|
‘(:debug T
:databases
((:maindb :sqlite3 :database-name ,(merge-pathnames #P"test.db"
*application-root*)))))

(defconfig |production|
’(:databases
((:maindb :mysql :database-name "myapp" :username "whoami" :password "1234")
(:workerdb :mysql :database-name "jobs" :username "whoami" :password "1234"))))

(defconfig |staging|
‘(:debug T
,@|production|))
“‘

Every configuration is a property list. You can choose the configuration which to use by setting ‘APP_ENV‘.

To get a value from the current configuration, call ‘myapp.config:config‘ with the key you want.

“‘common-lisp
(import ’myapp.config:config)

(setf (osicat:environment-variable "APP_ENV") "development")
(config :debug)
;=> T
“‘

### Database

When you add ‘:databases‘ to the configuration, Caveman enables database support. ‘:databases‘ is an association list of database settings.

“‘common-lisp
(defconfig |production|
’(:databases
((:maindb :mysql :database-name "myapp" :username "whoami" :password "1234")
(:workerdb :mysql :database-name "jobs" :username "whoami" :password "1234"))))
“‘

‘db‘ in a package ‘myapp.db‘ is a function for connecting to each databases configured the above. Here is an example.

“‘common-lisp
(use-package ’(:myapp.db :sxql :datafly))

(defun search-adults ()
(with-connection (db)
(retrieve-all
(select :*
(from :person)
(where (:>= :age 20))))))
“‘

The connection is alive during the Lisp session, and will be reused in every HTTP request.

‘retrieve-all‘ and the query language came from [datafly](https://github.com/fukamachi/datafly) and [SxQL](https://github.com/fukamachi/sxql). See those sets of documentation for more information.

### Set HTTP headers or HTTP status

There are several special variables available during a HTTP request. ‘*request*‘ and ‘*response*‘ represent a request and a response. If you are familiar with [Clack](http://clacklisp.org/), these are instances of subclasses of [Clack.Request](http://quickdocs.org/clack/api#package-CLACK.REQUEST) and [Clack.Response](http://quickdocs.org/clack/api#package-CLACK.RESPONSE).

“‘common-lisp
(use-package :caveman2)

;; Get a value of Referer header.
(http-referer *request*)

;; Set Content-Type header.
(setf (getf (response-headers *response*) :content-type) "application/json")

;; Set HTTP status.
(setf (status *response*) 304)
“‘

If you would like to set Content-Type "application/json" for all "*.json" requests, ‘next-route‘ can be used.

“‘common-lisp
(defroute "/*.json" ()
(setf (getf (response-headers *response*) :content-type) "application/json")
(next-route))

(defroute "/user.json" () ...)
(defroute "/search.json" () ...)
(defroute ("/new.json" :method :POST) () ...)
“‘

### Using session

Session data is for memorizing user-specific data. ‘*session*‘ is a hash table that stores session data.

This example increments ‘:counter‘ in the session, and displays it for each visitor.

“‘common-lisp
(defroute "/counter" ()
(format nil "You came here ~A times."
(incf (gethash :counter *session* 0))))
“‘

Caveman2 stores session data in-memory by default. To change this, specify ‘:store‘ to ‘:session‘ in "PROJECT_ROOT/app.lisp".

This example uses RDBMS to store session data.

“‘diff
’(:backtrace
:output (getf (config) :error-log))
nil)
- :session
+ (:session
+ :store (make-dbi-store :connector (lambda ()
+ (apply #’dbi:connect
+ (myapp.db:connection-settings)))))
(if (productionp)
nil
(lambda (app)
“‘

NOTE: Don’t forget to add ‘:lack-session-store-dbi‘ as ‘:depends-on‘ of your app. It is not a part of Clack/Lack.

See the source code of Lack.Session.Store.DBi for more information.

- [Lack.Session.Store.Dbi](https://github.com/fukamachi/lack/blob/master/src/middleware/session/store/dbi.lisp)

### Throw an HTTP status code

“‘common-lisp
(import ’caveman2:throw-code)

(defroute ("/auth" :method :POST) (&key |name| |password|)
(unless (authorize |name| |password|)
(throw-code 403)))
“‘

### Specify error pages

To specify error pages for 404, 500 or such, define a method ‘on-exception‘ of your app.

“‘common-lisp
(defmethod on-exception ((app <web>) (code (eql 404)))
(declare (ignore app code))
(merge-pathnames #P"_errors/404.html"
*template-directory*))
“‘

### Hot Deployment

Though Caveman doesn’t have a feature for hot deployment, [Server::Starter](http://search.cpan.org/~kazuho/Server-Starter-0.15/lib/Server/Starter.pm) – a Perl module – makes it easy.

$ APP_ENV=production start_server –port 8080 – clackup –server :fcgi app.lisp

NOTE: Server::Starter requires the server to support binding on a specific fd, which means only ‘:fcgi‘ and ‘:woo‘ are the ones work with ‘start_server‘ command.

To restart the server, send HUP signal (‘kill -HUP <pid>‘) to the ‘start_server‘ process.

### Error Log

Caveman outputs error backtraces to a file which is specified at ‘:error-log‘ in your configuration.

“‘common-lisp
(defconfig |default|
‘(:error-log #P"/var/log/apps/myapp_error.log"
:databases
((:maindb :sqlite3 :database-name ,(merge-pathnames #P"myapp.db"
*application-root*)))))
“‘

## Use another templating library

### CL-WHO

“‘common-lisp
(import ’cl-who:with-html-output-to-string)

(defroute "/" ()
(with-html-output-to-string (output nil :prologue t)
(:html
(:head (:title "Welcome to Caveman!"))
(:body "Blah blah blah."))))
;=> "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">
; <html><head><title>Welcome to Caveman!</title></head><body>Blah blah blah.</body></html>"
“‘

* [CL-WHO Website](http://weitz.de/cl-who/)

### CL-Markup

“‘common-lisp
(import ’cl-markup:xhtml)

(defroute "/" ()
(xhtml
(:head (:title "Welcome to Caveman!"))
(:body "Blah blah blah.")))
;=> "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html><head><title>Welcome to Caveman!</title></head><body>Blah blah blah.</body></html>"
“‘

* [CL-Markup repository](https://github.com/arielnetworks/cl-markup)

### cl-closure-template

“‘html
{namespace myapp.view}

{template renderIndex}
<!DOCTYPE html>
<html>
<head>
<title>"Welcome to Caveman!</title>
</head>
<body>
Blah blah blah.
</body>
</html>
{/template}
“‘

“‘common-lisp
(import ’myapp.config:*template-directory*)

(closure-template:compile-cl-templates (merge-pathnames #P"index.tmpl"
*template-directory*))

(defroute "/" ()
(myapp.view:render-index))
“‘

* [cl-closure-template](http://quickdocs.org/cl-closure-template/)
* [Closure Templates Documentation](https://developers.google.com/closure/templates/docs/overview)

<!– Commenting out because these are old.

## Use another database library

### CLSQL

You can use Lack.Middleware.Clsql to use CLSQL in Clack compliant application.

In Caveman, add the middleware to ‘builder‘ in "PROJECT_ROOT/app.lisp".

“‘common-lisp
(ql:quickload :clack-middleware-clsql)
(import ’clack.middleware.clsql:<clack-middleware-clsql>)

(builder
(<clack-middleware-clsql>
:database-type :mysql
:connection-spec ’("localhost" "db" "fukamachi" "password"))
*web*)
“‘

* [Clack.Middleware.Clsql](http://quickdocs.org/clack/api#system-clack-middleware-clsql)
* [CLSQL: Common Lisp SQL Interface](http://clsql.b9.com/)

### Postmodern

You can use Clack.Middleware.Postmodern to use Postmodern in Clack compliant application.

In Caveman, add the middleware to ‘builder‘ in "PROJECT_ROOT/app.lisp".

“‘common-lisp
(ql:quickload :clack-middleware-postmodern)
(import ’clack.middleware.postmodern:<clack-middleware-postmodern>)

(builder
(<clack-middleware-postmodern>
:database "database-name"
:user "database-user"
:password "database-password"
:host "remote-address")
*web*)
“‘

* [Clack.Middleware.Postmodern](http://quickdocs.org/clack/api#system-clack-middleware-postmodern)
* [Postmodern](http://marijnhaverbeke.nl/postmodern/)

–>

## See Also

* [Clack](http://clacklisp.org/) - Web application environment.
* [Lack](https://github.com/fukamachi/lack) - The core of Clack.
* [ningle](http://8arrow.org/ningle/) - Super micro web application framework that Caveman is based on.
* [Djula](http://mmontone.github.io/djula/) - HTML Templating engine.
* [CL-DBI](http://8arrow.org/cl-dbi/) - Database-independent interface library.
* [SxQL](http://8arrow.org/sxql/) - SQL builder library.
* [Envy](https://github.com/fukamachi/envy) - Configuration switcher.
* [Roswell](https://github.com/snmsts/roswell) - Common Lisp implementation manager.

## Author

* Eitaro Fukamachi (e.arrows@gmail.com)

# License

Licensed under the LLGPL License.

Version

2.4.0

Dependencies
  • ningle (system).
  • lack-request (system).
  • lack-response (system).
  • cl-project (system).
  • dbi (system).
  • cl-syntax-annot (system).
  • myway (system).
  • quri (system).
Source

caveman2.asd.

Child Component

v2/src (module).


3 Modules

Modules are listed depth-first from the system components tree.


Previous: , Up: Modules   [Contents][Index]

3.1 caveman2/v2/src

Source

caveman2.asd.

Parent Component

caveman2 (system).

Child Components

4 Files

Files are sorted by type and then listed depth-first from the systems components trees.


Previous: , Up: Files   [Contents][Index]

4.1 Lisp


Next: , Previous: , Up: Lisp   [Contents][Index]

4.1.1 caveman2/caveman2.asd

Source

caveman2.asd.

Parent Component

caveman2 (system).

ASDF Systems

caveman2.


4.1.2 caveman2/v2/src/caveman.lisp

Dependencies
Source

caveman2.asd.

Parent Component

v2/src (module).

Packages

caveman2.


4.1.3 caveman2/v2/src/app.lisp

Dependency

exception.lisp (file).

Source

caveman2.asd.

Parent Component

v2/src (module).

Packages

caveman2.app.

Public Interface
Internals

*package-app-map* (special variable).


4.1.4 caveman2/v2/src/route.lisp

Dependencies
Source

caveman2.asd.

Parent Component

v2/src (module).

Packages

caveman2.route.

Public Interface
Internals

4.1.5 caveman2/v2/src/nested-parameter.lisp

Source

caveman2.asd.

Parent Component

v2/src (module).

Packages

caveman2.nested-parameter.

Public Interface

parse-parameters (function).

Internals

4.1.6 caveman2/v2/src/helper.lisp

Dependency

app.lisp (file).

Source

caveman2.asd.

Parent Component

v2/src (module).

Packages

caveman2.helper.

Public Interface

url-for (function).

Internals

4.1.7 caveman2/v2/src/exception.lisp

Source

caveman2.asd.

Parent Component

v2/src (module).

Packages

caveman2.exception.

Public Interface
Internals

4.1.8 caveman2/v2/src/skeleton.lisp

Source

caveman2.asd.

Parent Component

v2/src (module).

Packages

caveman2.skeleton.

Public Interface

make-project (function).

Internals

*skeleton-directory* (special variable).


5 Packages

Packages are listed by definition order.


Next: , Previous: , Up: Packages   [Contents][Index]

5.1 caveman2.skeleton

Source

skeleton.lisp.

Use List

common-lisp.

Public Interface

make-project (function).

Internals

*skeleton-directory* (special variable).


5.2 caveman2.route

Source

route.lisp.

Use List
  • cl-annot.
  • common-lisp.
Public Interface
Internals

Next: , Previous: , Up: Packages   [Contents][Index]

5.3 caveman2.nested-parameter

Source

nested-parameter.lisp.

Use List

common-lisp.

Public Interface

parse-parameters (function).

Internals

5.4 caveman2.app

Source

app.lisp.

Use List

common-lisp.

Public Interface
Internals

*package-app-map* (special variable).


Next: , Previous: , Up: Packages   [Contents][Index]

5.5 caveman2.exception

Source

exception.lisp.

Use List

common-lisp.

Public Interface
Internals

Next: , Previous: , Up: Packages   [Contents][Index]

5.6 caveman2.helper

Source

helper.lisp.

Use List

common-lisp.

Public Interface

url-for (function).

Internals

Previous: , Up: Packages   [Contents][Index]

5.7 caveman2

Source

caveman.lisp.

Use List

common-lisp.


6 Definitions

Definitions are sorted by export status, category, package, and then by lexicographic order.


Next: , Previous: , Up: Definitions   [Contents][Index]

6.1 Public Interface


Next: , Previous: , Up: Public Interface   [Contents][Index]

6.1.1 Special variables

Special Variable: *current-app*
Package

caveman2.app.

Source

app.lisp.

Special Variable: *exception-class*
Package

caveman2.exception.

Source

exception.lisp.

Special Variable: *parsed-parameters-symbol-name*
Package

caveman2.route.

Source

route.lisp.


6.1.2 Macros

Macro: defroute (&rest args)
Package

caveman2.route.

Source

route.lisp.

Macro: route (method routing-rule form)
Package

caveman2.route.

Source

route.lisp.


6.1.3 Ordinary functions

Function: find-package-app (package)
Package

caveman2.app.

Source

app.lisp.

Function: make-project (path &rest params &key name description author email license &allow-other-keys)
Package

caveman2.skeleton.

Source

skeleton.lisp.

Function: parse-parameters (params-alist)
Package

caveman2.nested-parameter.

Source

nested-parameter.lisp.

Function: redirect (url &optional code)
Package

caveman2.exception.

Source

exception.lisp.

Function: throw-code (code &rest args)
Package

caveman2.exception.

Source

exception.lisp.

Function: url-for (route-name &rest params)
Package

caveman2.helper.

Source

helper.lisp.


6.1.4 Generic functions

Generic Reader: exception-code (condition)
Package

caveman2.exception.

Methods
Reader Method: exception-code ((condition caveman-exception))
Source

exception.lisp.

Target Slot

code.

Generic Function: on-exception (app code)
Package

caveman2.app.

Source

app.lisp.

Methods
Method: on-exception ((app <app>) code)
Method: on-exception ((app <app>) (c http-exception))
Generic Reader: redirection-code (condition)
Package

caveman2.exception.

Methods
Reader Method: redirection-code ((condition caveman-redirection))
Source

exception.lisp.

Target Slot

code.

Generic Reader: redirection-to (condition)
Package

caveman2.exception.

Methods
Reader Method: redirection-to ((condition caveman-redirection))
Source

exception.lisp.

Target Slot

to.


6.1.5 Standalone methods

Method: call ((this <app>) env)
Package

lack.component.

Source

app.lisp.

Method: initialize-instance :after ((app <app>) &key)
Source

app.lisp.

Method: make-response ((app <app>) &optional status headers body)
Package

ningle/context.

Source

app.lisp.

Method: not-found ((this <app>))
Package

ningle/app.

Source

app.lisp.


6.1.6 Conditions

Condition: caveman-exception

Simple HTTP exception class

Package

caveman2.exception.

Source

exception.lisp.

Direct superclasses

error.

Direct subclasses

http-exception.

Direct methods

exception-code.

Direct slots
Slot: code
Initform

(quote 500)

Initargs

:code

Readers

exception-code.

Writers

This slot is read-only.

Condition: caveman-redirection
Package

caveman2.exception.

Source

exception.lisp.

Direct superclasses

error.

Direct methods
Direct slots
Slot: to
Initargs

:to

Readers

redirection-to.

Writers

This slot is read-only.

Slot: code
Initform

(quote 302)

Initargs

:code

Readers

redirection-code.

Writers

This slot is read-only.

Condition: http-exception

Customizable HTTP exception class

Package

caveman2.exception.

Source

exception.lisp.

Direct superclasses

caveman-exception.

Direct methods

on-exception.


Previous: , Up: Public Interface   [Contents][Index]

6.1.7 Classes

Class: <app>
Package

caveman2.app.

Source

app.lisp.

Direct superclasses

app.

Direct methods

6.2 Internals


Next: , Previous: , Up: Internals   [Contents][Index]

6.2.1 Special variables

Special Variable: *http-status*
Package

caveman2.exception.

Source

exception.lisp.

Special Variable: *package-app-map*
Package

caveman2.app.

Source

app.lisp.

Special Variable: *skeleton-directory*
Package

caveman2.skeleton.

Source

skeleton.lisp.


Previous: , Up: Internals   [Contents][Index]

6.2.2 Ordinary functions

Function: %build-val-for-array (nested-keys val current)
Package

caveman2.nested-parameter.

Source

nested-parameter.lisp.

Function: %build-val-for-hash (nested-keys val current)
Package

caveman2.nested-parameter.

Source

nested-parameter.lisp.

Function: add-app-if-omitted (routing-rule)
Package

caveman2.route.

Source

route.lisp.

Function: add-query-parameters (base-url params)

Add a query parameters string of PARAMS to BASE-URL.

Package

caveman2.helper.

Source

helper.lisp.

Function: build-val (nested-keys val current)
Package

caveman2.nested-parameter.

Source

nested-parameter.lisp.

Function: canonicalize-method (method)
Package

caveman2.route.

Source

route.lisp.

Function: expand-to-alist (obj)
Package

caveman2.nested-parameter.

Source

nested-parameter.lisp.

Function: http-status-reason (code)
Package

caveman2.exception.

Source

exception.lisp.

Function: make-lambda-list (lambda-list)
Package

caveman2.route.

Source

route.lisp.

Function: need-parsed-parameters (lambda-list)
Package

caveman2.route.

Source

route.lisp.

Function: params-form (params-symb lambda-list)
Package

caveman2.route.

Source

route.lisp.

Function: parse-key (key)
Package

caveman2.nested-parameter.

Source

nested-parameter.lisp.

Function: parse-key-arguments (lambda-list)
Package

caveman2.route.

Source

route.lisp.

Function: peek-key (string start)
Package

caveman2.nested-parameter.

Source

nested-parameter.lisp.

Function: redirect (url &optional status)
Package

caveman2.helper.

Source

helper.lisp.


Appendix A Indexes


Next: , Previous: , Up: Indexes   [Contents][Index]

A.1 Concepts


Next: , Previous: , Up: Indexes   [Contents][Index]

A.2 Functions

Jump to:   %  
A   B   C   D   E   F   G   H   I   M   N   O   P   R   T   U  
Index Entry  Section

%
%build-val-for-array: Private ordinary functions
%build-val-for-hash: Private ordinary functions

A
add-app-if-omitted: Private ordinary functions
add-query-parameters: Private ordinary functions

B
build-val: Private ordinary functions

C
call: Public standalone methods
canonicalize-method: Private ordinary functions

D
defroute: Public macros

E
exception-code: Public generic functions
exception-code: Public generic functions
expand-to-alist: Private ordinary functions

F
find-package-app: Public ordinary functions
Function, %build-val-for-array: Private ordinary functions
Function, %build-val-for-hash: Private ordinary functions
Function, add-app-if-omitted: Private ordinary functions
Function, add-query-parameters: Private ordinary functions
Function, build-val: Private ordinary functions
Function, canonicalize-method: Private ordinary functions
Function, expand-to-alist: Private ordinary functions
Function, find-package-app: Public ordinary functions
Function, http-status-reason: Private ordinary functions
Function, make-lambda-list: Private ordinary functions
Function, make-project: Public ordinary functions
Function, need-parsed-parameters: Private ordinary functions
Function, params-form: Private ordinary functions
Function, parse-key: Private ordinary functions
Function, parse-key-arguments: Private ordinary functions
Function, parse-parameters: Public ordinary functions
Function, peek-key: Private ordinary functions
Function, redirect: Public ordinary functions
Function, redirect: Private ordinary functions
Function, throw-code: Public ordinary functions
Function, url-for: Public ordinary functions

G
Generic Function, exception-code: Public generic functions
Generic Function, on-exception: Public generic functions
Generic Function, redirection-code: Public generic functions
Generic Function, redirection-to: Public generic functions

H
http-status-reason: Private ordinary functions

I
initialize-instance: Public standalone methods

M
Macro, defroute: Public macros
Macro, route: Public macros
make-lambda-list: Private ordinary functions
make-project: Public ordinary functions
make-response: Public standalone methods
Method, call: Public standalone methods
Method, exception-code: Public generic functions
Method, initialize-instance: Public standalone methods
Method, make-response: Public standalone methods
Method, not-found: Public standalone methods
Method, on-exception: Public generic functions
Method, on-exception: Public generic functions
Method, redirection-code: Public generic functions
Method, redirection-to: Public generic functions

N
need-parsed-parameters: Private ordinary functions
not-found: Public standalone methods

O
on-exception: Public generic functions
on-exception: Public generic functions
on-exception: Public generic functions

P
params-form: Private ordinary functions
parse-key: Private ordinary functions
parse-key-arguments: Private ordinary functions
parse-parameters: Public ordinary functions
peek-key: Private ordinary functions

R
redirect: Public ordinary functions
redirect: Private ordinary functions
redirection-code: Public generic functions
redirection-code: Public generic functions
redirection-to: Public generic functions
redirection-to: Public generic functions
route: Public macros

T
throw-code: Public ordinary functions

U
url-for: Public ordinary functions

Jump to:   %  
A   B   C   D   E   F   G   H   I   M   N   O   P   R   T   U  

Previous: , Up: Indexes   [Contents][Index]

A.4 Data types

Jump to:   <  
A   C   E   F   H   M   N   P   R   S   V  
Index Entry  Section

<
<app>: Public classes

A
app.lisp: The caveman2/v2/src/app․lisp file

C
caveman-exception: Public conditions
caveman-redirection: Public conditions
caveman.lisp: The caveman2/v2/src/caveman․lisp file
caveman2: The caveman2 system
caveman2: The caveman2 package
caveman2.app: The caveman2․app package
caveman2.asd: The caveman2/caveman2․asd file
caveman2.exception: The caveman2․exception package
caveman2.helper: The caveman2․helper package
caveman2.nested-parameter: The caveman2․nested-parameter package
caveman2.route: The caveman2․route package
caveman2.skeleton: The caveman2․skeleton package
Class, <app>: Public classes
Condition, caveman-exception: Public conditions
Condition, caveman-redirection: Public conditions
Condition, http-exception: Public conditions

E
exception.lisp: The caveman2/v2/src/exception․lisp file

F
File, app.lisp: The caveman2/v2/src/app․lisp file
File, caveman.lisp: The caveman2/v2/src/caveman․lisp file
File, caveman2.asd: The caveman2/caveman2․asd file
File, exception.lisp: The caveman2/v2/src/exception․lisp file
File, helper.lisp: The caveman2/v2/src/helper․lisp file
File, nested-parameter.lisp: The caveman2/v2/src/nested-parameter․lisp file
File, route.lisp: The caveman2/v2/src/route․lisp file
File, skeleton.lisp: The caveman2/v2/src/skeleton․lisp file

H
helper.lisp: The caveman2/v2/src/helper․lisp file
http-exception: Public conditions

M
Module, v2/src: The caveman2/v2/src module

N
nested-parameter.lisp: The caveman2/v2/src/nested-parameter․lisp file

P
Package, caveman2: The caveman2 package
Package, caveman2.app: The caveman2․app package
Package, caveman2.exception: The caveman2․exception package
Package, caveman2.helper: The caveman2․helper package
Package, caveman2.nested-parameter: The caveman2․nested-parameter package
Package, caveman2.route: The caveman2․route package
Package, caveman2.skeleton: The caveman2․skeleton package

R
route.lisp: The caveman2/v2/src/route․lisp file

S
skeleton.lisp: The caveman2/v2/src/skeleton․lisp file
System, caveman2: The caveman2 system

V
v2/src: The caveman2/v2/src module

Jump to:   <  
A   C   E   F   H   M   N   P   R   S   V