Webutvikling med Clojure


mandag 25. oktober 2010 Clojure Webutvikling

Web_Development

Jeg har begynt å eksperimentere med litt mer seriøs utvikling i Clojure. Jeg har testet ut en "full stack" for webutvikling, og er nå blitt kjent med tilstrekkelig med byggestener for å implementere fullverdige, databasedrevne webløsninger. Gjør deg klar for å bli introdusert til en veldig spennende verden – webutvikling i Clojure er nemlig både gøy og enkelt!

Leiningen

Første byggesten man bør bli kjent med, uavhengig av om man vil bedrive web- eller annen utvikling i Clojure, heter Leiningen. Dette er et kommandolinjeverktøy som hjelper deg å generere filstrukturen for prosjektet ditt, definere og laste ned avhengigheter, samt bygging og deployment. Det baserer seg på Maven, som Java-folket bruker, men er mye enklere å forholde seg til. Dessuten konfigurerer man Leiningen med Clojure-kode, ikke XML.

Når jeg skal starte et nytt prosjekt skriver jeg for eksempel kommandoen "lein new theUltimateBlog", og leiningen genererer diverse filer for meg – blant annet en project.clj fil hvor jeg kan liste opp avhengighetene mine. Her er en typisk prosjektfil for et webprosjekt:

1 (defproject theUltimateBlog "0.1.0-SNAPSHOT"
2   :description "Yet another blog engine in Clojure"
3   :dependencies [[org.clojure/clojure "1.2.0"]
4                  [org.clojure/clojure-contrib "1.2.0"]
5                  [org.apache.derby/derby "10.6.2.1"]
6                  [ring/ring "0.3.1"]
7                  [compojure "0.5.2"]
8                  [hiccup "0.3.0"]])

Med enkle kommandoer kan jeg nå for eksempel laste ned alle avhengighetene i prosjektet, eller kompilere det. Leiningen kan blant annet produsere en såkalt uberjar, som er en deploymentløsning hvor prosjektet ditt pluss alle avhengighetene (inkludert selve Clojure) pakkes ned i én eksekverbar jar-fil. Dette er ideelt om du trenger å distribuere en applikasjon til klientmaskiner som kanskje ikke har Clojure fra før.

På Github finner du en tutorial med alt du trenger for å komme igang med Leiningen. Det finnes dessuten et alternativt build-verktøy som heter Cake man bør ta en titt på. Cake gir deg større muligheter i forhold til både innebygde oppgaver og til å definere dine egne tasks. Jeg tviler på at jeg tar feil når jeg sier navnet kommer fra Ruby's Rake.

Ring

Den neste byggeklossen jeg gjorde meg kjent med var Ring. Prosjektet tilbyr webutviklere et stadard grensesnitt for å snakke med webservere, og spiller dermed samme rolle som Rack gjør i Ruby-verden.

Ring har et sett med biblotek for å håndtere ting som session, filopplasting osv., men styrken til Ring er at det er en felles plattform for utvikling av andre webutviklings-rammeverk. Det er dermed mange å velge blant, men jeg har testet ut..

Compojure

Det finnes altså mange rammeverk for webutvikling i clojure – Compojure virker som det mest kjente/omtalte, men man bør også ta en titt på Moustache, Conjure (Rails-inspirert) og Funkyweb. Disse rammeverkene gir deg et høyerenivås API for å definere ting som ruter og controller-logikk.

Her er et eksempel på en meget enkel webapp laget med Compojure og Ring. På roten ("/") vil den svare med "Hello World". På alle andre ruter vil den si "Page not found".

1 (ns hello-world
2   (:use compojure.core, ring.adapter.jetty))
3
4 (defroutes main-routes
5   (GET "/" [] "<h1>Hello World</h1>")
6   (compojure.route/not-found "<h1>Page not found</h1>"))
7
8 (run-jetty main-routes {:port 8080})

Eksempelet bruker en "embedded" webserver som heter Jetty, og den kjøres opp på port 8080.

I tillegg bør jeg nevne et prosjekt som heter Sandbar, som bygger på og utvider Compojure og Ring med ekstra funksjonalitet og abstraksjoner i forhold til bl.a. autorisering, autentisering og forms validering.

Hiccup

Når en har definert ruter og kontrollere i for eksempel Compojure trenger man en måte å generere dynamisk HTML. Også her finnes det en rekke biblotek man kan benytte. Jeg valgte å teste ut Hiccup, som gir deg en Clojure DSL for å definere HTML som minner mye om Haml i Ruby. For dem som mener det er bedre å kode viewene i HTML direkte finnes det andre prosjekter som for eksempel Fleet, men jeg synes det er ganske interresant å kunne manipulere HTML i form at Clojure-data, og har derfor større tro på Hiccup.

Ta for eksempel dette helt vilkårlige stykke med HTML:

