Implementing a blog in Common Lisp: Part 1

by Vetle Roeim - last updated 2009-01-15

Table of Contents

  1. Introduction
  2. Getting started
  3. The Model
  4. Setting up the persistent store
  5. Generating a web page
  6. Serving the web page
  7. End of part 1

Introduction

This tutorial will show how a blog can easily be implemented in Common Lisp, using a few frameworks. Installing these frameworks is not covered, and neither are details on getting Common Lisp implementation up and running.

Part 1 shows how data can be stored persistently, and how HTML pages can be generated and served to the end-user. For your convenience, the source code of the files created in this tutorial can be downloaded.

The frameworks I'll be using are:

Update: Part 2 of this tutorial is now available.

Getting started

First, let's define the package that will hold our code and load the packages that we require.

(in-package :cl-user)

(defpackage :my-blog
  (:use :common-lisp)
)


(in-package :my-blog)
(require :hunchentoot)
(require :html-template)
(require :elephant)
(use-package :elephant)

After in-package comes our package content. First, we make sure we have the necessary libraries loaded, by using require. Next, we use the function use-package to load the symbols exported by Elephant into our local namespace. Without this, we would have to prefix use of those symbols with elephant:, as we will see be done with the other packages.

The Model

Next, we define our model. To keep this simple, we will just define a class for our blog posts, and then put them all in a list. Alternativly, we could have defined a blog class that would be a container for our blog posts.

(defpclass blog-post ()
  ((title :initarg :title
          :accessor title
)

   (body :initarg :body
         :accessor body
)

   (timestamp :initarg :timestamp
              :accessor timestamp
              :initform (get-universal-time)
)
)
)

Here we define a new class, blog-post, which has three attributes: title, body and timestamp. Notice that we're using Elephants defpclass macro, instead of defclass which is the normal way of declaring classes.

defpclass makes sure that the class uses the persistent-metaclass metaclass, which adds persistence to our class. Alternativly, we could have written using defclass and setting the metaclass ourselves.

(defclass blog-post ()
  ((title :initarg :title
          :accessor title
)

   (body :initarg :body
         :accessor body
)

   (timestamp :initarg :timestamp
              :accessor timestamp
              :initform (get-universal-time)
)
)

  (:metaclass persistent-metaclass)
)

Setting up the persistent store

We're almost ready to start creating blog posts, but we will first need to tell Elephant where to store our data. In this example I will use CLSQL and SQLite, but there are several other alternatives.

