Clojure and the Web
       Nick Bailey
Who is this guy?


Nick Bailey

nick@datastax.com

@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                                 Uppercase the response
(defn wrap-upcase [app]
  (fn [req]                                     from the handler
    (let [orig-resp (app req)]
      (assoc orig-resp
             :body
             (.toUpperCase
               (:body orig-resp))))))

; Handler
(defn app [req]                              Basic, ‘Hello World’ Handler
    {: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})          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                                        Note we still have our
(defn wrap-upcase [app]
  (fn [req]                                          custom middleware
    (let [orig-resp (app req)]
      (assoc orig-resp
             :body
             (.toUpperCase
               (:body orig-resp))))))

; Handler                                           We use defroutes to
(defroutes app
  (GET "/hello" [] "Hello World from Compojure"))    create a handler
(def upcase-app (wrap-upcase app))


; Run the adapter
(run-jetty upcase-app {:port 8080})
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?

Clojure and the Web

  • 1.
    Clojure and theWeb Nick Bailey
  • 2.
    Who is thisguy? Nick Bailey nick@datastax.com @nickmbailey
  • 3.
    Clojure at DataStax OpsCenter- Monitoring tool for Apache Cassandra Server agents written in clojure Agent communication done using http ( Ring and Compojure)
  • 4.
    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.”
  • 5.
    Ok, What isRing? 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.”
  • 6.
  • 7.
    Adapters Responsible for implementingthe HTTP protocol. Basically, the server. Don’t worry, you don’t need to write your own Ring uses Jetty
  • 8.
  • 9.
    Middleware Augment the functionalityof a Handler. Can: Modify Requests Modify Responses
  • 10.
    Code or GTFO (nscompojure-talk.core (:use ring.adapter.jetty)) ; Middleware Uppercase the response (defn wrap-upcase [app] (fn [req] from the handler (let [orig-resp (app req)] (assoc orig-resp :body (.toUpperCase (:body orig-resp)))))) ; Handler (defn app [req] Basic, ‘Hello World’ Handler {: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}) Run our adapter
  • 11.
    But Wait, There’sMore 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
  • 12.
    Note for Developing ring.middleware.reload Automatically reload handler before each request. Allows for easy testing
  • 13.
    Ok, What aboutCompojure 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.
  • 14.
    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>)
  • 15.
    Anatomy of aRoute Each route has 4 components (<HTTP method> <URI> <Req. Destructuring> <response>)
  • 16.
    HTTP Method One ofthe http method types defined by compojure GET, POST, PUT, DELETE, HEAD, ANY (GET <URI> <Req. Destructuring> <response>)
  • 17.
    URI Simple the URIthis route should match (GET “/hello” <Req. Destructuring> <response>)
  • 18.
    Request Destructuring Dynamic URIs,URL Params, POST bodies We’ll come back to this (GET “/hello” [] <response>)
  • 19.
    Response How we wantto handle the request (GET “/hello” [] “Hello World from Compojure”)
  • 20.
    Hello World, Revisited (nscompojure-talk.core (:use ring.adapter.jetty)) ; Middleware Note we still have our (defn wrap-upcase [app] (fn [req] custom middleware (let [orig-resp (app req)] (assoc orig-resp :body (.toUpperCase (:body orig-resp)))))) ; Handler We use defroutes to (defroutes app (GET "/hello" [] "Hello World from Compojure")) create a handler (def upcase-app (wrap-upcase app)) ; Run the adapter (run-jetty upcase-app {:port 8080})
  • 21.
    Back to Destructuring Accessto 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 “!”))
  • 22.
    URI Destrcturing Match dynamicURIs (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
  • 23.
    Our Final Hello (nscompojure-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})
  • 24.
  • 25.