Saturday, 16 May 2009

Using Clojure and Ext/JS

Ext/JS is a popular framework for building Ajax interfaces. The model for Ajax applications is dead simple. Build a UI using JavaScript, and populate it using JSON retrieved from the server (I'm pretty sure there's more to it, but I like simple!).

JavaScript and Lisp languages should go together pretty well, Doug Crockford has described JavaScript as Lisp in C's clothing.

I haven't done enough JavaScript programming to be dangerous just yet, so I'll start with something simple from the Ext tutorial. The goal will be to display a data-grid populated by stuff retrieved using the persistence API I looked at previously. Here's the end goal:

Ext Datagrid populated with data from Clojure.

Firstly we need to write a servlet that will retrieve all the stories. If we were going to do this properly we'd parametrize this with restrictions (find me all stories between X and Y, or retrieve the top 100 stories). I'm confident that no-one will ever use this site, so I'll just go for brute force retrieve everything!

Using the persistence API we prepare a query object to find everything matching the given type and lazily convert this (map) each element across using entity-as-map


(defn get-all
[type]
(let [query (Query. (str type))
service (DatastoreServiceFactory/getDatastoreService)]
(map entity-to-map (.asIterable (.prepare service query)))))


Now we can retrieve stories, we need to squirt this back as JSON. There's a package, clojure-json that does this for you, but I (foolishly) decided to do it the quick and dirty way and print out a string!


(ns news.liststory
(:use (news appengine))
(:gen-class :extends javax.servlet.http.HttpServlet))

(defn story-to-json
[s]
(str "{\"title\":\"" (get s "title") "\"" ",\"body\":" "\"" (get s "body") "\"},"))

(defn -doGet
[_ request response]
(.setContentType response "text/xml")
(let [w (.getWriter response) stories (get-all "story")]
(.println w (str "{\"totalCount\":" (count stories) ",\"stories\":["))
(doseq [story stories]
(.println w (str \tab (story-to-json story))))
(.println w (str "]}"))))


So what were aiming to print out is a JSON representation of the stories that looks a little like this.


{"totalCount":3,"stories":[
{"title":"Home of the Clojure Language","body":"http://www.clojure.org/"},
{"title":"Jeff's web page, full of rubbish!","body":"http://www.fatvat.co.uk"},
{"title":"Wikipedia on Clojure","body":"http://en.wikipedia.org/wiki/Clojure"},
]}


Finally, we need something to render this. I took an example as a starting point and ended up with this. (Note that it's referencing local host because I'm running off a local development environment)



Ext.onReady(function(){
var store = new Ext.data.JsonStore({
root: 'stories',
totalProperty: 'totalCount',
idProperty: 'storyId',
fields: [
'body', 'title'
],

proxy: new Ext.data.HttpProxy({
url: 'http://localhost:8080/liststory?'
})
});

var grid = new Ext.grid.GridPanel({
el: 'story-grid',
title: 'Clojure News!',
store: store,
loadMask: true,
height: 400,
columns:[{
header: 'Link',
dataIndex: 'body'
},{
header: 'Description',
dataIndex: 'title'
}]
});

grid.render();
store.load({});
});


That was pretty painless to build. The painful bit was writing out JSON as a string. Lesson learnt, use a library (or build a decent abstraction).

I'm not sure I like mixing my languages, I'd really like a way to have Lisp goodness across the whole stack. One potential option for this is to use Google Web Toolkit. GWT compiles Java into cross-platform Javascript. I could (probably) have Clojure compile to Java which in turn is compiled to Javascript. That sounds fun!