clojure, ring, and 1990's style counters
Clojure is a Lisp-like programming language that runs on the JVM. Ring is a web application library that provides a simple framework for serving HTTP content with clojure. It is similar to Rack for Ruby or WSGI for Python.
Leiningen is a tool for managing Clojure projects. It is highly recommend to use this for new clojure projects.
If you are on Mac OS X run the following command to install the lein tool or click here if you are on another platform.
Counters from the 90s!
Remember those old sites with a hit-counter at the bottom of the page?
That is what we will be creating in clojure -- a dynamic counter image serving web app. If you do not remember those hit-counters, maybe you can find some examples with the Wayback Machine!
Creating the project
The following command creates a new Clojure project and a source file at src/web_counter/core.clj.
The project.clj allows you to configure your Clojure project. It is similar in function (but not in syntax!) to a pom.xml (AHHH) or build.gradle.
We have added a few dependencies, notably ring and spymemcached.
The lein-ring plugin is important as it gives the lein command extra tasks specific to ring.
The ring key configures our web app:
- handler - Function that handles requests
- init - Function that will be called during servlet initialization
The core of a ring application is the request handler. It takes a single argument, request, which contains all of the request attributes. In our handler we are only concerned with the uri, or the path of the request.
The return of a request handler is a map containing information about the response. Above are some common response attributes:
- status - The HTTP response status code
- headers - A map of HTTP headers for the response
- body - Ring supports a few different types for this, such as: String, File, InputStream, etc,.
Our handler does the following:
- / - Returns a simple HTML page that has an tag pointing to the counter image
- /counter - Increments the counter and generates a GIF image
- _ - Returns 404 for any other requests
The counter is stored in memcached (rather than in-memory) so that the application can be deployed on multiple instances (any shared storage will work, though).
First we make a forward declaration for a var named memcached-client. When the web application starts we will set the root binding for this var so that it can be used in our request handler.
This function creates a memcached client using the spymemcached library. The memcached server information is retrieved from environment variables. (The environment variables were used to work with the Heroku memcache addon)
Below is the initialization function. In our project.clj we set :init to web-counter.core/init so that Ring knows which function to call. The function below just sets the binding for the var memcached-client to a valid memcached client instance.
Generating the GIF images
The counter image is generated by piecing together a series of digit images. The code below loads digit images from a folder in the resource path. By default lein expects resources to be in the resources directory. In our app we have a folder named odometer that contains 10 digit images named 0.gif, 1.gif...
The var counter-images is bound to a map of digit -> counter image.
The function below generates the counter image by extracting the individual digits from the number and putting them into a single GIF.