Wednesday, August 28, 2013

Clojure for project development

In line with my last Clojure post, and with several comments I have come across the internet, I am going to post a guide to build a Clojure piece of software runnable from the beginning (i.e., outside of the REPL).

First of all, you need a JVM, obviously. If you haven't done that, install the latest version of Java SDK.

Now you want to install Leinigen. Leinigen is a building and dependency management tool favored by the Clojure community. Download Leinigen script here  Now you need to place it on a directory within your PAT (for example, /bin) and set it to be executable with chmod 755 ~/bin/lein.

Once we have done that, we need to tell the script to download the Leinigen system. You can easiy do that with lein self-install.

Now you can create a Clojure project, called hello:
lein new app hello
This uses the template app to create your new project. Now cd into the new directory, collect the dependencies and run the tests.
cd hello
lein deps
lein test
You'll see a single testcase which deliberately fails:
Testing hello.core-test FAIL in (replace-me) (core_test.clj:6) expected: false   actual: false Ran 1 tests containing 1 assertions. 1 failures, 0 errors.
Great! Clojure is installed in this project and working! To get a feel for Clojure, let's try out some basic stuff by starting a script console:
lein repl
You'll see something like:

nREPL server started on port 59654 on host
REPL-y 0.3.0
Clojure 1.5.1
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

(println "Hello World!") 
and press return. You should get:
Hello World!
Now let's define a function that does that:
(defn greet [] (println "Hello World!"))
The console will respond:
#'user/greet user=>
Run the function:  
Hello World! nil user=>
Returning to the project, edit src/hello/core.clj (the basic source skeleton that Leiningen created for you above). Add our greet function to it and call it, so core.clj reads:
(ns hello.core) (defn greet [] (println "Hello World!"))

(defn -main[] (greet "Sean"))
The (ns hello.core) line declares the namespace (think Java package) in which the code lives. The -main function will be the regular Java main function and we tell Clojure by writing the - prefix.
We can run this via Leiningen:
lein run -m hello.core
The -m argument specifies the namespace in which -main is defined.

Now let's modify our script so we can compile it and run it via the JVM. First we need to update the namespace declaration to tell Clojure we want to generate a (Java) class file, we remove the spaces in the output by call str to construct a single string (so we need a space after Hello), and we change our main method to accept an argument:
(ns hello.core (:gen-class))
(defn greet[who] (println (str "Hello " who "!")))
(defn -main[who] (greet who))
We also need to tell Leiningen about our main class. Edit project.clj and add a :main declaration so it looks like this:
(defproject hello "1.0.0-SNAPSHOT" 
   :description "FIXME: write"
   :dependencies [[org.clojure/clojure "1.2.1"]]
     :main hello.core)
Don't worry about the rest of it, that's part of the Leiningen/Maven magic used to ensure the right libraries are available. Now tell Leiningen to compile your script and create a JAR that we can execute via Java:
lein uberjar
If you look in the current directory, you'll see hello-1.0.0-SNAPSHOT.jar and hello-1.0.0-SNAPSHOT-standalone.jar and it's the second one we'll use:
java -cp hello-1.0.0-SNAPSHOT-standalone.jar hello.core
 You have now a functional project and you are ready to write some code for production software.

No comments:

Post a Comment