I recently started to learn clojure programming. It is an interesting experience. Ever since I learned computer programming almost 20 years ago, in Pascal, on a VAX minicomputer terminal, I have not experienced this newbie sensation with a computer language. The sense of excitement and novelty is high, and the eagerness to put the language to use is higher still. So for my new project at work, I am doing it with clojure.

This is a visual analytics project, and the visual part will be on the Web. It amazes me how much work has already been done for the Web using this 3 years old language. So it should be easy for me to get started. Here's what I have so far.


Know your lein

The standard build tool for clojure projects these days seems to be lein, short for leiningen. It is just a script, download it, make it executable, put it on your path. Also make sure you have java installed. Now go to whatever directory your project will live in, run lein:

lein new myproject

This creates the default project structure for a project creatively named "myproject", and downloads dependencies from the Internet, including clojure itself (clojure is built on java virtual machine, so the clojure language runtime is just a jar file).

Now in the "mypoject" directory, there is a "project.clj" file. This file controls everything about the project, except the writing code part. Edit the file according to your needs. There is a very extensive sample project.clj that I find very informative. My project.clj so far looks like this:


 1 (defproject myproject "0.0.1-SNAPSHOT"
 2   :description "MyProject has super duper visual analytics capabilities"
 3   :dependencies [[org.clojure/clojure "1.2.1"]
 4                  [org.clojure/clojure-contrib "1.2.0"]
 5                  [compojure "0.6.2"]
 6                  [hiccup "0.3.4"]
 7                  [ring "0.3.7"]
 8                  [commons-logging "1.1.1"]
 9                  [org.apache.lucene/lucene-core "3.1.0"]
10                  [xalan "2.7.1"]
11                  [javaewah "0.1.0"]
12                  [com.my.work/secret-lib1 "0.3.4b"]
13                  [com.my.work/secret-lib2 "0.1.0"]]
14   :dev-dependencies [[lein-ring "0.4.0"]
15                      ;[org.clojars.autre/lein-vimclojure "1.0.0"]
16                      [clj-stacktrace "0.2.1"]] 
17   :repositories 
18             {"myrepo" 
19              {:url 
20               "https://myrepo.my.com:8080/artifactory/libs-release-local"}}
21   :source-path "src/clojure"
22   :java-source-path "src/java"
23   :warn-on-reflection true
24   ;:main com.my.myproject.runtime.core)
25   :ring {:handler com.my.myproject.runtime.core/app})

Let me explain line by line. The first line is the project name and the current version number (using the so called semantic versioning scheme).

The third line starts the dependencies definition. These dependencies are libraries written in jvm languages such as java or clojure. lein will automatically find and download them from public repositories if they are publicly available. This is the case for libraries referred in line 3 - 10, where the last three libs are open source java libs and the rest are open source clojure libs: compojure is a lightweight Web framework, which builds upon ring, which abstract HTTP into a simple API, hiccup allows one to write html in clojure syntax.

The lib on line 11 is also open source, however, it has not been packaged by the author and submitted to a pubic repository, so lein will not be able to find it. What I did was to package it as a jar file myself and deploy it to a private repository I setup for my team, so my team members can all access to the same libs without needing to commit the libs to our version control system, which is not suitable for handling binary data. This private repository is defined on line 17 to 20. Here the repository server is a standard installation of artifactory. The libs on line 12 and 13 are our in-house developed java libraries, which are deployed the same way.

By default, lein expect clojure code in "myproject/src". Since we will be mixing java code and clojure code, we put them in separate folders. These are defined in line 21 and 22.

Line 14 starts the dev-dependencies. These are the dependencies for developers' convenience and will not be included in the final product. lein-ring is a plugin for facilitating Web development in clojure that utilizes ring. Basically, it adds a ring command for lein. For example,

lein ring server-headless

will start a jetty server with the Web app running on port 3000 (default). The Web app is defined on line 25, which is just a simple app to show a greeting in this case. The code consists of two files. core.clj defines the main routing table for the Web app:


 1 (ns com.my.myproject.runtime.core
 2   (:use compojure.core
 3         hiccup.middleware
 4         com.my.myproject.runtime.views)
 5   (:require [compojure.route :as route]
 6             [compojure.handler :as handler]))
 7
 8 (defroutes main-routes
 9   (GET "/" [] (index-page))
10   (route/not-found (page-404))
11   (route/resources "/"))
12
13 (def app
14   (-> (handler/site main-routes)
15       (wrap-base-url)))

views.clj defines the pages to show:


 1 (ns com.my.myproject.runtime.views
 2   (:use [hiccup core page-helpers]))
 3
 4 (defn index-page []
 5   (html
 6     [:head 
 7      [:title "Welcome"] 
 8      (include-css "/css/style.css")]
 9     [:body
10      [:h1 
11        "Hello World!"]]))
12
13 (defn page-404 []
14   (html
15     [:head 
16      [:title "Sorry"] 
17      (include-css "/css/style.css")]
18     [:body
19      [:h1 "Page not found"]]))

One very nice thing about lein-ring is that it will automatically pick up any changes made in the project. That's right, live changes, no need to wait for the code to compile, restart the server, etc, just refresh the browser and you will see the changes. This is extremely convenient for Web development, especially for experimentation in clojure REPL.

vimclojure

I am a vi addict. For my fix, there is a vimclojure plugin for clojure development with vim. To have dynamic features such as code snippet evaluation, code completion etc, there is a need to start a nailgun server so vimclojure can contact with a clojure REPL. This script is what I use:


 1 # clojure jar is also installed in ~/.vim/lib
 2 CL_CP=.:~/.vim/lib/*
 3
 4 if [ -f "project.clj" ]; then
 5   CP=`lein classpath`:" $CL_CP"
 6 else
 7   CP=" $CL_CP"
 8 fi
 9
10 if [  $# -eq 0 ]; then 
11      exec java -server -cp " $CP" vimclojure.nailgun.NGServer 127.0.0.1
12 else
13      exec java -server -cp " $CP" vimclojure.nailgun.NGServer $1 
14 fi

I normally run this script in the root directory of the project, this allows "lein classpath" to pick up all the classpaths for the REPL session. There's also a lein-vimclojure plungin that will install vimclojure and start a nailgun server for you, but I found it does not load "user.clj", so my convenient functions defined there are not autoloaded. I will stick to my script.

For better navigation of clojure source code, vim users need TagList plugin. The plugin does not automatically work with clojure though. This blog post has a solution, and it worked for me. Basically, this tells TagList to treat clojure code just like other Lisp code, which it is.

That's all folks.



Comments

comments powered by Disqus