====== Clojure ====== ===== Intro ===== I don't know anything about this Lisp dialect yet, but I intend to find out more about it... Here's an excellent guide on how to get [[http://lostincode.net/blog/clojure-lein-vim|Clojure working with Vim]]. Although originally written for Clojure 1.2, it's still valid for 1.4. This guide pretty much covers what's also recorded here in this wiki. If you've got Clojure and Vim working together seamlessly, here's a nice tutorial to get you started with the actual language: [[http://moxleystratton.com/clojure/clojure-tutorial-for-the-non-lisp-programmer|Clojure Tutorial For the Non-Lisp Programmer]]. After that, take a look at this tutorial: [[http://www.lisperati.com/clojure-spels/casting.html|Casting SPELs with Clojure]]. It's got cartoons in it! {{:clojure:casting_spels.jpg?200|}} (Yes, it says Lisp, but the tutorial was adapted for Clojure) ===== Install on Ubuntu ===== These links pretty much point you in the right direction: * [[http://java.ociweb.com/mark/clojure/article.html#GettingStarted|Install with Git]] - Cutting edge, but not necessarily stable. * [[http://fireinside.me/post/3551419794/clojure-emacs-on-ubuntu|Clojure and Emacs on Ubuntu]] - I don't intend to learn Emacs (yet), just for the sake of Clojure, but this site gives you some installation hints as well. And yes, there is an apt-get (well dpkg) package for Clojure, but it's hopelessly out of date. Just use git (to get the latest, non-stable version) or download the official zip (containing the stable release) on [[http://clojure.org/downloads|clojure.org]] and compile it yourself using the guidance from the sites mentioned above. One more thing: clojure-contrib for the latest version (1.3) is no longer Riddell's github site. It's now here: [[https://github.com/clojure/clojure-contrib|github.com/clojure/clojure-contrib]]. ==== Latest Working Install (1.4)==== As per [[http://clojure.org/getting_started|clojure.org/getting_started]], download the zip [[http://clojure.org/downloads|here]]. Unzip, e.g. in ~/local. Then, inside the unzipped directory: java -cp clojure-1.4.0.jar clojure.main - to test the repl. ===== Configuration ===== The configuration is pretty simple. Inside ''~/local'', make a symlink called ''clojure'' to your unzipped directory. Then just stick this into your .bashrc file: export CLOJURE_EXT=~/local/clojure Here's an additional launcher script, which will either invoke the repl or execute a clojure program. Just stick this file in your ~/.scripts directory (or wherever you keep your shell scripts). # FILE: clj set -o errexit #set -o nounset #set -o xtrace if [ -n "${CLOJURE_EXT:-}" ]; then OLD="$IFS" IFS=":" EXT="$(find -H $CLOJURE_EXT -mindepth 1 -maxdepth 1 -print0 | tr \\0 \:)" IFS="$OLD" if [ -n "${CLASSPATH:-}" ]; then export CLASSPATH="$EXT$CLASSPATH" else export CLASSPATH="${EXT%:}" fi fi JAVA=${CLOJURE_JAVA:-java} OPTS=${CLOJURE_OPTS:-} MAIN=${CLOJURE_MAIN:-clojure.main} if [ $# -eq 0 ]; then exec rlwrap --remember -c -b "$breakchars" \ $JAVA $OPTS $MAIN else exec $JAVA $OPTS $MAIN "$@" fi So, if no commandline arguments are used when invoking the clj script, the rlwrap utility is used (install rlwrap through Synaptic Package Manager). If, on the other hand, you're executing clojure code from a file, the rlwrap utility is skipped. Example: clj test.clj // executes test.clj program clj // invokes repl A final note: the clj bash script does not mention any specify jar file. Don't worry about it. Apparently, java looks into each jar file inside the CLOJURE_EXT directory/ies to collect class names, so it will know in which file to find the clojure.main class. ==== Warning ==== Some Clojure sites no longer contain accurate information about clojure-contrib - as of version 1.3. The monolithic clojure-contrib.jar file is no longer in vogue. Instead, you should copy any additional module you want to use. Do this by copying the ''target/*.jar'' file inside the module directory to your ~./clojure directory (or use Leiningen, see below). Apparently, CLOJURE_EXT defines your ''classpath'' (a Java concept) for your clojure programs. ==== Additional Features ==== Here's a nice install guide, [[http://programmingzen.com/2010/07/13/how-to-setup-clojure-from-scratch/|How to setup Clojure from scratch]], which also specifies how to add autocompletion to the repl. ===== Leiningen ===== Leiningen is a dependency management tool, kinda like Ruby's Bundler, and a project file structure generator. Install it from [[http://leiningen.org/|leiningen.org]]: - Put the lein script inside your ~/bin directory - Invoke ''lein self-install'' And you're done! Now you can create a new project: cd ~/clojure lein new musicprogram This will create a directory structure for your new project, prepopulated with a few files Leiningen uses to keep track of dependencies. Just take a look in ~/clojure/musicprogram/project.clj, which will look something like this: (defproject musicprogram "0.1.0-SNAPSHOT" :description "FIXME: write description" :url "http://example.com/FIXME" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.4.0"]]) ===== Nailgun ===== You can use Nailgun to start a Clojure runtime environment. From the [[http://www.martiansoftware.com/nailgun/|official site]]: "Nailgun is a client, protocol, and server for running Java programs from the command line without incurring the JVM startup overhead. Programs run in the server (which is implemented in Java), and are triggered by the client (written in C), which handles all I/O." If you're using Vim: Nailgun also comes bundled with the excellent vimclojure plugin. Here's how to install the bundled version of Nailgun: make -C ~/.vim/lib/vimclojure-nailgun-client Then tell Leiningen about Nailgun. Add this to the plugins vector of your :user profile located in ~/.lein/profiles.clj (just create the ''profiles.clj'' file if it doesn't exist yet): {:user {:plugins [[lein-tarsier "0.9.1"]]}} Now reinstall your project: onno@asus-ubuntu:~/clojure$ lein new musicprogram And finally use Leiningen to start the Nailgun server through the vimclojure plugin: onno@asus-ubuntu:~/clojure/musicprogram$ lein vimclojure Starting VimClojure server on 127.0.0.1, port 2113 Happy hacking! ===== Vimclojure plugin ===== The vimclojure plugin provides syntax highlighting, a repl in a Vim buffer, and omnicompletion. I installed this plugin using these sources as documentation: * [[https://github.com/daveray/vimclojure-easy|https://github.com/daveray/vimclojure-easy]] * [[http://lostincode.net/blog/clojure-lein-vim|Setting up Clojure 1.2, Leiningen, and Vim]] * [[http://blog.darevay.com/2010/10/how-i-tamed-vimclojure/|How I Tamed VimClojure]] Convenient shortcuts: * ,sr - **s**how **r**epl in a new vim buffer * ,ef - **e**valuate current **f**ile * ,sw - **s**how source for **w**ord under cursor * ,lw - **l**ist documentation for **w**ord under cursor * ,eb - **e**valuate visual **b**lock ... ===== Noir web framework ===== Just use Leiningen to create a new [[http://webnoir.org/|Noir]] project, and Noir will install itself! lein new noir musicprogram ===== Importing libraries ===== We will use Leiningen's project file to add libraries. First, you need to know how to reference the library in the project file. To do this, go to [[http://maven.org|maven.org]], "The Central Repository" for clojure libraries. Here, look up the exact name and latest version of your library. E.g. to look up a math library, search for ''math''. Suppose we want to include ''math.numeric-tower'', version 0.0.1, we would have to add ''[org.clojure/math.numeric-tower "0.0.1"]'' to the Leiningen's project file. Please note that maven.org may also mention a 'groupid'. Apparently, you have to add this in front of the actual library, followed by a slash forward. E.g.: (defproject musicprogram "0.1.0-SNAPSHOT" :description "FIXME: write description" :url "http://example.com/FIXME" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.4.0"] [overtone "0.5.0"] [clj-time "0.4.2"] [org.clojure/math.numeric-tower "0.0.1"]) Here, I've added the math library, overtone, and clj-time library, which is a wrapper around Java date/time classes (the Joda date time API). Now, go to your project's root directory and do ''lein install''. Leiningen will install the specified library for you. In your REPL, issue the command ''use'': user=>(use 'clj-time.core) nil A simple test: user=> (now) # And for the math library you'd have to include ''clojure.'': user=> (use 'clojure.math.numeric-tower) nil user=>(floor 1.3) 1.0 ===== Routes ===== Routes are matches for urls. If you type in something like ''http://localhost:8080/welcome'', Noir will try to find a matching ''/welcome'' route. You can define routes using the ''defpage'' macro. As far as I can tell, it doesn't really matter where defpage is called, as long as it's in a file inside the ''views'' directory. Here's an example where we've replaced the default ''welcome'' page of Noir: (ns lalea.views.index (:require [lalea.views.common :as common] [noir.response :as resp]) (:use [noir.core :only [defpage]])) (defpage "/" [] (if (not-logged-in?) (resp/redirect "/login") (common/layout [:p "Welcome to lalea from index.clj"]))) Whenever you type in ''http://localhost:8080'', Noir will match this url with the "/" route and display the Welcome message - if you're logged in. In this example, the login logic is contained inside the defpage call. This is not very efficient, because we don't want to repeat this code for each section of the website where logging in is required. Noir offers a ''pre-route'' macro for applying filters. Every time you hit a url without having met certain pre-conditions, such as being logged in, the pre-route will be applied. Pre-routes are applied before any other matches defined with ''defpage''. Here's an example of a website where everything is shut tight, except for the login page itself: (ns lalea.views.index (:require [lalea.views.common :as common] [noir.response :as resp]) (:use [noir.core :only [defpage pre-route]])) (pre-route [:any [":anything" :anything #"^(?!\/login$).*"]] {} (when-not (is-logged-in?) (resp/redirect "/login"))) (defpage "/" [] (common/layout [:p "Welcome to lalea from index.clj"])) This is a complicated example, because we're using a regex to define the route. A simpler example could be: (pre-route "/admin/*" {} (when-not (is-logged-in?) (resp/redirect "/login"))) This example would apply the filter only to sections of the website that are located in the ''/admin'' section. Now, to use a regex, you need to be aware of how pre-routes work. This is described in more detail on this aptly named website: [[https://github.com/weavejester/compojure/wiki/Routes-In-Detail|Routes In Detail]]. Our particular regex is explained here on [[http://stackoverflow.com/questions/10254774/java-regex-that-matches-anything-but-literal-string-nil-or-nil|Stackoverflow]]. ===== Accessing a (MySQL) Database ===== ==== JDBC driver ==== You can use the Java Database Connection driver to access your database. Here's an example consisting of a Leiningen ''project.clj'' file and some actual code. ;;;; File: project.clj (defproject db-app "0.1.0-SNAPSHOT" :description "FIXME: write description" :url "http://example.com/FIXME" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.4.0"] [org.clojure/java.jdbc "0.2.3"] [mysql/mysql-connector-java "5.1.6"]]) And the actual retrieval code: (ns db-app.core (:require [clojure.java.jdbc :as sql])) (def db {:classname "com.mysql.jdbc.Driver" :subprotocol "mysql" :subname "//localhost:3306/your-db-name" :user "your-db-user" :password "your-db-password"}) (defn list-users [] (sql/with-connection db (sql/with-query-results rows ["select username, email from mdl_user"] (println rows)))) To test this, just call ''(list-users)'' in your repl. ==== Korma ==== "Korma is a domain specific language for Clojure" which you can use to access your database. Korma's [[http://sqlkorma.com/|website]] explains everything in more detail. Here's a comprehensive example, consisting of a Leiningen project file and an actual script. Put the following code in your project.clj file, and execute ''lein install'' on the command line (within your project directory). ;;;; File: project.clj (defproject korma-test "0.1.0-SNAPSHOT" :description "FIXME: write description" :url "http://example.com/FIXME" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.4.0"] [org.clojure/java.jdbc "0.2.3"] [mysql/mysql-connector-java "5.1.6"] [korma "0.3.0-beta11"]]) And here's a sample script to actually access a database and retrieve some rows from a table: (ns korma-test.core (:use [korma.core] [korma.db])) (defdb db (mysql {:host "localhost" :port "3306" :delimiters "`" :db "your-db-name" :user "your-db-user" :password "your-db-password" })) (defentity users (table :users) (database db)) (select users (fields :username, :email) (where {:lastname "Jones"})) Nice to know: "you can get a string of the SQL instead of executing by using the sql-only mode": (sql-only (select users)) ==== Consolidating Configuration Settings In One Location ==== If you're using a web app framework like Noir, the business logic for each entity of your domain will be stored in a separate file, under a directory ''models''. Obviously, we don't want to store the database settings in each of those files as well. Here's my solution for Noir: * Put the database configuration settings in a file called ''config.clj'', under my-app/src/my-app. * Call ''load-file'' in each model file (this seems the only way to circumvent certain limitations on indirectly including namespaces, see discussion below). Example: (use 'korma.core) (require '[korma.db :as db]) (defdb db (mysql {:host "localhost" :port "3306" :delimiters "`" :db "my-app-db-name" :user "db-user" :password "db-password" })) (ns lalea.models.user) (load-file "./src/my-app/config.clj") (defentity user (table :users)) Please note that using plain ''load'' resulted in vague compilation errors for me. === Discussion === I could not get the required korma namespace 'included' (so to speak) indirectly. In other words: it seems impossible to include a namespace A which includes namespace B and then have the functions of B available in the namespace that does the including of A... But I cannot imagine that Clojure would make you include all of the indirectly included libraries **again**. Obviously, I have to dive further into this. Looks as though this discussion might be relevant: [[http://stackoverflow.com/questions/6881025/how-do-you-organize-function-names-when-building-clojure-libraries-for-public-co|How do you organize function names when building clojure libraries for public consumption?]] Maybe this is the beginning of a solution: [[http://stackoverflow.com/questions/4690758/splitting-a-clojure-namespace-over-multiple-files|Splitting a Clojure namespace over multiple files]]. But then again, this article seems to suggest that it is (yet) impossible to do what we want: [[http://onclojure.com/2010/02/17/managing-namespaces/|Managing namespaces]]. Here's a quote from the (2010) article: "I’d like to be able to set up a namespace to my taste and then be able to use it as a basis for deriving other namespaces. With that possibility, I would define a master namespace once per project, being careful to always use the :only option in :refer-clojure and :use. All other namespaces in my project would then be based on this master namespace and only add or remove symbols for their specific local needs." That's exactly what I need, but what's apparently not part of the Clojure core... A huge disappointment!