Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Liberator Free your data with RFC 2616

Liberator Free your data with RFC 2616

My talk on liberator given at clojure/conj 2013.

Philipp Meier

November 16, 2013
Tweet

More Decks by Philipp Meier

Other Decks in Programming

Transcript

  1. Liberator Free your data with RFC 2616 Philipp Meier Software

    Developer http://philipp.meier.name/ @ordnungswprog clojure/conj 2013 Alexandria, VA http://www.fickr.com/photos/aidanmorgan/3352417491/
  2. What is HTTP? • Request and response • Headers and

    body • Methods • Status codes • Content negotiation • Conditional request • And more... • Request and response • Headers and body • Methods • Status codes • Content negotiation • Conditional request • And more...
  3. HTTP Methods 8 RFC 2616 GET, PUT, POST, DELETE, HEAD,

    OPTIONS, TRACE, CONNECT Methods 8 RFC 2616 GET, PUT, POST, DELETE, HEAD, OPTIONS, TRACE, CONNECT
  4. HTTP Methods 8 RFC 2616 GET, PUT, POST, DELETE, HEAD,

    OPTIONS, TRACE, CONNECT Methods 8 RFC 2616 GET, PUT, POST, DELETE, HEAD, OPTIONS, TRACE, CONNECT
  5. HTTP Methods 8+1+7 RFC 2616 PATCH (RFC 5789) WEBDAV (RFC

    4918) Methods 8+1+7 RFC 2616 PATCH (RFC 5789) WEBDAV (RFC 4918)
  6. HTTP Status codes 1xx Informational 2xx Successful 3xx Redirection 4xx

    Client Error 5xx Server error Status codes 1xx Informational 2xx Successful 3xx Redirection 4xx Client Error 5xx Server error
  7. HTTP Status codes 41+1 (RFC 2616) WEBDAV (RFC 5842) Status

    codes 41+1 (RFC 2616) WEBDAV (RFC 5842)
  8. HTTP Status codes 41+1+1 (RFC 2616) WEBDAV (RFC 5842) HTCPCP

    (RFC 2324) Status codes 41+1+1 (RFC 2616) WEBDAV (RFC 5842) HTCPCP (RFC 2324)
  9. HTTP Status codes 41+1+1 (RFC 2616) WEBDAV (RFC 5842) HTCPCP

    (RFC 2324) Status codes 41+1+1 (RFC 2616) WEBDAV (RFC 5842) HTCPCP (RFC 2324) I‘m a Teapot
  10. HTTP Status codes (< 41 26) Maximum # of decisions

    Still 40 branching points Status codes (< 41 26) Maximum # of decisions Still 40 branching points
  11. Ring {:request-method … :headers … :body … …} {:status …

    :headers … :body …} {:request-method … :headers … :body … …} {:status … :headers … :body …}
  12. Ring (→ {:request-method … :headers … :body … …} (ring-handler))

    => {:status … :headers … :body …} (→ {:request-method … :headers … :body … …} (ring-handler)) => {:status … :headers … :body …}
  13. Ring (fn [req] ???) ;; => {:status … ;; :headers

    … ;; :body … } (fn [req] ???) ;; => {:status … ;; :headers … ;; :body … }
  14. Ring (fn [req] (cond … {:status 503} … {:status 501}

    … {:status 403} … {:status 405} …)) (fn [req] (cond … {:status 503} … {:status 501} … {:status 403} … {:status 405} …))
  15. Ring (fn [req] (cond ? {:status 503} … {:status 501}

    … {:status 403} … {:status 405} …)) (fn [req] (cond ? {:status 503} … {:status 501} … {:status 403} … {:status 405} …))
  16. Ring (fn [req] (cond ? {:status 503} ? {:status 501}

    … {:status 403} … {:status 405} …)) (fn [req] (cond ? {:status 503} ? {:status 501} … {:status 403} … {:status 405} …))
  17. Ring (fn [req] (cond ? {:status 503} ? {:status 501}

    ? {:status 403} ? {:status 405} …)) (fn [req] (cond ? {:status 503} ? {:status 501} ? {:status 403} ? {:status 405} …))
  18. Ring (defn handler [request] {:status 200 :headers {"Content-Type" "text/html"} :body

    "a event"}) (defn handler [request] {:status 200 :headers {"Content-Type" "text/html"} :body "a event"})
  19. Ring (defn handler [{m :request-method {id "id"} :params}] (if (=

    :get m) {:status 200 :headers {"Content-Type" "text/plain; charset=UTF-8"} :body (pr-str (find-session id))} {:status 405 :body "Method not allowed."})) (defn handler [{m :request-method {id "id"} :params}] (if (= :get m) {:status 200 :headers {"Content-Type" "text/plain; charset=UTF-8"} :body (pr-str (find-session id))} {:status 405 :body "Method not allowed."}))
  20. Ring (defn handler [{m :request-method {id "id"} :params}] (if (=

    :get m) (if-let [e (find-session id)] {:status 200 :headers {"Content-Type" "text/plain; charset=UTF-8"} :body (pr-str (find-session id))} {:status 404 :body "Not found."}) {:status 405 :body "Method not allowed."})) (defn handler [{m :request-method {id "id"} :params}] (if (= :get m) (if-let [e (find-session id)] {:status 200 :headers {"Content-Type" "text/plain; charset=UTF-8"} :body (pr-str (find-session id))} {:status 404 :body "Not found."}) {:status 405 :body "Method not allowed."}))
  21. Ring (defn handler [{m :request-method {id "id"} :params {accept "accept"}

    :headers}] (if (= :get m) (if-let [e (find-session id)] (if-let [mt (some #(when (or (nil? accept) (= accept "*/*") (.contains accept %)) %) ["application/clojure" "text/html"])] (condp = mt "application/clojure" {:status 200 :headers {"Content-Type" "application/clojure; charset=UTF-8"} :body (pr-str e)} "text/html" {:status 200 :headers {"Content-Type" "text/html; charset=UTF-8"} :body (apply format "<html><h1>%s</h1><p>%s</p><p><small>%s</small></p></html>" ((juxt :title :time :speaker) e)) }) {:status 406 :body "Requested media type not acceptable."}) {:status 404 :body "Not found."}) {:status 505 :body "Method not allowed."})) (defn handler [{m :request-method {id "id"} :params {accept "accept"} :headers}] (if (= :get m) (if-let [e (find-session id)] (if-let [mt (some #(when (or (nil? accept) (= accept "*/*") (.contains accept %)) %) ["application/clojure" "text/html"])] (condp = mt "application/clojure" {:status 200 :headers {"Content-Type" "application/clojure; charset=UTF-8"} :body (pr-str e)} "text/html" {:status 200 :headers {"Content-Type" "text/html; charset=UTF-8"} :body (apply format "<html><h1>%s</h1><p>%s</p><p><small>%s</small></p></html>" ((juxt :title :time :speaker) e)) }) {:status 406 :body "Requested media type not acceptable."}) {:status 404 :body "Not found."}) {:status 505 :body "Method not allowed."}))
  22. Ring (defn handler [{m :request-method {id "id"} :params {accept "accept"

    ims "if-modified-since"} :headers}] (if (#{:get :post :put :delete :head :options :trace} m) (if (= :get m) (if-let [e (find-session id)] (if-let [mt (some #(when (or (nil? accept) (= accept "*/*") (.contains accept %)) %) ["application/clojure" "text/html"])] (let [last-modified (begin-of-last-minute) cache-headers {"Last-Modified" (.format (http-date-format) last-modified) "Vary" "Accept"}] (if (and ims (let [imsd (.parse (http-date-format) ims)] (= last-modified imsd))) {:status 304 :headers cache-headers} (condp = mt "application/clojure" {:status 200 :headers (assoc cache-headers "Content-Type" "application/clojure; charset=UTF-8") :body (pr-str e)} "text/html" {:status 200 :headers (assoc cache-headers "Content-Type" "text/html; charset=UTF-8") :body (apply format "<html><h1>%s</h1><p>%s</p><p><small>%s</small></p></html>" ((juxt :title :time :speaker) e)) }))) {:status 406 :body "Requested media type not acceptable."}) {:status 404 :body "Not found."}) {:status 501 :body "Not implemented."}) {:status 405 :body "Method not allowed."})) (defn handler [{m :request-method {id "id"} :params {accept "accept" ims "if-modified-since"} :headers}] (if (#{:get :post :put :delete :head :options :trace} m) (if (= :get m) (if-let [e (find-session id)] (if-let [mt (some #(when (or (nil? accept) (= accept "*/*") (.contains accept %)) %) ["application/clojure" "text/html"])] (let [last-modified (begin-of-last-minute) cache-headers {"Last-Modified" (.format (http-date-format) last-modified) "Vary" "Accept"}] (if (and ims (let [imsd (.parse (http-date-format) ims)] (= last-modified imsd))) {:status 304 :headers cache-headers} (condp = mt "application/clojure" {:status 200 :headers (assoc cache-headers "Content-Type" "application/clojure; charset=UTF-8") :body (pr-str e)} "text/html" {:status 200 :headers (assoc cache-headers "Content-Type" "text/html; charset=UTF-8") :body (apply format "<html><h1>%s</h1><p>%s</p><p><small>%s</small></p></html>" ((juxt :title :time :speaker) e)) }))) {:status 406 :body "Requested media type not acceptable."}) {:status 404 :body "Not found."}) {:status 501 :body "Not implemented."}) {:status 405 :body "Method not allowed."}))
  23. Ring (defn handler [{m :request-method {id "id"} :params {accept "accept"

    ims "if-modified-since"} :headers}] (if (#{:get :post :put :delete :head :options :trace} m) (if (= :get m) (if-let [e (find-session id)] (if-let [mt (some #(when (or (nil? accept) (= accept "*/*") (.contains accept %)) %) ["application/clojure" "text/html"])] (let [last-modified (begin-of-last-minute) cache-headers {"Last-Modified" (.format (http-date-format) last-modified) "Vary" "Accept"}] (if (and ims (let [imsd (.parse (http-date-format) ims)] (= last-modified imsd))) {:status 304 :headers cache-headers} (condp = mt "application/clojure" {:status 200 :headers (assoc cache-headers "Content-Type" "application/clojure; charset=UTF-8") :body (pr-str e)} "text/html" {:status 200 :headers (assoc cache-headers "Content-Type" "text/html; charset=UTF-8") :body (apply format "<html><h1>%s</h1><p>%s</p><p><small>%s</small></p></html>" ((juxt :title :time :speaker) e)) }))) {:status 406 :body "Requested media type not acceptable."}) {:status 404 :body "Not found."}) {:status 501 :body "Not implemented."}) {:status 405 :body "Method not allowed."})) (defn handler [{m :request-method {id "id"} :params {accept "accept" ims "if-modified-since"} :headers}] (if (#{:get :post :put :delete :head :options :trace} m) (if (= :get m) (if-let [e (find-session id)] (if-let [mt (some #(when (or (nil? accept) (= accept "*/*") (.contains accept %)) %) ["application/clojure" "text/html"])] (let [last-modified (begin-of-last-minute) cache-headers {"Last-Modified" (.format (http-date-format) last-modified) "Vary" "Accept"}] (if (and ims (let [imsd (.parse (http-date-format) ims)] (= last-modified imsd))) {:status 304 :headers cache-headers} (condp = mt "application/clojure" {:status 200 :headers (assoc cache-headers "Content-Type" "application/clojure; charset=UTF-8") :body (pr-str e)} "text/html" {:status 200 :headers (assoc cache-headers "Content-Type" "text/html; charset=UTF-8") :body (apply format "<html><h1>%s</h1><p>%s</p><p><small>%s</small></p></html>" ((juxt :title :time :speaker) e)) }))) {:status 406 :body "Requested media type not acceptable."}) {:status 404 :body "Not found."}) {:status 501 :body "Not implemented."}) {:status 405 :body "Method not allowed."}))
  24. Ring (defn handler [{m :request-method {id "id"} :params {accept "accept"

    ims "if-modified-since"} :headers}] (if (#{:get :post :put :delete :head :options :trace} m) (if (= :get m) (if-let [e (find-session id)] (if-let [mt (some #(when (or (nil? accept) (= accept "*/*") (.contains accept %)) %) ["application/clojure" "text/html"])] (let [last-modified (begin-of-last-minute) cache-headers {"Last-Modified" (.format (http-date-format) last-modified) "Vary" "Accept"}] (if (and ims (let [imsd (.parse (http-date-format) ims)] (= last-modified imsd))) {:status 304 :headers cache-headers} (condp = mt "application/clojure" {:status 200 :headers (assoc cache-headers "Content-Type" "application/clojure; charset=UTF-8") :body (pr-str e)} "text/html" {:status 200 :headers (assoc cache-headers "Content-Type" "text/html; charset=UTF-8") :body (apply format "<html><h1>%s</h1><p>%s</p><p><small>%s</small></p></html>" ((juxt :title :time :speaker) e)) }))) {:status 406 :body "Requested media type not acceptable."}) {:status 404 :body "Not found."}) {:status 501 :body "Not implemented."}) {:status 405 :body "Method not allowed."})) (defn handler [{m :request-method {id "id"} :params {accept "accept" ims "if-modified-since"} :headers}] (if (#{:get :post :put :delete :head :options :trace} m) (if (= :get m) (if-let [e (find-session id)] (if-let [mt (some #(when (or (nil? accept) (= accept "*/*") (.contains accept %)) %) ["application/clojure" "text/html"])] (let [last-modified (begin-of-last-minute) cache-headers {"Last-Modified" (.format (http-date-format) last-modified) "Vary" "Accept"}] (if (and ims (let [imsd (.parse (http-date-format) ims)] (= last-modified imsd))) {:status 304 :headers cache-headers} (condp = mt "application/clojure" {:status 200 :headers (assoc cache-headers "Content-Type" "application/clojure; charset=UTF-8") :body (pr-str e)} "text/html" {:status 200 :headers (assoc cache-headers "Content-Type" "text/html; charset=UTF-8") :body (apply format "<html><h1>%s</h1><p>%s</p><p><small>%s</small></p></html>" ((juxt :title :time :speaker) e)) }))) {:status 406 :body "Requested media type not acceptable."}) {:status 404 :body "Not found."}) {:status 501 :body "Not implemented."}) {:status 405 :body "Method not allowed."}))
  25. Liberator (defresource event :available-media-types ["text/html" "application/json" "application/edn"] :exists? (fn [{{{id

    "id"} :params} :request}] (if-let [talk (find-talk id)] {::talk talk})) :handle-ok ::talk :last-modified (fn [_] (begin-of-last-minute)) :etag (fn [{{{id "id"} :params} :request}] (apply str (reverse (str id id))))) (defresource event :available-media-types ["text/html" "application/json" "application/edn"] :exists? (fn [{{{id "id"} :params} :request}] (if-let [talk (find-talk id)] {::talk talk})) :handle-ok ::talk :last-modified (fn [_] (begin-of-last-minute)) :etag (fn [{{{id "id"} :params} :request}] (apply str (reverse (str id id)))))
  26. Remember? (defn handler [{m :request-method {id "id"} :params {accept "accept"

    ims "if-modified-since"} :headers}] (if (#{:get :post :put :delete :head :options :trace} m) (if (= :get m) (if-let [e (find-session id)] (if-let [mt (some #(when (or (nil? accept) (= accept "*/*") (.contains accept %)) %) ["application/clojure" "text/html"])] (let [last-modified (begin-of-last-minute) cache-headers {"Last-Modified" (.format (http-date-format) last-modified) "Vary" "Accept"}] (if (and ims (let [imsd (.parse (http-date-format) ims)] (= last-modified imsd))) {:status 304 :headers cache-headers} (condp = mt "application/clojure" {:status 200 :headers (assoc cache-headers "Content-Type" "application/clojure; charset=UTF-8") :body (pr-str e)} "text/html" {:status 200 :headers (assoc cache-headers "Content-Type" "text/html; charset=UTF-8") :body (apply format "<html><h1>%s</h1><p>%s</p><p><small>%s</small></p></html>" ((juxt :title :time :speaker) e)) }))) {:status 406 :body "Requested media type not acceptable."}) {:status 404 :body "Not found."}) {:status 501 :body "Not implemented."}) {:status 405 :body "Method not allowed."})) (defn handler [{m :request-method {id "id"} :params {accept "accept" ims "if-modified-since"} :headers}] (if (#{:get :post :put :delete :head :options :trace} m) (if (= :get m) (if-let [e (find-session id)] (if-let [mt (some #(when (or (nil? accept) (= accept "*/*") (.contains accept %)) %) ["application/clojure" "text/html"])] (let [last-modified (begin-of-last-minute) cache-headers {"Last-Modified" (.format (http-date-format) last-modified) "Vary" "Accept"}] (if (and ims (let [imsd (.parse (http-date-format) ims)] (= last-modified imsd))) {:status 304 :headers cache-headers} (condp = mt "application/clojure" {:status 200 :headers (assoc cache-headers "Content-Type" "application/clojure; charset=UTF-8") :body (pr-str e)} "text/html" {:status 200 :headers (assoc cache-headers "Content-Type" "text/html; charset=UTF-8") :body (apply format "<html><h1>%s</h1><p>%s</p><p><small>%s</small></p></html>" ((juxt :title :time :speaker) e)) }))) {:status 406 :body "Requested media type not acceptable."}) {:status 404 :body "Not found."}) {:status 501 :body "Not implemented."}) {:status 405 :body "Method not allowed."}))
  27. Liberator (defresource event :available-media-types ["text/html" "application/json" "application/edn"] :exists? (fn [{{{id

    "id"} :params} :request}] (if-let [talk (find-talk id)] {::talk talk})) :handle-ok ::talk :last-modified (fn [_] (begin-of-last-minute)) :etag (fn [{{{id "id"} :params} :request}] (apply str (reverse (str id id))))) (defresource event :available-media-types ["text/html" "application/json" "application/edn"] :exists? (fn [{{{id "id"} :params} :request}] (if-let [talk (find-talk id)] {::talk talk})) :handle-ok ::talk :last-modified (fn [_] (begin-of-last-minute)) :etag (fn [{{{id "id"} :params} :request}] (apply str (reverse (str id id)))))
  28. Liberator (defresource event :available-media-types (fn [ctx] …) :exists? (fn [ctx]

    …) :handle-ok (fn [ctx] …) :last-modified (fn [ctx] …) :etag (fn [ctx] …)) (defresource event :available-media-types (fn [ctx] …) :exists? (fn [ctx] …) :handle-ok (fn [ctx] …) :last-modified (fn [ctx] …) :etag (fn [ctx] …))
  29. Liberator (defresource event :available-media-types (fn [ctx] …) :exists? (fn [ctx]

    …) :handle-ok (fn [ctx] …) :last-modified (fn [ctx] …) :etag (fn [ctx] …)) (defresource event :available-media-types (fn [ctx] …) :exists? (fn [ctx] …) :handle-ok (fn [ctx] …) :last-modified (fn [ctx] …) :etag (fn [ctx] …))
  30. Liberator (defresource event :available-media-types (fn [ctx] …) :exists? (fn [ctx]

    …) :handle-ok (fn [ctx] …) :last-modified (fn [ctx] …) :etag (fn [ctx] …)) (defresource event :available-media-types (fn [ctx] …) :exists? (fn [ctx] …) :handle-ok (fn [ctx] …) :last-modified (fn [ctx] …) :etag (fn [ctx] …)) Essential Complexity Essential Complexity
  31. Liberator (defresource event :available-media-types ["text/html" "application/json" "application/edn"] :exists? (fn [{{{id

    "id"} :params} :request}] (if-let [talk (find-talk id)] {::talk talk})) :handle-ok ::talk :last-modified (fn [_] (begin-of-last-minute)) :etag (fn [{{{id "id"} :params} :request}] (apply str (reverse (str id id))))) (defresource event :available-media-types ["text/html" "application/json" "application/edn"] :exists? (fn [{{{id "id"} :params} :request}] (if-let [talk (find-talk id)] {::talk talk})) :handle-ok ::talk :last-modified (fn [_] (begin-of-last-minute)) :etag (fn [{{{id "id"} :params} :request}] (apply str (reverse (str id id)))))
  32. Context (defresource event :available-media-types (fn [ctx] …) :exists? (fn [ctx]

    …) :handle-ok (fn [ctx] …) :last-modified (fn [ctx] …) :etag (fn [ctx] …)) (defresource event :available-media-types (fn [ctx] …) :exists? (fn [ctx] …) :handle-ok (fn [ctx] …) :last-modified (fn [ctx] …) :etag (fn [ctx] …))
  33. Context {:request {:request-method :get :params …}} ::talk {:speaker „Philipp Meier“…}}

    {:request {:request-method :get :params …}} ::talk {:speaker „Philipp Meier“…}}
  34. Context {:request {:request-method :get :params …}} ::talk {:speaker „Philipp Meier“…}

    :representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}} {:request {:request-method :get :params …}} ::talk {:speaker „Philipp Meier“…} :representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}}
  35. Context {:request {:request-method :get :params …}} ::talk {:speaker „Philipp Meier“…}

    :representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}} {:request {:request-method :get :params …}} ::talk {:speaker „Philipp Meier“…} :representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}}
  36. Representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} {:media-type

    „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}
  37. Representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} {:media-type

    „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}
  38. Representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} {:media-type

    „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}
  39. Representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} {:media-type

    „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}
  40. Representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} {:media-type

    „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}
  41. Representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} {:media-type

    „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}
  42. Representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} (defprotocol

    Representation (as-response [x ctx]) {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} (defprotocol Representation (as-response [x ctx])
  43. Representation (defprotocol Representation (as-response [x ctx]) String File InputStream map

    seq (defprotocol Representation (as-response [x ctx]) String File InputStream map seq
  44. Representation (defprotocol Representation (as-response [x ctx]) String File {:body x}

    InputStream map seq (defprotocol Representation (as-response [x ctx]) String File {:body x} InputStream map seq
  45. Representation (defprotocol Representation (as-response [x ctx]) String File InputStream map

    seq (defprotocol Representation (as-response [x ctx]) String File InputStream map seq
  46. Representation map seq edn json text/plain (html) (text/csv) ... map

    seq edn json text/plain (html) (text/csv) ...
  47. Representation (defmethod render-map-generic "foo/bar" [foo _] (str „my foo is

    „ (:color foo)) (defmethod render-map-generic "foo/bar" [foo _] (str „my foo is „ (:color foo))
  48. Representation (defresource custom-representation :available-media-types [„application/json“ „foo/bar“] :exists? (fn [_] {::foo

    {:color :green}}) :handle-ok ::foo) (defresource custom-representation :available-media-types [„application/json“ „foo/bar“] :exists? (fn [_] {::foo {:color :green}}) :handle-ok ::foo)
  49. Representation Escape Hatch Fall back to plain ring response (defresource

    custom-representation :available-media-types [„foo/bar“] :exists? (fn [_] {::foo {:color :green}}) :handle-ok (fn [{foo ::foo}] (ring-response (str „foo is (:color foo))))) Escape Hatch Fall back to plain ring response (defresource custom-representation :available-media-types [„foo/bar“] :exists? (fn [_] {::foo {:color :green}}) :handle-ok (fn [{foo ::foo}] (ring-response (str „foo is (:color foo)))))
  50. Representation cheshire data.fressian clj-ical image/png enlive, hiccup, selmer … cheshire

    data.fressian clj-ical image/png enlive, hiccup, selmer …
  51. POST :exists? :existed? :respond-with-entity? :new? :post-redirect? :can-post-to-missing? :processable? :post! :handle-ok

    :handle-created :exists? :existed? :respond-with-entity? :new? :post-redirect? :can-post-to-missing? :processable? :post! :handle-ok :handle-created
  52. POST :exists? :not-found :existed? :gone :respond-with-entity? :no-content :new? :created :post-redirect?

    :see-other :can-post-to-missing? :not-found :processable? 422 :post! side-effect! :handle-ok :handle-created :exists? :not-found :existed? :gone :respond-with-entity? :no-content :new? :created :post-redirect? :see-other :can-post-to-missing? :not-found :processable? 422 :post! side-effect! :handle-ok :handle-created
  53. POST :exists? :not-found :existed? :gone :respond-with-entity? :no-content :new? :created :post-redirect?

    :see-other :can-post-to-missing? :not-found :processable? 422 :post! side-effect! :handle-ok :handle-created ... :exists? :not-found :existed? :gone :respond-with-entity? :no-content :new? :created :post-redirect? :see-other :can-post-to-missing? :not-found :processable? 422 :post! side-effect! :handle-ok :handle-created ...
  54. PUT :exists? :not-found :existed? :gone :respond-with-entity? :no-content :new? :created :can-put-to-missing?

    :not-found :conflict? :conflict :processable? 422 :put! side-effect! :handle-ok :handle-created ... :exists? :not-found :existed? :gone :respond-with-entity? :no-content :new? :created :can-put-to-missing? :not-found :conflict? :conflict :processable? 422 :put! side-effect! :handle-ok :handle-created ...
  55. Integration It’s a ring handler Ring routing libraries compojure, clout

    It’s a ring handler Ring routing libraries compojure, clout
  56. Integration It’s a ring handler Ring routing libraries compojure, clout

    Middleware It’s a ring handler Ring routing libraries compojure, clout Middleware
  57. Integration (defresource foo… ) (defresource bar… ) (→ (routes (ANY

    „/foo“ [] foo) (ANY „/bar/:id“ [id] (bar id)) wrap-dosync wrap-keyword-params wrap-params (wrap-trace :ui) wrap-stacktrace) (defresource foo… ) (defresource bar… ) (→ (routes (ANY „/foo“ [] foo) (ANY „/bar/:id“ [id] (bar id)) wrap-dosync wrap-keyword-params wrap-params (wrap-trace :ui) wrap-stacktrace)
  58. Future Patch Request body processing Custom decision fows Whatever Patch

    Request body processing Custom decision fows Whatever
  59. Liberator Free your data with RFC 2616 Philipp Meier Software

    Developer http://philipp.meier.name/ @ordnungswprog clojure/conj 2013 Alexandria, VA http://www.fickr.com/photos/aidanmorgan/3352417491/