Skip to content

Instantly share code, notes, and snippets.

@jmorton
Last active June 30, 2016 07:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jmorton/c9608f634cbd7b8a1146d9d8e6392fc5 to your computer and use it in GitHub Desktop.
Save jmorton/c9608f634cbd7b8a1146d9d8e6392fc5 to your computer and use it in GitHub Desktop.
(defn parse-quality
"Convert the first 'q' parameter to a number (or 1.0)"
[extension]
(let [[_ qvalue] (re-find #";q=([0-9\.]+)" (or extension ""))]
(java.lang.Double. (or qvalue "1.0"))))
(defn +media-range
""
[accept]
(->> (re-find #"(([\w\*]+)/([^;]+))(;.*)?" accept)
(zipmap [:original :media-range :type :subtype :parameters])))
(defn +quality
"Takes first 'q' parameter with a "
[accept]
(->> (if-let [[_ q] (re-find #"q=([10]?\.?[0-9]+)" (:original accept))]
(java.lang.Double. q)
1.0)
(assoc accept :quality)))
(defn +extensions
"Convert a single extensions string into a list of strings."
[accept]
(if-let [params (:parameters accept)]
(->> (clojure.string/replace-first params #"q=([10]?\.?[0-9]+)" "")
(re-seq #"[\w\.]+")
vec
(assoc accept :extensions))
accept))
(defn +version
"Non-standard."
[accept]
(->> (:subtype accept)
(re-find #"v([0-9\.]+)")
last
(assoc accept :version)))
(defn +format
"Non-standard."
[accept]
(->> (:subtype accept)
(re-find #"\+([\w\.]+)")
last
(assoc accept :format)))
(defn +specificity
"Used when sorting indidual accept values"
[accept]
(assoc accept :specificity
(cond->> 0
(accept :extensions) inc
(not= (accept :subtype) "*") inc
(not= (accept :type) "*") inc)))
(def accept-xf (comp (map +media-range)
(map +quality)
(map +extensions)
(map +version)
(map +format)
(map +specificity)))
(defn accept-builder
""
[accept]
(if accept
(->> (clojure.string/split accept #"[,\s]+")
(sequence accept-xf)
vec
(sort-by (juxt :quality :specificity))
reverse)))
(defn accept-handler
"A Ring handler that parses the Accept header and updates request
with map ."
[handler]
(fn [request]
(if-let [header (get-in request [:headers "accept"])]
(handler (assoc request :accept (accept-builder header))
(handler request)))))
@jmorton
Copy link
Author

jmorton commented Jun 30, 2016

An Accept header like this...

(accept-builder "text/vndr.moo.v0.5+html;level=1;q=0.9, text/vndr.moo.v0.5+json")

...produces a sequence of maps like this...

({:media-range "text/vndr.moo.v0.5+html",
  :format "html",
  :original "text/vndr.moo.v0.5+html;level=1;q=1.0",
  :type "text",
  :extensions ["level" "1"],
  :quality 1.0,
  :version "0.5",
  :specificity 3,
  :parameters ";level=1;q=1.0",
  :subtype "vndr.moo.v0.5+html"}
 {:original "text/vndr.moo.v0.5+json",
  :media-range "text/vndr.moo.v0.5+json",
  :type "text",
  :subtype "vndr.moo.v0.5+json",
  :parameters nil,
  :quality 1.0,
  :version "0.5",
  :format "json",
  :specificity 2})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment