Tonλ's blog May the λ be with you

How to bootstrap a clojure project

by @ardumont on

In this article, we will see how to rapidly startup a clojure project using leiningen.

Pre-requisites

clojure environment

See this previous post

a network to fetch dependencies

Create a new clojure project

lein new hello

This will create an arborescence like this:

/home/tony/repositories/test/hello/
├── .gitignore
├── project.clj
├── README
├── src
│   └── hello
│       └── core.clj
└── test
    └── hello
        └── test
            └── core.clj

5 directories, 4 files

I usually initialize a git project to hold my modifications.

git init && git add . && git commit -m "Bootstrap clojure project"

Launch a repl

clojure-jack-in

As we already installed emacs, we can launch the repl by doing simply:

  • M-x cd to change the current folder emacs runs into the hello folder (the one containing the project.clj file)
  • M-x clojure-jack-in to launch the repl.

A new buffer must have launched itself (may take some time the first time).

; SLIME 20100404
user>

Basic check

Launch some basic operations and hit enter to check that the repl reacts:

; SLIME 20100404
user> (+ 1 1)
2
user> (reduce + [ 1 2 3 4])
10
user>

Setup dependencies

project.clj

The project.clj file is the leiningen file holding the metadata of your project. This is this file that you want to edit to add dependencies or dev-dependencies.

By default, here is the content:

(defproject hello "1.0.0-SNAPSHOT"
  :description "FIXME: write description"
  :dependencies [[org.clojure/clojure "1.3.0"]])

This describes that the project :

  • if packaged, the version will be 1.0.0-SNAPSHOT
  • a simple description to update
  • holds only one runtime dependencies, clojure in its version 1.3.0 (currently, the 1.4.0

has been released).

Note For those from the java world, you can see it as the equivalent of the maven pom.xml with less verbosity!

Add dependencies

As i'm a tdd fanboy, i like to add midje, an excellent unit test framework for clojure.

dev-dependencies

To add some dev-dependencies, add the :dev-dependencies in this file, like this:

(defproject hello "1.0.0-SNAPSHOT"
  :description "A simple hello world from clojure"
  :dependencies [[org.clojure/clojure "1.3.0"]]
  :dev-dependencies [[midje "1.3.1"]])

or dependencies

As midje is a unit test framework, this must be a dev-dependencies (to avoid having it in the jar at runtime).

But, considering that clojure is extremely expressive, i'd rather make midje a runtime dependencies so that my facts (unit test in midje) stays with the code they tests.

Thus, my facts are another level of documentation (it's quite clear when using marginalia to generate documentations from the code).

If you're like me, then you can change the previous block into this

(defproject hello "1.0.0-SNAPSHOT"
  :description "A simple hello world from clojure"
  :dependencies [[org.clojure/clojure "1.3.0"]
                 [midje "1.3.1"]])

Note Another plus side, we do not have any problems concerning the visibility of the functions. A function private is still testable (you do have trouble otherwise).

Update the dependencies

Now that we modify this file, we need to update the jars locally. To do this, we will tell leiningen "go fetch my dependencies" with the deps commands.

lein deps

This will use the clojars and/or maven artifacts around the world to satisfy my demands.

Setup environment

Rapid check

Launch the unit tests, lein midje

tony@dagobah(0.08,) 09:31:10 ~/repositories/test/hello $ lein midje
>>> Output from clojure.test tests:

FAIL in (replace-me) (core.clj:6)
No tests have been written.
expected: false
  actual: false

>>> clojure.test summary:
Ran 1 tests containing 1 assertions.
1 failures, 0 errors.
>>> Midje summary:
All claimed facts (0) have been confirmed.

This is a success.

It's normal that the test fail as there is a default false claim in the file test/hello/test/core.clj

(ns hello.test.core
  (:use [hello.core])
  (:use [clojure.test]))

(deftest replace-me ;; FIXME: write
  (is false "No tests have been written."))

You can remove this file as we will write:

  • our tests in the source file directly.
  • using midje (and not clojure.test, default one)

Add a midje fact

Open the file src/hello/core.clj. This contains only the ns declaration without any dependencies yet.

(ns hello.core)

First of all, we want to solve this problem in tdd. So a first step, is to add a framework that permits that, i chose to use midje.

For this, we add this namespace like this

(ns hello.core
    (:use [midje.sweet]))

To check that all is ok, we can add a false fact the midje way.

(fact (+ 1 1) => 3)

Note: We can read this fact like this: "This is a fact that (+ 1 1) gives 3" We all know it's false but bare with me.

In the terminal, launch lein midje again

tony@dagobah(0.14,) 10:21:37 ~/repositories/test/hello (master) $ lein midje

FAIL at (core.clj:4)
    Expected: 3
      Actual: 2
FAILURE: 1 fact was not confirmed.

Ok, the test is failing but it's ok, that's what we want.

Now, the ultimate test, check the output when the test is ok.

For this, change the fact with the right result.

(fact (+ 1 1) => 2)

Note This is a fact that (+ 1 1) gives 2.

Now launch lein midje

tony@dagobah(0.53,) 10:21:58 (1) ~/repositories/test/hello (master) $ lein midje
All claimed facts (1) have been confirmed.

Another way of checking midje facts

Compilation

When in the core.clj buffer, C-c C-k launches the compilation of all the clj file. The output of this compilation appears in the repl.

When in error:

; SLIME 20100404
FAIL at (core.clj:4)
    Expected: 3
      Actual: 2

user>

When there is no error, nothing appears (except if there are prints in your code).

Check one fact

When in the core.clj buffer, =C-c ,= launch the evaluation of the fact. If the fact is true, there will be a quotation just before the fact.

;.;. Any intelligent fool can make things bigger, more complex, and more violent. It takes a touch of genius -- and a lot of
;.;. courage -- to move in the opposite direction. -- Schumacher
(fact (+ 1 1) => 2)

Else, there will be a summary of the error

;.;. FAIL at (NO_SOURCE_FILE:1)
;.;.     Expected: 3
;.;.       Actual: 2
(fact (+ 1 1) => 3)

Ultimate way

There is an ultimate way for testing with midje that i prefer above all lein midje --lazytest

At the moment, it seems there is a problem with my platform around the leiningen 1.7.1 version (by downgrading it to leiningen 1.6.2, this works) that i was not yet able to solve.

This mode permits to relaunch the facts after a modification on the file system has been done. This is quite handy to avoid the manual compilation. The output is the same as previously described.

When this work on my machine, I use it by opening a terminal buffer (M-x multi-term) in emacs in which i launch the lein midje --lazytest command.

Try lazytest

For those with leiningen 1.6.2 who wants to try it.

  • Change the project.clj file to this
(defproject hello "1.0.0-SNAPSHOT"
  :description "A simple project"
  :dependencies [[org.clojure/clojure "1.3.0"]
                 [midje "1.3.1"]]
  :dev-dependencies [[com.intelie/lazytest "1.0.0-SNAPSHOT" :exclusions [swank-clojure]]])
  • Relaunch lein deps.
  • Restart emacs and clojure-jack-in
  • Open a new buffer with multi-term or shell or eshell or whatever (M-x the-mode-you-chose).
  • Then launch the command in the terminal as said previously

Conclusion

You're now able to setup a project in clojure and knows a little about midje.

In a near future, i intend to make some other blog posts to focus on:

  • a simple problem resolution using top down tdd in clojure with midje
  • continued integration with travis-ci
  • heroku for the deploying part
  • marginalia for the documentation generation and the github integration.

Latest posts