New Site (Built with Stasis)

Written by J David Smith
Published on 3 March 2014

First off: why not Wordpress?

Nothing against Automatic, but after having run several WP blogs I sympathize with this guy:

Wondering how I managed to end up building a Wordpress site today. For those of you that do this regularly, you have my deepest sympathies.

— Daniel Grant (@danieljohngrant) February 25, 2014

I don't want to run another WP blog and I don't want to have to hack any more PHP. The solution?

Static Site Generation

Static Site Generation is a pretty simple concept. You have some templates and some content, you want to put the content in the templates, and only want to do so once. The site content is transformed into HTML once by the site owner (aka me) and then served without any extra work by the server.

This has some big advantages. First, it makes a very fast website, as the restriction is not HTML generation time but simple transmission time. Second, it is extremely secure because malicious content serving is impossible short of someone gaining root access on my server (or someone hijacking Disqus; I trust Disqus' security people to do better than I could – it is their job, after all).

Even better, because the code doesn't need to interact with the server, I am not restricted to things that play nicely with the server (which Clojure actually does through Java, but that's not a place I'd like to go right now).

I only had one big requirement – that I be able to write my posts in Org format – but I also wanted something that I could hack on. Clojure is the language I'm most interested in right now, so I started looking in that direction. I toyed around with several options – even going so far as to [fork nakkaya's static](https://github.com/emallson/static) – but eventually settled on magnar's stasis.

The biggest problem I had with static was how it dealt with posts. This snippet says it best:

(defn list-files [d]
  (let [d (File. (dir-path d))]
    (if (.isDirectory d)
      (sort
       (FileUtils/listFiles d (into-array ["markdown"
                                           "md"
                                           "clj"
                                           "cssgen"
                                           "org"
                                           "html"]) true)) [] )))
(defn create-latest-posts
  "Create and write latest post pages."
  []
  (let [posts-per-page (:posts-per-page (config))
        posts (partition posts-per-page
                         posts-per-page
                         []
                         (reverse (list-files :posts)))
        pages (partition 2 (interleave (reverse posts) (range)))
        [_ max-index] (last pages)]
    ...))

As you can see, the posts list is created by using partition on what amounts to a directory listing. While this isn't a huge problem, my blog posts aren't organized that way and I didn't want to change that. Having dates in the file name looks ugly to me – never mind the fact that it duplicates the #+DATE headers that are in all of my posts.

This is where stasis comes in. It's a no-batteries-included framework, which means basically all it does is apply the templates to my sources. This leaves designing the templates, template framework and sources to me. I used the whattheemacsd source as my stasis-basis and built from there.

The biggest thing I had to do was implement conversion of Org files into HTML. While not the fastest option (in terms of running time), I opted to simply leave that to emacs by calling it in batch mode. The #+STUFF headers are trivial to parse using regexp, so pulling in my #+DATE's was a non-issue.

Ultimately, I'm pretty happy with how things turned out. This is the first post I've written using the new system and it's worked great!

What next?

There are a couple of features that I want to build, starting with category and tag views. After that, I may look at implementing an elisp command to replace my current deployment method (a shell script) so that I can deploy directly from the editor.

Technology & Style Credits

The full source code is available on github.