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

From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxKatpro Technologies
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
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
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
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
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessPixlogix Infotech
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024Results
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
 

Kürzlich hochgeladen (20)

From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
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...
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
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...
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your Business
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 

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