Last active
August 29, 2015 14:16
-
-
Save jmorton/4c20ce774af51d5bb098 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns accept.core) | |
;; Content Type matters in a REST API. | |
(defrecord Accept [media-range quality accept-extension]) | |
(defn accept [str] | |
"Parse a single accept string into a map" | |
; according to RFC2616, the "q" parameter must precede the accept-extension | |
(let [pattern #"([^;]+)\s*(?:;q=([0-9+\.]+))?\s*(;.+)*" | |
matches (re-find pattern str) | |
[_ media-range qvalue accept-extension] matches | |
quality (java.lang.Double/parseDouble (or qvalue "1"))] | |
(Accept. media-range quality accept-extension))) | |
(defn accept-list [str] | |
"Build a sorted list of parsed accept values" | |
(sort-by :quality > (map accept (clojure.string/split str #",\s*")))) | |
(defrecord Address [street city state]) | |
(def here (Address. "710 S. Euclid" "Sioux Falls" "South Dakota")) | |
(defrecord Person [name age address]) | |
(def me (Person. "Jon" 35 here)) | |
(def you (Person. "Tom" 37 here)) | |
(def wanting-a (accept-list "text/html")) | |
(def wanting-b (accept-list "application/json, text/html;q=0.9")) | |
(def wanting-c (accept-list "text/*, application/xml;q=0.9")) | |
;; Issues with this approach: | |
;; 1. No notion of object type... | |
;; 2. Doesn't set response header, no access to response | |
;; 3. Explicit wild car mapping. | |
;; 4. Maybe render takes request, response, object. Now the library is | |
;; coupled to ring like applications? So, move that render to a | |
;; ring specific namespace. | |
(defmulti render | |
(fn [obj accept] ; accept might be request... | |
[(:media-range accept) (type obj)])) | |
(defmethod render ["text/html" Person] [obj _] | |
(str "Behold, a person, I render " (:name obj) " as HTML")) | |
(defmethod render ["text/plain" Person] [obj _] | |
(str "Behold, a person, I render " (:name obj) " as plain text")) | |
(defmethod render ["application/json" Person] [obj _] | |
(str "{'name':'" (:name obj) "'}")) | |
(defmethod render ["application/xml" Person] [obj _] | |
(str "<person>" (:name obj) "</person>")) | |
(defmethod render ["text/*" Person] [obj accept] | |
(render obj { :type "text" :subtype "html" })) | |
(defmethod render ["*/*" Person] [obj accept] | |
(render obj { :type "text" :subtype "html" })) | |
(render me {:media-range "text/foo"}) | |
(render here {:media-range "text/html"}) | |
;(render you {:type "text" :subtype "plain"}) | |
;(render me {:type "*" :subtype "*"}) | |
;(def as [{:type :image :subtype :*} {:type :text :subtype :*}]) | |
;(render me as) |
Reverse defmethod pattern to be obj-type then content-type
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
It'd be nice to have a way to get a list of the content types that the Person class... "supports" ...