; Open the store where our data is stored
(defvar *elephant-store* (open-store '(:clsql (:sqlite3 "/tmp/blog.db"))))

; Container for all our blog posts
(defvar *blog-posts* (or (get-from-root "blog-posts")
                         (let ((blog-posts (make-pset)))
                           (add-to-root "blog-posts" blog-posts)
                           blog-posts
)
)
)

In the first line here, the function open-store is called, which tells Elephant to open a new persistent store where our data is to be stored. The parameters tell it to use the CLSQL package, and SQLite3. The store object is put into a variable in case we need it later.

The next line defines a variable that will contain all our blog posts. It is either retrieved from the Elephant store using get-from-root, but if it's not found then a new pset is created and added to the store.

The reason for using pset instead of a normal Common Lisp collection such as a list, is that operations which change these do not have persistent side effects. In other words; if we want collections that are persistent, we have to use classes provided by Elephant. Two classes are provided: pset which provides a persistent and unordered collection of objects, and btree which provides persistent collection of key-value pairs.

Let's create a blog post and see if things work.

CL-USER> (in-package :my-blog)
#<PACKAGE "MY-BLOG">
MY-BLOG> (setq first-post (make-instance 'blog-post :title "Hello blog world"
                                         :body "First post!"))
#<BLOG-POST oid:5>
MY-BLOG> (insert-item first-post *blog-posts*)
#<BLOG-POST oid:5>
MY-BLOG> 
  

Ok, so far so good. Let's stop the Lisp instance and see if the blog post was stored.

CL-USER> (in-package :my-blog)
#<PACKAGE "MY-BLOG">
MY-BLOG> *blog-posts*
#<DEFAULT-PSET oid:4>
MY-BLOG> (pset-list *blog-posts*)
(#<BLOG-POST oid:5>)
MY-BLOG> (title (first (pset-list *blog-posts*)))
"Hello blog world"
MY-BLOG> 
  

When we load our package again, we can see that the *blog-posts* is defined, as we would expect, and when we generate a list of the contents of the pset using pset-list, we can see that it contains the blog posts that we created.

Let's create another blog post for good measure.

MY-BLOG> (insert-item (make-instance 'blog-post :title "This is fun"
                                         :body "Common Lisp is easy!") *blog-posts*)
#<BLOG-POST oid:6>
MY-BLOG> 
  

Generating a web page

Now that we have the possibility of storing and fetching blog posts, we need to somehow generate a web page containing the blog posts. We will do this using the html-template framework. We will create a single template, which simply displays all blog posts on a single page.

<!DOCTYPE html>
<html>
<head>
  <title>My Blog</title>
</head>
<body>
  <h1>My Blog</h1>
  
  <!-- tmpl_loop blog-posts -->
    <div>
      <h2><!-- tmpl_var title --></h2>
      <div>
        <!-- tmpl_var body -->
      </div>
    </div>
  <!-- /tmpl_loop -->
  
</body>
</html>
  

The format of html-template is based on the Perl module HTML::Template. tmpl_loop will loop over a collection, and tmpl_var will print the value of the supplied parameters.

Next, we create a function that will use this template and generate the final page.

(defun generate-index-page ()
  "Generate the index page showing all the blog posts."
  (with-output-to-string (stream)
    (html-template:fill-and-print-template
     #P"index.tmpl"
     (list :blog-posts
           (loop for blog-post in (pset-list *blog-posts*)
              collect (list :title (title blog-post) :body (body blog-post))
)
)

     :stream stream
)
)
)

Let's go through this step by step. First, since html-template::fill-and-print-template needs to print the resulting HTML to a stream, we use the macro with-output-to-string to create a stream that can be written to and that will return the result as a string.

Next, we call the function fill-and-print-template, which does exactly that. Since it's in the package html-template, we prefix it with the correct namespace. The required parameters to this function are the template that is to be rendered and parameters that are to be used in the template. We also tell the function to print to our character stream. The loop goes through the list of blog posts and creates property lists for each post, containing the title and body. To show how what the result of this loop , we can run it separatly.

MY-BLOG> (loop for blog-post in (pset-list *blog-posts*) 
              collect (list :title (title blog-post) :body (body blog-post)))
((:TITLE "This is fun" :BODY "Common Lisp is easy!")
 (:TITLE "Hello blog world" :BODY "First post!"))
MY-BLOG> 
  

To make sure fill-and-print finds the correct file, we can set variable *default-template-pathname* to point to where our templates are.

(setq html-template:*default-template-pathname* #P"/Users/vetler/Documents/devel/blog/source/")

Let's try it out.

MY-BLOG> (generate-index-page)
"<!DOCTYPE html>
<html>
<head>
  <title>My Blog</title>
</head>
<body>
  <h1>My Blog</h1>
  
  
    <div>
      <h2>Hello blog world</h2>
      <div>
        First post!
      </div>
    </div>
  
  
</body>
</html>"
MY-BLOG> 
  

Serving the web page

Finally, we will need to set up a web server to serve our web page. We will do this using Hunchentoot, a web server implemented in Common Lisp.

Since we already have everything we need for generating the web page, it's just a matter of telling the web server to call the function for generating the web page and starting it.

(setq hunchentoot:*dispatch-table*
      (list (hunchentoot:create-prefix-dispatcher "/" 'generate-index-page))
)

(defvar *ht-server* (hunchentoot:start-server :port 8080))

The web server uses the *dispatch-table* to map URLs to functions. In this example we simply create a mapping from the root to our function, but it's also possible to create dispatchers with regular expressions, using create-regex-dispatcher.

Lastly the web server is started, at port 8080. That's it! Now it's just a matter of pointing a browser at http://localhost:8080/ and looking at our beautiful blog.

End of part 1

The basics of how to store data persistently and how to generate and serve web pages should now be clear. Of course, there is some essential functionality missing from our current implementation. In the next part, it will be shown how show blog posts on separate pages, edit content, and how to query the Elephant store.

Until then, good luck with Common Lisp!