Compojure Setup für schnelle Entwicklung


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.

Weitere Artikel

Neue Tastatur: Pok3r Vortex RGB

New minecraft survival mod for 1.11.2

Änderungen beim Flug mit Quadkoptern

Ein paar Fotos

Nach den Crossfit Open

Crossfit Open WOD 17.5

Crossfit Open WOD 17.4

Crossfit Open WOD 17.3

Crossfit Open Workout 17.2

Crossfit Open Workout 17.1