Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Liberator Free your data with RFC 2616
Search
Philipp Meier
November 16, 2013
Programming
0
420
Liberator Free your data with RFC 2616
My talk on liberator given at clojure/conj 2013.
Philipp Meier
November 16, 2013
Tweet
Share
More Decks by Philipp Meier
See All by Philipp Meier
Creating Web Services with Liberator - f(by) 2016
ordnungswidrig
0
92
Liberator Free your data with RFC 2616
ordnungswidrig
0
190
Other Decks in Programming
See All in Programming
CSC307 Lecture 02
javiergs
PRO
1
780
Vibe Coding - AI 驅動的軟體開發
mickyp100
0
180
コントリビューターによるDenoのすゝめ / Deno Recommendations by a Contributor
petamoriken
0
210
なぜSQLはAIぽく見えるのか/why does SQL look AI like
florets1
0
480
余白を設計しフロントエンド開発を 加速させる
tsukuha
7
2.1k
CSC307 Lecture 05
javiergs
PRO
0
500
AI & Enginnering
codelynx
0
120
生成AIを活用したソフトウェア開発ライフサイクル変革の現在値
hiroyukimori
PRO
0
100
16年目のピクシブ百科事典を支える最新の技術基盤 / The Modern Tech Stack Powering Pixiv Encyclopedia in its 16th Year
ahuglajbclajep
5
1k
AIで開発はどれくらい加速したのか?AIエージェントによるコード生成を、現場の評価と研究開発の評価の両面からdeep diveしてみる
daisuketakeda
1
2.5k
Raku Raku Notion 20260128
hareyakayuruyaka
0
360
AIと一緒にレガシーに向き合ってみた
nyafunta9858
0
250
Featured
See All Featured
Faster Mobile Websites
deanohume
310
31k
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
58
50k
How to build an LLM SEO readiness audit: a practical framework
nmsamuel
1
650
Introduction to Domain-Driven Design and Collaborative software design
baasie
1
590
A designer walks into a library…
pauljervisheath
210
24k
Music & Morning Musume
bryan
47
7.1k
The Cost Of JavaScript in 2023
addyosmani
55
9.5k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.1k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
3.9k
The Anti-SEO Checklist Checklist. Pubcon Cyber Week
ryanjones
0
70
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.7k
Deep Space Network (abreviated)
tonyrice
0
64
Transcript
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/
None
Liberator A clojure library to implement REST resources
Liberator A clojure library to implement REST resources ...on the
server side.
What is HTTP?
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...
HTTP Methods Methods
HTTP Methods 8 Methods 8
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
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
HTTP Methods 8+1 RFC 2616 PATCH (RFC 5789) Methods 8+1
RFC 2616 PATCH (RFC 5789)
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)
HTTP Header Fields Header Fields
HTTP Header Fields 47 Header Fields 47
HTTP Header Fields 47 Accept … WWW-Authenticate Header Fields 47
Accept … WWW-Authenticate
HTTP Status codes Status codes
HTTP Status codes 5 Classes Status codes 5 Classes
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
HTTP Status codes Status codes
HTTP Status codes 41 Status codes 41
HTTP Status codes 41 (RFC 2616) Status codes 41 (RFC
2616)
HTTP Status codes 41+1 (RFC 2616) WEBDAV (RFC 5842) Status
codes 41+1 (RFC 2616) WEBDAV (RFC 5842)
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)
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
HTTP Status codes 41 Status codes 41
HTTP Status codes (< 41 26) Maximum # of decisions
Still 40 branching points Status codes (< 41 26) Maximum # of decisions Still 40 branching points
Ring {:request-method … :headers … :body … …} {:status …
:headers … :body …} {:request-method … :headers … :body … …} {:status … :headers … :body …}
Ring (→ {:request-method … :headers … :body … …} (ring-handler))
=> {:status … :headers … :body …} (→ {:request-method … :headers … :body … …} (ring-handler)) => {:status … :headers … :body …}
Ring (fn [req] ???) ;; => {:status … ;; :headers
… ;; :body … } (fn [req] ???) ;; => {:status … ;; :headers … ;; :body … }
Ring (fn [req] (cond … {:status 503} … {:status 501}
… {:status 403} … {:status 405} …)) (fn [req] (cond … {:status 503} … {:status 501} … {:status 403} … {:status 405} …))
Ring (fn [req] (cond ? {:status 503} … {:status 501}
… {:status 403} … {:status 405} …)) (fn [req] (cond ? {:status 503} … {:status 501} … {:status 403} … {:status 405} …))
Ring (fn [req] (cond ? {:status 503} ? {:status 501}
… {:status 403} … {:status 405} …)) (fn [req] (cond ? {:status 503} ? {:status 501} … {:status 403} … {:status 405} …))
Ring (fn [req] (cond ? {:status 503} ? {:status 501}
? {:status 403} ? {:status 405} …)) (fn [req] (cond ? {:status 503} ? {:status 501} ? {:status 403} ? {:status 405} …))
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"})
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."}))
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."}))
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."}))
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."}))
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."}))
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."}))
Liberate all the data!
Liberator is extracted decision fow
Liberator is extracted decision fow
Liberator is extracted decision fow
Demo
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)))))
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."}))
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)))))
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] …))
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] …))
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
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)))))
Liberator’s execution model
Context
Context State of the computation State of the computation
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] …))
Context {:request {:request-method :get :params …}}} {:request {:request-method :get :params
…}}}
Context {:request {:request-method :get :params …}} ::talk {:speaker „Philipp Meier“…}}
{:request {:request-method :get :params …}} ::talk {:speaker „Philipp Meier“…}}
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“}}
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“}}
Representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} {:media-type
„application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}
Representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} {:media-type
„application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}
Representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} {:media-type
„application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}
Representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} {:media-type
„application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}
Representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} {:media-type
„application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}
Representation {:media-type „application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“} {:media-type
„application/edn“ :language „de“ :charset „UTF-8“ :encoding „deflate“}
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])
Representation (defprotocol Representation (as-response [x ctx]) String File InputStream map
seq (defprotocol Representation (as-response [x ctx]) String File InputStream map seq
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
Representation (defprotocol Representation (as-response [x ctx]) String File InputStream map
seq (defprotocol Representation (as-response [x ctx]) String File InputStream map seq
Representation map seq edn json text/plain (html) (text/csv) ... map
seq edn json text/plain (html) (text/csv) ...
Representation Do your own implementation Do your own implementation
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))
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)
More methods GET POST PUT DELETE
Representation Escape Hatch Escape Hatch
Representation Escape Hatch Fall back to plain ring response Escape
Hatch Fall back to plain ring response
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)))))
Representation Escape Hatch Extend multimethod as-response Escape Hatch Extend multimethod
as-response
Representation cheshire data.fressian clj-ical image/png enlive, hiccup, selmer … cheshire
data.fressian clj-ical image/png enlive, hiccup, selmer …
More methods
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
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
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 ...
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 ...
Integration
Integration It’s a ring handler It’s a ring handler
Integration It’s a ring handler Ring routing libraries compojure, clout
It’s a ring handler Ring routing libraries compojure, clout
Integration It’s a ring handler Ring routing libraries compojure, clout
Middleware It’s a ring handler Ring routing libraries compojure, clout Middleware
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)
Future Patch Request body processing Custom decision fows Whatever Patch
Request body processing Custom decision fows Whatever
Questions?
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/
http://clojure-liberator.github.io/liberator https://github.com/ordnungswidrig/liberator-presentation http://clojure-liberator.github.io/liberator https://github.com/ordnungswidrig/liberator-presentation Thank you Philipp Meier Software Developer
http://philipp.meier.name/ @ordnungswprog clojure/conj 2013 Alexandria, VA