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
91
Liberator Free your data with RFC 2616
ordnungswidrig
0
190
Other Decks in Programming
See All in Programming
第9回 情シス転職ミートアップ 株式会社IVRy(アイブリー)の紹介
ivry_presentationmaterials
1
320
ソフトウェア品質を数字で捉える技術。事業成長を支えるシステム品質の マネジメント
takuya542
1
13k
Deep Dive into ~/.claude/projects
hiragram
14
2.5k
PHPでWebSocketサーバーを実装しよう2025
kubotak
0
280
20250628_非エンジニアがバイブコーディングしてみた
ponponmikankan
0
680
今ならAmazon ECSのサービス間通信をどう選ぶか / Selection of ECS Interservice Communication 2025
tkikuc
21
4k
「Cursor/Devin全社導入の理想と現実」のその後
saitoryc
0
820
Hypervel - A Coroutine Framework for Laravel Artisans
albertcht
1
120
Goで作る、開発・CI環境
sin392
0
230
すべてのコンテキストを、 ユーザー価値に変える
applism118
3
1.3k
ペアプロ × 生成AI 現場での実践と課題について / generative-ai-in-pair-programming
codmoninc
1
18k
ruby.wasmで多人数リアルタイム通信ゲームを作ろう
lnit
3
470
Featured
See All Featured
Making Projects Easy
brettharned
116
6.3k
Producing Creativity
orderedlist
PRO
346
40k
Become a Pro
speakerdeck
PRO
29
5.4k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
667
120k
Building Adaptive Systems
keathley
43
2.7k
Imperfection Machines: The Place of Print at Facebook
scottboms
267
13k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.4k
Rails Girls Zürich Keynote
gr2m
95
14k
GitHub's CSS Performance
jonrohan
1031
460k
Designing Experiences People Love
moore
142
24k
Into the Great Unknown - MozCon
thekraken
40
1.9k
Keith and Marios Guide to Fast Websites
keithpitt
411
22k
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