Permalink
1ce02b7 Oct 29, 2011
@weavejester @pyr
66 lines (57 sloc) 1.87 KB
(ns ring.middleware.nested-params
"Convert a single-depth map of parameters to a nested map.")
(defn parse-nested-keys
"Parse a parameter name into a list of keys using a 'C'-like index
notation. e.g.
\"foo[bar][][baz]\"
=> [\"foo\" \"bar\" \"\" \"baz\"]"
[param-name]
(let [[_ k ks] (re-matches #"(.*?)((?:\[.*?\])*)" (name param-name))
keys (if ks (map second (re-seq #"\[(.*?)\]" ks)))]
(cons k keys)))
(defn- assoc-nested
"Similar to assoc-in, but treats values of blank keys as elements in a
list."
[m [k & ks] v]
(conj m
(if k
(if-let [[j & js] ks]
(if (= j "")
{k (assoc-nested (get m k []) js v)}
{k (assoc-nested (get m k {}) ks v)})
{k v})
v)))
(defn- param-pairs
"Return a list of name-value pairs for a parameter map."
[params]
(mapcat
(fn [[name value]]
(if (sequential? value)
(for [v value] [name v])
[[name value]]))
params))
(defn- nest-params
"Takes a flat map of parameters and turns it into a nested map of
parameters, using the function parse to split the parameter names
into keys."
[params parse]
(reduce
(fn [m [k v]]
(assoc-nested m (parse k) v))
{}
(param-pairs params)))
(defn wrap-nested-params
"Middleware to converts a flat map of parameters into a nested map.
Uses the function in the :key-parser option to convert parameter names
to a list of keys. Values in keys that are empty strings are treated
as elements in a list. Defaults to using the parse-nested-keys function.
e.g.
{\"foo[bar]\" \"baz\"}
=> {\"foo\" {\"bar\" \"baz\"}}
{\"foo[]\" \"bar\"}
=> {\"foo\" [\"bar\"]}"
[handler & [opts]]
(fn [request]
(let [parse (:key-parser opts parse-nested-keys)
request (update-in request [:params] nest-params parse)]
(handler request))))