clojure and the web
DESCRIPTION
An introduction to the Ring and Compojure libraries for clojure. Presentation from the February meeting of the Austin Clojure Meetup.TRANSCRIPT
Clojure and the WebNick Bailey
Who is this guy?
Nick Bailey
@nickmbailey
Clojure at DataStax
OpsCenter - Monitoring tool for Apache Cassandra
Server agents written in clojure
Agent communication done using http ( Ring and Compojure)
What is Compojure?
https://github.com/weavejester/compojure/wiki
“Compojure is a small web framework for the Clojure programming language. It uses a concise DSL to generate Ring handler functions.”
Ok, What is Ring?
https://github.com/mmcgrana/ring
“Ring is a Clojure web applications library inspired by Python's WSGI and Ruby's Rack. By abstracting the details of HTTP into a simple, unified API, Ring allows web applications to be constructed of modular components that can be shared among a variety of applications, web servers, and web frameworks.”
Ring Architecture
Adapters
Handlers
Middleware
Request Map
Response Map
Adapters
Responsible for implementing the HTTP protocol.
Basically, the server.
Don’t worry, you don’t need to write your own
Ring uses Jetty
Handlers
Your application
Process request, return a response
Middleware
Augment the functionality of a Handler.
Can:
Modify Requests
Modify Responses
Code or GTFO(ns compojure-talk.core (:use ring.adapter.jetty)) ; Middleware (defn wrap-upcase [app] (fn [req] (let [orig-resp (app req)] (assoc orig-resp :body (.toUpperCase (:body orig-resp)))))) ; Handler (defn app [req] {:status 200 :headers {"Content-Type" "text/html"} :body "Hello World from Ring"}) (def upcase-app (wrap-upcase app)) ; Run the adapter (run-jetty upcase-app {:port 8080})
Uppercase the response from the handler
Basic, ‘Hello World’ Handler
Run our adapter
But Wait, There’s More
See: ring.middleware
Serve static files -- ring.middleware.file
Browser cookies -- ring.middleware.cookies
User sessions -- ring.middleware.session
And much more: https://github.com/mmcgrana/ring
Note for Developing
ring.middleware.reload
Automatically reload handler before each request.
Allows for easy testing
Ok, What about Compojure
From earlier: “It uses a concise DSL to generate Ring handler functions.”
An easy an concise way to generate the handler part of your ring application.
The Compojure DSL
defroutes
Macro taking a sequence of routes and the code to process requests that match those routes.
(defroutes app (<route>) (<route>) (<route>)
Anatomy of a Route
Each route has 4 components
(<HTTP method> <URI> <Req. Destructuring> <response>)
HTTP Method
One of the http method types defined by compojure
GET, POST, PUT, DELETE, HEAD, ANY
(GET <URI> <Req. Destructuring> <response>)
URI
Simple the URI this route should match
(GET “/hello” <Req. Destructuring> <response>)
Request Destructuring
Dynamic URIs, URL Params, POST bodies
We’ll come back to this
(GET “/hello” [] <response>)
Response
How we want to handle the request
(GET “/hello” [] “Hello World from Compojure”)
Hello World, Revisited (ns compojure-talk.core (:use ring.adapter.jetty)) ; Middleware (defn wrap-upcase [app] (fn [req] (let [orig-resp (app req)] (assoc orig-resp :body (.toUpperCase (:body orig-resp)))))) ; Handler (defroutes app (GET "/hello" [] "Hello World from Compojure")) (def upcase-app (wrap-upcase app)) ; Run the adapter (run-jetty upcase-app {:port 8080})
Note we still have ourcustom middleware
We use defroutes tocreate a handler
Back to Destructuring
Access to URI parameters and request body
In the basic form, bind parameter maps and body value to local vars
(GET “/test” {params :params} (println params))
Take full advantage of clojure destructuring
(GET “/hello” {{user :user} :params} (str “Hello” user “!”))
URI Destrcturing
Match dynamic URIs
(GET “/hello/:user” [user] (str “Hello” user “!”))
Note: the destructuring syntax above is short for “{{user :user} :params}”
Also allows for advanced pattern matching in the URI
Our Final Hello
(ns compojure-talk.compojure (:use compojure.core, ring.adapter.jetty, compojure.handler)) (defroutes app (GET "/hello/:user" [user] (str "Hello " user "!")) (GET "/hello" {{user :user} :params} (if user (str "Hello " user "!") "Hello from Compojure!"))) ; Run the adapter (run-jetty (site app) {:port 8080})
Links
https://github.com/mmcgrana/ring
https://github.com/weavejester/compojure
QUESTIONS?