SlideShare ist ein Scribd-Unternehmen logo
1 von 41
Downloaden Sie, um offline zu lesen
Ring

deconstructing the functional web
Norman Richards
orb@nostacktrace.com
Response Map

Request Map

HTTP → {} → fn → {} → HTTP
Handler FN
ring-jetty-adaptor

ring handler
(def server
(ring.adapter.jetty/run-jetty #’handler
{:port 8080 :join? false}))
!

(.stop server)
 ring.adaptor.jetty/proxy-handler

(defn- proxy-handler
  "Returns an Jetty Handler implementation for the given Ring handler."
  [handler]
  (proxy [AbstractHandler] []
    (handle [_ ^Request base-request request response]
      (let [request-map (servlet/build-request-map request)
            response-map (handler request-map)]
        (when response-map
          (servlet/update-servlet-response response response-map)
          (.setHandled base-request true))))))
ring.util.servlet/build-request-map
(defn build-request-map
  "Create the request map from the HttpServletRequest object."
  [^HttpServletRequest request]
  {:server-port
(.getServerPort request)
   :server-name
(.getServerName request)
   :remote-addr
(.getRemoteAddr request)
   :uri
(.getRequestURI request)
   :query-string
(.getQueryString request)
   :scheme
(keyword (.getScheme request))
   :request-method
(keyword (.toLowerCase (.getMethod request)))
   :headers
(get-headers request)
   :content-type
(.getContentType request)
   :content-length
(get-content-length request)
   :character-encoding (.getCharacterEncoding request)
   :ssl-client-cert
(get-client-cert request)
   :body
(.getInputStream request)})
not functional :(
ring.util.servlet/update-servlet-response

(defn update-servlet-response
  "Update the HttpServletResponse using a response map."
  [^HttpServletResponse response, {:keys [status headers body]}]
  (when-not response
    (throw (Exception. "Null response given.")))
  (when status
    (set-status response status))
  (doto response
    (set-headers headers)
    (set-body body)))
Ring Handlers
Handler: (RequestMap → ResponseMap)
(defn handler-nil [req]
{:body nil})
response body
ring.util.servlet/set-body

(defn- set-body
  "Update a HttpServletResponse body with a String, ISeq, File or InputStream."
  [^HttpServletResponse response, body]
  (cond
    (string? body)
      (with-open [writer (.getWriter response)]
        (.print writer body))
    (seq? body)
  ;; …
    (instance? File body)
  ;; …
    (nil? body)
      nil
    :else
      (throw (Exception. ^String (format "Unrecognized body: %s" body)))))
(defn handler-string [req]
{:body "hello world”})
(defn handler-file [req]
{:body (clojure.java.io/file "info.txt")})
(defn handler-status [req]
{:status 402
:headers {"Location"
"bitcoin:1G9TyAaKrfJn7q4Vrr15DscLXFSRPxBFaH?amount=.001"}})
Handlers can return status code and headers
ring.util.response/*

A few response helpers

(defn response
  "Returns a skeletal Ring response with the given body, status of 200, and no
headers."
  [body]
  {:status 200
   :headers {}
   :body
body})
!

(defn not-found
  "Returns a 404 'not found' response."
  [body]
  {:status 404
   :headers {}
   :body
body})
!

(defn redirect
  "Returns a Ring response for an HTTP 302 redirect."
  [url]
  {:status 302
   :headers {"Location" url}
   :body
""})
(defn handler [req]
(response/response "Hello, world!”))
!

(defn handler [req]
(response/redirect "http://lmgtfy.com/?q=http+redirect"))
!

(defn handler [req]
(response/resource-response "hello.txt"))
Building up a response

(defn handler [req]
(-> (response/response "")
(response/status 302)
(response/header "Location" "http://www.google.com")))
Wrapping requests
(middleware)
Middleware: (Handler → Handler)
(defn handler-reload1 [req]
(response/response (reload-me/some-work)))
A function we’d like to be reloaded if it changes
(defn handler-reload2 [req]
(require 'ringtest.reload-me :reload)
(handler-reload1 req))
The original handler is wrapped
Abstracting the reloading

(defn wrap-reload [other-handler]
(fn [req]
(require 'ringtest.reload-me :reload)
(other-handler req)))
 
(def handler-reload3 (wrap-reload #'handler-reload1))
ring.middleware.reload/wrap-reload

(defn wrap-reload
  "Reload namespaces of modified files before the request is passed to the
supplied handler.
!

Takes the following options:
:dirs - A list of directories that contain the source files.
Defaults to ["src"]."
  [handler & [options]]
  (let [source-dirs (:dirs options ["src"])
        modified-namespaces (ns-tracker source-dirs)]
    (fn [request]
Smarter reloading surrounding the wrapped handler
      (doseq [ns-sym (modified-namespaces)]
        (require ns-sym :reload))
      (handler request))))
ring.server.standalone/add-middleware

This is what “lein ring server” does

(defn- add-middleware [handler options]
  (-> handler
      (add-auto-refresh options)
      (add-auto-reload options)
      (add-stacktraces options)))
Middleware stacks
(middleware1 (middleware2 (middleware3 handler)))
(defn ring-stack [handler]
Our custom “ring” middleware stack
(-> handler
(wrap-reload)
(wrap-stacktrace)))
 
(defonce server-atom (atom nil))
And some custom sever code
 
(defn start [handler]
(swap! server-atom
(fn [server]
(when server (.stop server))
(jetty/run-jetty handler
{:port 8080 :join? false}))))
 
(defn stop []
(swap! server-atom
(fn [server]
(when server (.stop server))
nil)))
ring.middleware.*

•
•
•
•
•
•
•
•
•
•
•
•

wrap-content-type
wrap-cookies
wrap-file-info
wrap-flash
wrap-head 
wrap-keyword-params
wrap-multi-part-params
wrap-nested-params
wrap-not-modified
wrap-params
wrap-resource
wrap-session

Lot’s of middleware to choose from
compojure.handler/api
An existing minimal stack for APIs

(defn api
  "Create a handler suitable for a web API. This adds the following
middleware to your routes:
- wrap-params
- wrap-nested-params
- wrap-keyword-params"
  [routes]
  (-> routes
      wrap-keyword-params
      wrap-nested-params
      wrap-params))
compojure.handler/site
(defn site
  "Create a handler suitable for a standard website. This adds the
following middleware to your routes:
- wrap-session
- wrap-flash
- wrap-cookies
- wrap-multipart-params
- wrap-params
- wrap-nested-params
- wrap-keyword-params
!

A map of options may also be provided. These keys are provided:
:session
- a map of session middleware options
:multipart - a map of multipart-params middleware options"
  [routes & [opts]]
  (-> (api routes)
      (with-opts wrap-multipart-params (:multipart opts))
Extends the API stack
      (wrap-flash)
      (with-opts wrap-session (:session opts))))
Use it, or make your own

(def handler (-> #'app
compojure.handler/site
ring-stack))
 
noir.util.middleware/app-handler
(defn app-handler [app-routes & {:keys [session-options store multipart
middleware access-rules formats]}]
  (letfn [(wrap-middleware-format [handler]
            (if formats (wrap-restful-format handler :formats formats) handler))]
    (-> (apply routes app-routes)
A hook to extend the noir stack
        (wrap-middleware middleware)
        (wrap-request-map)
        (api)
        (wrap-base-url)
        (wrap-middleware-format)
        (with-opts wrap-multipart-params multipart)
        (wrap-access-rules access-rules)
        (wrap-noir-validation)
Lot’s of customization
        (wrap-noir-cookies)
        (wrap-noir-flash)
        (wrap-noir-session
         (update-in session-options [:store] #(or % (memory-store mem)))))))
Routing
Route: (RequestMap → (Option ResponseMap))
Not ring handlers
because they don’t
take a request.

(defn home []
(response/response "Home Page"))
 
(defn foo []
(response/response "Foo Page"))
 
(defn foo-n [n]
(response/response (str "This is Foo#" n)))
 
 
(defn app1 [req]
(condp re-matches (:uri req)
#"/"
(home)
#"/foo"
(foo)
#"/foo/(.*)" :>> #(foo-n (second %))
(response/not-found "Wat")))

Select the page to
show based on URL
Abstract the route dispatch

(defn route-to [handler]
(fn [match]
(if (string? match)
(handler)
(apply handler (rest match)))))
 
(defn app2 [req]
(condp re-matches (:uri req)
#"/"
Cleaner, but still awkward
:>> (route-to home)
 
#"/foo"
:>> (route-to foo)
 
#"/foo/(.*)"
:>> (route-to foo-n)
 
(response/not-found "Wat")))
Include the pattern

(defn my-route [pattern page-fn]
(fn [req]
(if-let [match (re-matches pattern (:uri req))]
((route-to handler) page-fn))))
 
(defn app3 [req]
(let [my-routes [(my-route #"/"
home)
Much cleaner
(my-route #"/foo"
foo)
(my-route #"/foo/(.*)" foo-n)
(my-route #".*"
#(response/not-found "Wat"))]]
(some #(% req) my-routes)))
The first route that responds wins
Routing fn includes method and path

(defn app4 [req]
(let [my-routes [(GET "/" [] (home))
Some extra macro magic
(GET "/foo" [] (foo))
(GET "/foo/:id" [id] (foo-n id))
(route/not-found "Wat")]]
(some #(% req) my-routes)))
 
Some things never change
compojure.core/*
(defn make-route
  "Returns a function that will only call the handler if the method and Clout
route match the request."
  [method route handler]
  (if-method method
    (if-route route
      (fn [request]
        (render (handler request) request)))))
!

(defn- compile-route
  "Compile a route in the form (method path & body) into a function."
  [method route bindings body]
  `(make-route
    ~method ~(prepare-route route)
    (fn [request#]
      (let-request [~bindings request#] ~@body))))
!

(defmacro GET "Generate a GET route."
  [path args & body]
  (compile-route :get path args body))
a collection of routes

(def app5
(routes
(GET "/" [] (home))
(GET "/foo" [] (foo))
(GET "/foo/:id" [id] (foo-n id))
(route/not-found "Wat")))
(defn routing
  "Apply a list of routes to a Ring request map."
  [request & handlers]
  (some #(% request) handlers))
!

(defn routes
  "Create a Ring handler by combining several handlers into one."
  [& handlers]
  #(apply routing % handlers))
(def foo-routes
(routes
(GET "/foo" [] (foo))
(GET "/foo/:id" [id] (foo-n id))))
 
(def app6
(routes
(GET "/" [] (home))
Routing functions nest easily
foo-routes
(route/not-found "Wat")))
(defn foobar-routes [foobar-type]
(routes
(GET "/" [] (str foobar-type " Page"))
(GET "/:id" [id] (str foobar-type "#" id))))
 
(def app7
Not ideal - generates the route fn each call
(routes
(GET "/" [] (home))
(context "/foo" [] (foobar-routes "Foo"))
(context "/bar" [] (foobar-routes "Bar"))
(route/not-found "Wat")))
(defmacro context
  "Give all routes in the form a common path prefix and set of bindings.
!

The following example demonstrates defining two routes with a common
path prefix ('/user/:id') and a common binding ('id'):
!

(context "/user/:id" [id]
(GET "/profile" [] ...)
(GET "/settings" [] ...))"
  [path args & routes]
  `(#'if-route ~(context-route path)
     (#'wrap-context
       (fn [request#]
         (let-request [~args request#]
           (routing request# ~@routes))))))
compojure.response/Renderable
(defprotocol Renderable
  (render [this request]
    "Render the object into a form suitable for the given request map."))
!

(extend-protocol Renderable
  nil …
  String …
  APersistentMap …
  IFn …
  IDeref …
  File …
  ISeq …
  InputStream …
  URL … )

Generate response maps based on types
Norman Richards
orb@nostacktrace.com

Weitere ähnliche Inhalte

Was ist angesagt?

Building Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at StripeBuilding Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at StripeMongoDB
 
Mastering Kotlin Standard Library
Mastering Kotlin Standard LibraryMastering Kotlin Standard Library
Mastering Kotlin Standard LibraryNelson Glauber Leal
 
Building Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at StripeBuilding Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at StripeStripe
 
PostgreSQL Procedural Languages: Tips, Tricks and Gotchas
PostgreSQL Procedural Languages: Tips, Tricks and GotchasPostgreSQL Procedural Languages: Tips, Tricks and Gotchas
PostgreSQL Procedural Languages: Tips, Tricks and GotchasJim Mlodgenski
 
Building Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at StripeBuilding Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at StripeMongoDB
 
Python postgre sql a wonderful wedding
Python postgre sql   a wonderful weddingPython postgre sql   a wonderful wedding
Python postgre sql a wonderful weddingStéphane Wirtel
 
"PostgreSQL and Python" Lightning Talk @EuroPython2014
"PostgreSQL and Python" Lightning Talk @EuroPython2014"PostgreSQL and Python" Lightning Talk @EuroPython2014
"PostgreSQL and Python" Lightning Talk @EuroPython2014Henning Jacobs
 
The Ring programming language version 1.5.4 book - Part 40 of 185
The Ring programming language version 1.5.4 book - Part 40 of 185The Ring programming language version 1.5.4 book - Part 40 of 185
The Ring programming language version 1.5.4 book - Part 40 of 185Mahmoud Samir Fayed
 
Kotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is comingKotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is comingKirill Rozov
 
ZeroMQ Is The Answer
ZeroMQ Is The AnswerZeroMQ Is The Answer
ZeroMQ Is The AnswerIan Barber
 
KubeCon EU 2016: Custom Volume Plugins
KubeCon EU 2016: Custom Volume PluginsKubeCon EU 2016: Custom Volume Plugins
KubeCon EU 2016: Custom Volume PluginsKubeAcademy
 
Dpilot Source Code With ScreenShots
Dpilot Source Code With ScreenShots Dpilot Source Code With ScreenShots
Dpilot Source Code With ScreenShots DeepAnshu Sharma
 
Source Code for Dpilot
Source Code for Dpilot Source Code for Dpilot
Source Code for Dpilot Nidhi Chauhan
 
リローダブルClojureアプリケーション
リローダブルClojureアプリケーションリローダブルClojureアプリケーション
リローダブルClojureアプリケーションKenji Nakamura
 
ZeroMQ: Messaging Made Simple
ZeroMQ: Messaging Made SimpleZeroMQ: Messaging Made Simple
ZeroMQ: Messaging Made SimpleIan Barber
 
(BDT401) Big Data Orchestra - Harmony within Data Analysis Tools | AWS re:Inv...
(BDT401) Big Data Orchestra - Harmony within Data Analysis Tools | AWS re:Inv...(BDT401) Big Data Orchestra - Harmony within Data Analysis Tools | AWS re:Inv...
(BDT401) Big Data Orchestra - Harmony within Data Analysis Tools | AWS re:Inv...Amazon Web Services
 

Was ist angesagt? (20)

Building Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at StripeBuilding Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at Stripe
 
Mastering Kotlin Standard Library
Mastering Kotlin Standard LibraryMastering Kotlin Standard Library
Mastering Kotlin Standard Library
 
Building Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at StripeBuilding Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at Stripe
 
PostgreSQL Procedural Languages: Tips, Tricks and Gotchas
PostgreSQL Procedural Languages: Tips, Tricks and GotchasPostgreSQL Procedural Languages: Tips, Tricks and Gotchas
PostgreSQL Procedural Languages: Tips, Tricks and Gotchas
 
Gevent rabbit rpc
Gevent rabbit rpcGevent rabbit rpc
Gevent rabbit rpc
 
dotCloud and go
dotCloud and godotCloud and go
dotCloud and go
 
Building Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at StripeBuilding Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at Stripe
 
Python postgre sql a wonderful wedding
Python postgre sql   a wonderful weddingPython postgre sql   a wonderful wedding
Python postgre sql a wonderful wedding
 
"PostgreSQL and Python" Lightning Talk @EuroPython2014
"PostgreSQL and Python" Lightning Talk @EuroPython2014"PostgreSQL and Python" Lightning Talk @EuroPython2014
"PostgreSQL and Python" Lightning Talk @EuroPython2014
 
The Ring programming language version 1.5.4 book - Part 40 of 185
The Ring programming language version 1.5.4 book - Part 40 of 185The Ring programming language version 1.5.4 book - Part 40 of 185
The Ring programming language version 1.5.4 book - Part 40 of 185
 
Kotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is comingKotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is coming
 
PostgreSQL and PL/Java
PostgreSQL and PL/JavaPostgreSQL and PL/Java
PostgreSQL and PL/Java
 
How to ride a whale
How to ride a whaleHow to ride a whale
How to ride a whale
 
ZeroMQ Is The Answer
ZeroMQ Is The AnswerZeroMQ Is The Answer
ZeroMQ Is The Answer
 
KubeCon EU 2016: Custom Volume Plugins
KubeCon EU 2016: Custom Volume PluginsKubeCon EU 2016: Custom Volume Plugins
KubeCon EU 2016: Custom Volume Plugins
 
Dpilot Source Code With ScreenShots
Dpilot Source Code With ScreenShots Dpilot Source Code With ScreenShots
Dpilot Source Code With ScreenShots
 
Source Code for Dpilot
Source Code for Dpilot Source Code for Dpilot
Source Code for Dpilot
 
リローダブルClojureアプリケーション
リローダブルClojureアプリケーションリローダブルClojureアプリケーション
リローダブルClojureアプリケーション
 
ZeroMQ: Messaging Made Simple
ZeroMQ: Messaging Made SimpleZeroMQ: Messaging Made Simple
ZeroMQ: Messaging Made Simple
 
(BDT401) Big Data Orchestra - Harmony within Data Analysis Tools | AWS re:Inv...
(BDT401) Big Data Orchestra - Harmony within Data Analysis Tools | AWS re:Inv...(BDT401) Big Data Orchestra - Harmony within Data Analysis Tools | AWS re:Inv...
(BDT401) Big Data Orchestra - Harmony within Data Analysis Tools | AWS re:Inv...
 

Andere mochten auch

The Logical Burrito - pattern matching, term rewriting and unification
The Logical Burrito - pattern matching, term rewriting and unificationThe Logical Burrito - pattern matching, term rewriting and unification
The Logical Burrito - pattern matching, term rewriting and unificationNorman Richards
 
ClojuTRE2015: Kekkonen - making your Clojure web APIs more awesome
ClojuTRE2015: Kekkonen - making your Clojure web APIs more awesomeClojuTRE2015: Kekkonen - making your Clojure web APIs more awesome
ClojuTRE2015: Kekkonen - making your Clojure web APIs more awesomeMetosin Oy
 
Wieldy remote apis with Kekkonen - ClojureD 2016
Wieldy remote apis with Kekkonen - ClojureD 2016Wieldy remote apis with Kekkonen - ClojureD 2016
Wieldy remote apis with Kekkonen - ClojureD 2016Metosin Oy
 
Functional web with clojure
Functional web with clojureFunctional web with clojure
Functional web with clojureJohn Stevenson
 
Frameworkless Web Development in Clojure
Frameworkless Web Development in ClojureFrameworkless Web Development in Clojure
Frameworkless Web Development in ClojureKungi2342
 
Swaggered web apis in Clojure
Swaggered web apis in ClojureSwaggered web apis in Clojure
Swaggered web apis in ClojureMetosin Oy
 
Euroclojure2014: Schema & Swagger - making your Clojure web APIs more awesome
Euroclojure2014: Schema & Swagger - making your Clojure web APIs more awesomeEuroclojure2014: Schema & Swagger - making your Clojure web APIs more awesome
Euroclojure2014: Schema & Swagger - making your Clojure web APIs more awesomeMetosin Oy
 
Ring: Web Apps in Idiomatic Clojure
Ring: Web Apps in Idiomatic ClojureRing: Web Apps in Idiomatic Clojure
Ring: Web Apps in Idiomatic ClojureMark McGranaghan
 
Clojure and the Web
Clojure and the WebClojure and the Web
Clojure and the Webnickmbailey
 
Web programming in clojure
Web programming in clojureWeb programming in clojure
Web programming in clojurebdemchak
 

Andere mochten auch (11)

core.logic introduction
core.logic introductioncore.logic introduction
core.logic introduction
 
The Logical Burrito - pattern matching, term rewriting and unification
The Logical Burrito - pattern matching, term rewriting and unificationThe Logical Burrito - pattern matching, term rewriting and unification
The Logical Burrito - pattern matching, term rewriting and unification
 
ClojuTRE2015: Kekkonen - making your Clojure web APIs more awesome
ClojuTRE2015: Kekkonen - making your Clojure web APIs more awesomeClojuTRE2015: Kekkonen - making your Clojure web APIs more awesome
ClojuTRE2015: Kekkonen - making your Clojure web APIs more awesome
 
Wieldy remote apis with Kekkonen - ClojureD 2016
Wieldy remote apis with Kekkonen - ClojureD 2016Wieldy remote apis with Kekkonen - ClojureD 2016
Wieldy remote apis with Kekkonen - ClojureD 2016
 
Functional web with clojure
Functional web with clojureFunctional web with clojure
Functional web with clojure
 
Frameworkless Web Development in Clojure
Frameworkless Web Development in ClojureFrameworkless Web Development in Clojure
Frameworkless Web Development in Clojure
 
Swaggered web apis in Clojure
Swaggered web apis in ClojureSwaggered web apis in Clojure
Swaggered web apis in Clojure
 
Euroclojure2014: Schema & Swagger - making your Clojure web APIs more awesome
Euroclojure2014: Schema & Swagger - making your Clojure web APIs more awesomeEuroclojure2014: Schema & Swagger - making your Clojure web APIs more awesome
Euroclojure2014: Schema & Swagger - making your Clojure web APIs more awesome
 
Ring: Web Apps in Idiomatic Clojure
Ring: Web Apps in Idiomatic ClojureRing: Web Apps in Idiomatic Clojure
Ring: Web Apps in Idiomatic Clojure
 
Clojure and the Web
Clojure and the WebClojure and the Web
Clojure and the Web
 
Web programming in clojure
Web programming in clojureWeb programming in clojure
Web programming in clojure
 

Ähnlich wie Deconstructing the Functional Web with Clojure

Clojure Workshop: Web development
Clojure Workshop: Web developmentClojure Workshop: Web development
Clojure Workshop: Web developmentSytac
 
InfluxData Platform Future and Vision
InfluxData Platform Future and VisionInfluxData Platform Future and Vision
InfluxData Platform Future and VisionInfluxData
 
(map Clojure everyday-tasks)
(map Clojure everyday-tasks)(map Clojure everyday-tasks)
(map Clojure everyday-tasks)Jacek Laskowski
 
ClojureScript loves React, DomCode May 26 2015
ClojureScript loves React, DomCode May 26 2015ClojureScript loves React, DomCode May 26 2015
ClojureScript loves React, DomCode May 26 2015Michiel Borkent
 
(first '(Clojure.))
(first '(Clojure.))(first '(Clojure.))
(first '(Clojure.))niklal
 
Writing Redis in Python with asyncio
Writing Redis in Python with asyncioWriting Redis in Python with asyncio
Writing Redis in Python with asyncioJames Saryerwinnie
 
Terraform 0.9 + good practices
Terraform 0.9 + good practicesTerraform 0.9 + good practices
Terraform 0.9 + good practicesRadek Simko
 
Avoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.jsAvoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.jscacois
 
A Tour of Building Web Applications with R Shiny
A Tour of Building Web Applications with R Shiny A Tour of Building Web Applications with R Shiny
A Tour of Building Web Applications with R Shiny Wendy Chen Dubois
 
Reitit - Clojure/North 2019
Reitit - Clojure/North 2019Reitit - Clojure/North 2019
Reitit - Clojure/North 2019Metosin Oy
 
Elixir & Phoenix - fast, concurrent and explicit
Elixir & Phoenix - fast, concurrent and explicitElixir & Phoenix - fast, concurrent and explicit
Elixir & Phoenix - fast, concurrent and explicitTobias Pfeiffer
 
Think Async: Asynchronous Patterns in NodeJS
Think Async: Asynchronous Patterns in NodeJSThink Async: Asynchronous Patterns in NodeJS
Think Async: Asynchronous Patterns in NodeJSAdam L Barrett
 
Customising Your Own Web Framework In Go
Customising Your Own Web Framework In GoCustomising Your Own Web Framework In Go
Customising Your Own Web Framework In GoJonathan Gomez
 
Grails transactions
Grails   transactionsGrails   transactions
Grails transactionsHusain Dalal
 
こわくないよ❤️ Playframeworkソースコードリーディング入門
こわくないよ❤️ Playframeworkソースコードリーディング入門こわくないよ❤️ Playframeworkソースコードリーディング入門
こわくないよ❤️ Playframeworkソースコードリーディング入門tanacasino
 

Ähnlich wie Deconstructing the Functional Web with Clojure (20)

Clojure Workshop: Web development
Clojure Workshop: Web developmentClojure Workshop: Web development
Clojure Workshop: Web development
 
InfluxData Platform Future and Vision
InfluxData Platform Future and VisionInfluxData Platform Future and Vision
InfluxData Platform Future and Vision
 
(map Clojure everyday-tasks)
(map Clojure everyday-tasks)(map Clojure everyday-tasks)
(map Clojure everyday-tasks)
 
ClojureScript loves React, DomCode May 26 2015
ClojureScript loves React, DomCode May 26 2015ClojureScript loves React, DomCode May 26 2015
ClojureScript loves React, DomCode May 26 2015
 
(first '(Clojure.))
(first '(Clojure.))(first '(Clojure.))
(first '(Clojure.))
 
Server Side Swift: Vapor
Server Side Swift: VaporServer Side Swift: Vapor
Server Side Swift: Vapor
 
Writing Redis in Python with asyncio
Writing Redis in Python with asyncioWriting Redis in Python with asyncio
Writing Redis in Python with asyncio
 
Hadoop
HadoopHadoop
Hadoop
 
Play vs Rails
Play vs RailsPlay vs Rails
Play vs Rails
 
Terraform 0.9 + good practices
Terraform 0.9 + good practicesTerraform 0.9 + good practices
Terraform 0.9 + good practices
 
Fabric Python Lib
Fabric Python LibFabric Python Lib
Fabric Python Lib
 
Avoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.jsAvoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.js
 
A Tour of Building Web Applications with R Shiny
A Tour of Building Web Applications with R Shiny A Tour of Building Web Applications with R Shiny
A Tour of Building Web Applications with R Shiny
 
Reitit - Clojure/North 2019
Reitit - Clojure/North 2019Reitit - Clojure/North 2019
Reitit - Clojure/North 2019
 
Elixir & Phoenix - fast, concurrent and explicit
Elixir & Phoenix - fast, concurrent and explicitElixir & Phoenix - fast, concurrent and explicit
Elixir & Phoenix - fast, concurrent and explicit
 
Think Async: Asynchronous Patterns in NodeJS
Think Async: Asynchronous Patterns in NodeJSThink Async: Asynchronous Patterns in NodeJS
Think Async: Asynchronous Patterns in NodeJS
 
DevOps with Fabric
DevOps with FabricDevOps with Fabric
DevOps with Fabric
 
Customising Your Own Web Framework In Go
Customising Your Own Web Framework In GoCustomising Your Own Web Framework In Go
Customising Your Own Web Framework In Go
 
Grails transactions
Grails   transactionsGrails   transactions
Grails transactions
 
こわくないよ❤️ Playframeworkソースコードリーディング入門
こわくないよ❤️ Playframeworkソースコードリーディング入門こわくないよ❤️ Playframeworkソースコードリーディング入門
こわくないよ❤️ Playframeworkソースコードリーディング入門
 

Mehr von Norman Richards

An Adventure in Serverless ClojureScript
An Adventure in Serverless ClojureScriptAn Adventure in Serverless ClojureScript
An Adventure in Serverless ClojureScriptNorman Richards
 
Logic programming a ruby perspective
Logic programming a ruby perspectiveLogic programming a ruby perspective
Logic programming a ruby perspectiveNorman Richards
 
Lisp 1.5 - Running history
Lisp 1.5 - Running historyLisp 1.5 - Running history
Lisp 1.5 - Running historyNorman Richards
 
The Lambda Calculus and The JavaScript
The Lambda Calculus and The JavaScriptThe Lambda Calculus and The JavaScript
The Lambda Calculus and The JavaScriptNorman Richards
 

Mehr von Norman Richards (6)

An Adventure in Serverless ClojureScript
An Adventure in Serverless ClojureScriptAn Adventure in Serverless ClojureScript
An Adventure in Serverless ClojureScript
 
Logic programming a ruby perspective
Logic programming a ruby perspectiveLogic programming a ruby perspective
Logic programming a ruby perspective
 
Lisp 1.5 - Running history
Lisp 1.5 - Running historyLisp 1.5 - Running history
Lisp 1.5 - Running history
 
Immutant
ImmutantImmutant
Immutant
 
Vert.X mini-talk
Vert.X mini-talkVert.X mini-talk
Vert.X mini-talk
 
The Lambda Calculus and The JavaScript
The Lambda Calculus and The JavaScriptThe Lambda Calculus and The JavaScript
The Lambda Calculus and The JavaScript
 

Kürzlich hochgeladen

Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...apidays
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAndrey Devyatkin
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...DianaGray10
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfOverkill Security
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...apidays
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century educationjfdjdjcjdnsjd
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDropbox
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CVKhem
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024The Digital Insurer
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdflior mazor
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWERMadyBayot
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodJuan lago vázquez
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherRemote DBA Services
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsNanddeep Nachan
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesrafiqahmad00786416
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businesspanagenda
 

Kürzlich hochgeladen (20)

Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdf
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 

Deconstructing the Functional Web with Clojure

  • 1. Ring deconstructing the functional web Norman Richards orb@nostacktrace.com
  • 2. Response Map Request Map HTTP → {} → fn → {} → HTTP Handler FN
  • 3. ring-jetty-adaptor ring handler (def server (ring.adapter.jetty/run-jetty #’handler {:port 8080 :join? false})) ! (.stop server)
  • 4.  ring.adaptor.jetty/proxy-handler (defn- proxy-handler   "Returns an Jetty Handler implementation for the given Ring handler."   [handler]   (proxy [AbstractHandler] []     (handle [_ ^Request base-request request response]       (let [request-map (servlet/build-request-map request)             response-map (handler request-map)]         (when response-map           (servlet/update-servlet-response response response-map)           (.setHandled base-request true))))))
  • 5. ring.util.servlet/build-request-map (defn build-request-map   "Create the request map from the HttpServletRequest object."   [^HttpServletRequest request]   {:server-port (.getServerPort request)    :server-name (.getServerName request)    :remote-addr (.getRemoteAddr request)    :uri (.getRequestURI request)    :query-string (.getQueryString request)    :scheme (keyword (.getScheme request))    :request-method (keyword (.toLowerCase (.getMethod request)))    :headers (get-headers request)    :content-type (.getContentType request)    :content-length (get-content-length request)    :character-encoding (.getCharacterEncoding request)    :ssl-client-cert (get-client-cert request)    :body (.getInputStream request)}) not functional :(
  • 6. ring.util.servlet/update-servlet-response (defn update-servlet-response   "Update the HttpServletResponse using a response map."   [^HttpServletResponse response, {:keys [status headers body]}]   (when-not response     (throw (Exception. "Null response given.")))   (when status     (set-status response status))   (doto response     (set-headers headers)     (set-body body)))
  • 8. (defn handler-nil [req] {:body nil}) response body
  • 9. ring.util.servlet/set-body (defn- set-body   "Update a HttpServletResponse body with a String, ISeq, File or InputStream."   [^HttpServletResponse response, body]   (cond     (string? body)       (with-open [writer (.getWriter response)]         (.print writer body))     (seq? body)   ;; …     (instance? File body)   ;; …     (nil? body)       nil     :else       (throw (Exception. ^String (format "Unrecognized body: %s" body)))))
  • 11. (defn handler-file [req] {:body (clojure.java.io/file "info.txt")})
  • 12. (defn handler-status [req] {:status 402 :headers {"Location" "bitcoin:1G9TyAaKrfJn7q4Vrr15DscLXFSRPxBFaH?amount=.001"}}) Handlers can return status code and headers
  • 13. ring.util.response/* A few response helpers (defn response   "Returns a skeletal Ring response with the given body, status of 200, and no headers."   [body]   {:status 200    :headers {}    :body body}) ! (defn not-found   "Returns a 404 'not found' response."   [body]   {:status 404    :headers {}    :body body}) ! (defn redirect   "Returns a Ring response for an HTTP 302 redirect."   [url]   {:status 302    :headers {"Location" url}    :body ""})
  • 14. (defn handler [req] (response/response "Hello, world!”)) ! (defn handler [req] (response/redirect "http://lmgtfy.com/?q=http+redirect")) ! (defn handler [req] (response/resource-response "hello.txt"))
  • 15. Building up a response (defn handler [req] (-> (response/response "") (response/status 302) (response/header "Location" "http://www.google.com")))
  • 17. (defn handler-reload1 [req] (response/response (reload-me/some-work))) A function we’d like to be reloaded if it changes
  • 18. (defn handler-reload2 [req] (require 'ringtest.reload-me :reload) (handler-reload1 req)) The original handler is wrapped
  • 19. Abstracting the reloading (defn wrap-reload [other-handler] (fn [req] (require 'ringtest.reload-me :reload) (other-handler req)))   (def handler-reload3 (wrap-reload #'handler-reload1))
  • 20. ring.middleware.reload/wrap-reload (defn wrap-reload   "Reload namespaces of modified files before the request is passed to the supplied handler. ! Takes the following options: :dirs - A list of directories that contain the source files. Defaults to ["src"]."   [handler & [options]]   (let [source-dirs (:dirs options ["src"])         modified-namespaces (ns-tracker source-dirs)]     (fn [request] Smarter reloading surrounding the wrapped handler       (doseq [ns-sym (modified-namespaces)]         (require ns-sym :reload))       (handler request))))
  • 21. ring.server.standalone/add-middleware This is what “lein ring server” does (defn- add-middleware [handler options]   (-> handler       (add-auto-refresh options)       (add-auto-reload options)       (add-stacktraces options)))
  • 23. (defn ring-stack [handler] Our custom “ring” middleware stack (-> handler (wrap-reload) (wrap-stacktrace)))   (defonce server-atom (atom nil)) And some custom sever code   (defn start [handler] (swap! server-atom (fn [server] (when server (.stop server)) (jetty/run-jetty handler {:port 8080 :join? false}))))   (defn stop [] (swap! server-atom (fn [server] (when server (.stop server)) nil)))
  • 25. compojure.handler/api An existing minimal stack for APIs (defn api   "Create a handler suitable for a web API. This adds the following middleware to your routes: - wrap-params - wrap-nested-params - wrap-keyword-params"   [routes]   (-> routes       wrap-keyword-params       wrap-nested-params       wrap-params))
  • 26. compojure.handler/site (defn site   "Create a handler suitable for a standard website. This adds the following middleware to your routes: - wrap-session - wrap-flash - wrap-cookies - wrap-multipart-params - wrap-params - wrap-nested-params - wrap-keyword-params ! A map of options may also be provided. These keys are provided: :session - a map of session middleware options :multipart - a map of multipart-params middleware options"   [routes & [opts]]   (-> (api routes)       (with-opts wrap-multipart-params (:multipart opts)) Extends the API stack       (wrap-flash)       (with-opts wrap-session (:session opts))))
  • 27. Use it, or make your own (def handler (-> #'app compojure.handler/site ring-stack))  
  • 28. noir.util.middleware/app-handler (defn app-handler [app-routes & {:keys [session-options store multipart middleware access-rules formats]}]   (letfn [(wrap-middleware-format [handler]             (if formats (wrap-restful-format handler :formats formats) handler))]     (-> (apply routes app-routes) A hook to extend the noir stack         (wrap-middleware middleware)         (wrap-request-map)         (api)         (wrap-base-url)         (wrap-middleware-format)         (with-opts wrap-multipart-params multipart)         (wrap-access-rules access-rules)         (wrap-noir-validation) Lot’s of customization         (wrap-noir-cookies)         (wrap-noir-flash)         (wrap-noir-session          (update-in session-options [:store] #(or % (memory-store mem)))))))
  • 29. Routing Route: (RequestMap → (Option ResponseMap))
  • 30. Not ring handlers because they don’t take a request. (defn home [] (response/response "Home Page"))   (defn foo [] (response/response "Foo Page"))   (defn foo-n [n] (response/response (str "This is Foo#" n)))     (defn app1 [req] (condp re-matches (:uri req) #"/" (home) #"/foo" (foo) #"/foo/(.*)" :>> #(foo-n (second %)) (response/not-found "Wat"))) Select the page to show based on URL
  • 31. Abstract the route dispatch (defn route-to [handler] (fn [match] (if (string? match) (handler) (apply handler (rest match)))))   (defn app2 [req] (condp re-matches (:uri req) #"/" Cleaner, but still awkward :>> (route-to home)   #"/foo" :>> (route-to foo)   #"/foo/(.*)" :>> (route-to foo-n)   (response/not-found "Wat")))
  • 32. Include the pattern (defn my-route [pattern page-fn] (fn [req] (if-let [match (re-matches pattern (:uri req))] ((route-to handler) page-fn))))   (defn app3 [req] (let [my-routes [(my-route #"/" home) Much cleaner (my-route #"/foo" foo) (my-route #"/foo/(.*)" foo-n) (my-route #".*" #(response/not-found "Wat"))]] (some #(% req) my-routes))) The first route that responds wins
  • 33. Routing fn includes method and path (defn app4 [req] (let [my-routes [(GET "/" [] (home)) Some extra macro magic (GET "/foo" [] (foo)) (GET "/foo/:id" [id] (foo-n id)) (route/not-found "Wat")]] (some #(% req) my-routes)))   Some things never change
  • 34. compojure.core/* (defn make-route   "Returns a function that will only call the handler if the method and Clout route match the request."   [method route handler]   (if-method method     (if-route route       (fn [request]         (render (handler request) request))))) ! (defn- compile-route   "Compile a route in the form (method path & body) into a function."   [method route bindings body]   `(make-route     ~method ~(prepare-route route)     (fn [request#]       (let-request [~bindings request#] ~@body)))) ! (defmacro GET "Generate a GET route."   [path args & body]   (compile-route :get path args body))
  • 35. a collection of routes (def app5 (routes (GET "/" [] (home)) (GET "/foo" [] (foo)) (GET "/foo/:id" [id] (foo-n id)) (route/not-found "Wat")))
  • 36. (defn routing   "Apply a list of routes to a Ring request map."   [request & handlers]   (some #(% request) handlers)) ! (defn routes   "Create a Ring handler by combining several handlers into one."   [& handlers]   #(apply routing % handlers))
  • 37. (def foo-routes (routes (GET "/foo" [] (foo)) (GET "/foo/:id" [id] (foo-n id))))   (def app6 (routes (GET "/" [] (home)) Routing functions nest easily foo-routes (route/not-found "Wat")))
  • 38. (defn foobar-routes [foobar-type] (routes (GET "/" [] (str foobar-type " Page")) (GET "/:id" [id] (str foobar-type "#" id))))   (def app7 Not ideal - generates the route fn each call (routes (GET "/" [] (home)) (context "/foo" [] (foobar-routes "Foo")) (context "/bar" [] (foobar-routes "Bar")) (route/not-found "Wat")))
  • 39. (defmacro context   "Give all routes in the form a common path prefix and set of bindings. ! The following example demonstrates defining two routes with a common path prefix ('/user/:id') and a common binding ('id'): ! (context "/user/:id" [id] (GET "/profile" [] ...) (GET "/settings" [] ...))"   [path args & routes]   `(#'if-route ~(context-route path)      (#'wrap-context        (fn [request#]          (let-request [~args request#]            (routing request# ~@routes))))))
  • 40. compojure.response/Renderable (defprotocol Renderable   (render [this request]     "Render the object into a form suitable for the given request map.")) ! (extend-protocol Renderable   nil …   String …   APersistentMap …   IFn …   IDeref …   File …   ISeq …   InputStream …   URL … ) Generate response maps based on types