Compojure Setup für schnelle Entwicklung
Von Carsten
Ich hab in den letzten Tagen weiter mit Compojure herumgebastelt und will jetzt meinen aktuellen Stand der Entwicklungsumgebung aufschreiben. Vielleicht hilft es euch ja beim Einstieg. Ich bin jedenfalls etwas enttäuscht, das man keine guten Tutorials und Hilfen findet, wenn man frisch in Clojure und gerade Compojure einsteigt.
Meine Dateien findet ihr jetzt auch in einem Repository bei github.
Als erstes lege ich mit leiningen ein neues Projekt an:
lein new CmpjrAjaxTest
Anschliessend editiere ich die Datei project.clj
und füge die Abhängigkeiten für ein minimales Compojure-Setup ein:
(defproject CmpjrAjaxTest "1.0.0-SNAPSHOT"
:description "FIXME: write description"
:dependencies [[org.clojure/clojure "1.2.0"]
[org.clojure/clojure-contrib "1.2.0"]
[enlive "1.0.0-SNAPSHOT"]
[compojure "0.6.0-RC4"]
[ring/ring-jetty-adapter "0.3.5"]]
:dev-dependencies [[lein-ring "0.3.2"]]
:ring {:handler CmpjrAjaxTest.app/app})
Wichtig sind die beiden letzten Zeilen mit den Keys :dev-dependencies
und :ring
. Damit wird leiningen angewiesen, einen Ring-Server zu starten, der automatisch meine Äderungen nachlädt und immer den aktuellen Stand der Entwicklung präsentiert.
Mit :ring {:handler CmpjrAjaxTest.app/app})
gebe ich damit eine Referenz auf meinen Routen-Handler an leiningen. Die Definition findet sich in der Datei app.clj
:
(ns CmpjrAjaxTest.app
(:use compojure.core
clojure.contrib.json
net.cgrand.enlive-html
ring.adapter.jetty
CmpjrAjaxTest.core)
(:require [compojure.route :as route]
[compojure.handler :as handler]))
(deftemplate index "resources/test.html"
[cmap]
[:span#msg] (content (:msg cmap)))
(defroutes main-routes
(GET "/" [] (index {:msg Hello World!"}))
(GET "/msg/:msg" [msg] (index {:msg msg}))
(GET "/json" []
{:headers {"Content-Type" "application/json"}
:body (json-str {:foo "foo", :bar "bar"})})
(route/resources "/")
(route/not-found "Page not found"))
(def app
(handler/site main-routes))
(defn dev []
(run-jetty #'app {:port 8080}))
Hier passiert jetzt einiges. Erst einmal übernimmt Compojure für mich alles, was mit Routing der Request zu tun hat. Ich definiere mit (defroutes NAME ...)
nur die Zuordnung von Requests zu meinen Funktionen. Die Funktionen liefern anschliessend das Ergebnis, also eine HTML-Seite. Oder ein JSON-Objekt. Oder irgendwas ganz anderes.
Die erste Route führt ihr aus, wenn ihr einfach http://127.0.0.1:3000
im Browser öffnet. Dort wird nur das Template geladen und Hello, World!
ausgegeben. Interessanter wird das zweite Beispiel:
(defroutes main-routes
...
(GET "/msg/:msg" [msg] (index {:msg msg}))
...)
Diese Route führt ihr aus, wenn ihr im Browser http://127.0.0.1:3000/msg/blahfasel
öffnet. Alles nach msg/
wird von Compojure jetzt als Parameter in meine Funktion übergeben.
Und noch interessanter ist das dritte Beispiel:
(defroutes main-routes
...
(GET "/json" []
{:headers {"Content-Type" "application/json"}
:body (json-str {:foo "foo", :bar "bar"})})
...)
Wenn ihr also http://127.0.0.1:3000/json
im Browser öffnet, wird euch ein JSON-Objekt mit 2 Attributen zurückgegeben. Das ganze läuft über die ganz normale clojure.contrib JSON API, hat also weder was mit Compojure oder meiner Template-Engine zu tun. Ich erzeuge einfach mit folgenden Aufruf ein JSON-Objekt aus einer Map:
(json-str {:foo "foo", :bar "bar"})
Und das Ergebnis ist folgendes:
{"foo":"foo","bar":"bar"}
Ich habe in meinem Beispiel bereits die Template-Engine Enlive verwendet. Damit kann ich meine Antwort aus einer HTML-Datei lesen, mit Variablen anreichern und dem Nutzer zurückliefern. Dazu definiere ich mir ein Template, das eine map
mit Werten bekommt und in die Datei einsetzt:
(deftemplate index "resources/test.html"
[cmap]
[:span#msg] (content (:msg cmap)))
Enlive arbeitet mit Selektoren, wer CSS und/oder JQuery kennt, wird sich da gleich wohl fühlen. Für mich reicht das erstmal aus und ich will für meine Beispiele so einfach wie möglich bleiben. Nachher versteh ich das selbst nicht mehr… 😉
Jetzt kann ich mit folgendem Kommando den Server starten und meine Seite bewundern. Anschliessend kann ich meine Dateien bearbeiten. leiningen übernimmt für mich das Neuladen der Äderungen und ich brauche im Browser nur noch F5 zu drücken. Hiermit wird der Server gestartet:
$ lein ring server
2011-02-24 18:14:59.362:INFO::Logging to STDERR via org.mortbay.log.StdErrLog
2011-02-24 18:14:59.363:INFO::jetty-6.1.26
2011-02-24 18:14:59.382:INFO::Started SocketConnector@0.0.0.0:3000
Started server on port 3000
Bei mir öffnet sich der Browser dann selbst und ich sehe direkt die Startseite. Viel Spass! 🙂
PS: Ihr könnt euch mein Beispielprojekt auch als ZIP runterladen: CmpjrAjaxTest.zip
PPS: Ihr solltet euch doch lieber gleich mein Repository bei github clonen.