10 <div id="main">
11   <h1>Hello World</h1>
12   <p>
13     <a href="http://www.lipsum.com/">Lorem ipsum</a> 
14     dolor sit amet, consectetur adipiscing elit. Nam dictum.
15   </p>
16   Tags:
17   <ul>
18     <li>Tag 1</li>
19     <li>Tag 2</li>
20     <li>Tag 3</li>
21   </ul>
22 </div> 

Nedenfor ser du en liten funksjon som ved hjelp av Hiccup returnerer nøyaktig samme HTML.

19 (defn display []
20       (html
21         [:div#main
22           [:h1 "Hello World"]
23           [:p (link-to "http://www.lipsum.com/" "Lorem ipsum")
24               " dolor sit amet, consectetur adipiscing elit. Nam dictum."]
25           "Tags:" (unordered-list ["Tag 1" "Tag 2" "Tag 3"])]))

Man oppretter og strukturerer HTML ved hjelp av vektorer og keywords. [:b "foo"] omgjøres for eksempel til <b>foo</b>. I tillegg ser du bruk av et par hjelpefunksjoner, link-to som oppretter en HTML-link, og unordered-list som oppretter en liste. Hiccup har mange slike funksjoner, spesielt knyttet til generering av skjema-HTML.

PS: Et tredje biblotek man bør ta en titt på er Enlive, som baserer seg på "selektorer" (ala CSS selectors) og transformering. Og så finnes det faktisk også en Haml-klone, men for meg ser Hiccup bedre ut. 

Og hvis du ønsker den samme styrken når det kommer til CSS kan du bruke cssgen-bibloteket til å kode stylesheets i Clojure.

Databaser

Den siste byggeklossen man normalt behøver for å lage webapps er persistering. I clojure.contrib.sql finner vi grei støtte for å jobbe med relasjonsdatabaser via jdbc. Her er et lite eksempel hvor jeg bruker en Derby-database, oppretter en enkel tabell med to kollonner, inserter et par rader, og til slutt leser dem ut igjen.

10 (ns myFirstDb.core
11     (:use [clojure.contrib.sql :as sql :only ()]))
12
13 (def db {:classname   "org.apache.derby.jdbc.EmbeddedDriver"
14          :subprotocol "derby"
15          :subname     "c:\\temp\\myFirstDb.db"
16          :create      true})
17
18 (defn create-tasks-table []
19       (sql/create-table :task
20         [:description "varchar(255)"]
21         [:state :int]))
22
23 (def *state-open* 0)
24
25 (defn insert-task [description]
26       (sql/insert-rows :task
27         [description *state-open*]))
28
29 (defn main []
30       (sql/with-connection db
31          (create-tasks-table)
32          (insert-task "Buy milk")
33          (insert-task "Write a blog")
34          (sql/with-query-results res
35             ["SELECT * FROM task"]
36             (doseq [rec res]
37                (println rec)))))
38
39 ; Output when running main:
40 ; {:description Buy milk, :state 0}
41 ; {:description Write a blog, :state 0}

For et mere komplett eksempel kan du ta en titt på testkoden til bibloteket.

Contrib.sql er egentlig alt man trenger, men det finnes andre abstraksjoner man med fordel kan ta en titt på. clj-record er for eksempel et forsøk på å gjenskape Ruby on Rail's ActiveRecord i Clojure.

Om du er bitt av NoSQL-basillen finnes det selvsagt diverse Clojure-biblotek for deg også. Congomongo er en wrapper for Java API'et til MongoDB, mens du kan bruke clojure-couchdb om du er glad i Apache CouchDB. Her er litt eksempelkode fra det førstnevnte:

43 (ns my-mongo-app
44     (:use somnium.congomongo))     ; use the congomongo lib
45
46 (mongo! :db "mydb")                ; setup database to use
47
48 (insert! :robots {:name "robby"})  ; insert some data
49
50 (def my-robot (fetch-one :robots)) ; read some data
51
52 ; my-robot => { :name "robby",
53 ;               :_id  #<ObjectId> "0c23396f7e53e34a4c8cf400">,
54 ;               :_ns  "robots" }

Konklusjon

Jeg har nå dratt deg gjennom en komplett liste med byggeklosser du kan bruke for å lage komplette webløsninger i Clojure. Det kan kanskje virke overveldende til å begynne med, men hver liten bit er veldig enkel å forholde seg til, og Clojure har virkelig potensiale til å være en ypperlig plattform for webutvikling. Det er først og fremst språket i seg selv som er årsaken til dette, men mye innsats har også gått inn i å lage praktiske API'er som gjør utviklingen rask samtidig som man fokuserer på det som er viktig.

Det er min vurdering at Clojure kan gjøre deg minst like produktiv som Ruby på dette feltet.

Clojure-prosjekter nevnt i denne posten: Cake | clj-haml | clj-record | Clojure Contrib | Clojure-CouchDB | Compojure | Congomongo | Conjure | cssgen | Enlive | Fleet | Funkyweb | Hiccup | Leiningen | Moustache | Ring | Sandbar.


comments powered by Disqus