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
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
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
Claude Codeと2つの巻き戻し戦略 / Two Rewind Strategies with Claude Code
fruitriin
0
140
Raku Raku Notion 20260128
hareyakayuruyaka
0
360
ぼくの開発環境2026
yuzneri
0
240
高速開発のためのコード整理術
sutetotanuki
1
410
AI & Enginnering
codelynx
0
120
CSC307 Lecture 03
javiergs
PRO
1
490
並行開発のためのコードレビュー
miyukiw
0
1.2k
izumin5210のプロポーザルのネタ探し #tskaigi_msup
izumin5210
1
140
15年続くIoTサービスのSREエンジニアが挑む分散トレーシング導入
melonps
2
230
AgentCoreとHuman in the Loop
har1101
5
250
CSC307 Lecture 01
javiergs
PRO
0
690
生成AIを使ったコードレビューで定性的に品質カバー
chiilog
1
280
Featured
See All Featured
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
117
110k
Rebuilding a faster, lazier Slack
samanthasiow
85
9.4k
Public Speaking Without Barfing On Your Shoes - THAT 2023
reverentgeek
1
310
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.1k
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
120
Evolving SEO for Evolving Search Engines
ryanjones
0
130
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
0
68
Fashionably flexible responsive web design (full day workshop)
malarkey
408
66k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.4k
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
1
300
The Anti-SEO Checklist Checklist. Pubcon Cyber Week
ryanjones
0
70
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1k
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