Permalink
Please sign in to comment.
Showing
with
82 additions
and 0 deletions.
64
ring-core/src/ring/middleware/nested_params.clj
| @@ -0,0 +1,64 @@ | ||
| +(ns ring.middleware.nested-params) | ||
| + | ||
| +(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 #"(.*?)((?:\[.*?\])*)" 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)))) |
18
ring-core/test/ring/middleware/nested_params_test.clj
| @@ -0,0 +1,18 @@ | ||
| +(ns ring.middleware.nested-params-test | ||
| + (:use clojure.test | ||
| + ring.middleware.nested-params)) | ||
| + | ||
| +(deftest nested-params-test | ||
| + (let [handler (wrap-nested-params :params)] | ||
| + (testing "nested parameter maps" | ||
| + (are [p r] (= (handler {:params p}) r) | ||
| + {"foo" "bar"} {"foo" "bar"} | ||
| + {"x[y]" "z"} {"x" {"y" "z"}} | ||
| + {"a[b][c]" "d"} {"a" {"b" {"c" "d"}}} | ||
| + {"a" "b", "c" "d"} {"a" "b", "c" "d"})) | ||
| + (testing "nested parameter lists" | ||
| + (are [p r] (= (handler {:params p}) r) | ||
| + {"foo[]" "bar"} {"foo" ["bar"]} | ||
| + {"foo[]" ["bar" "baz"]} {"foo" ["bar" "baz"]} | ||
| + {"a[x][]" ["b"], "a[x][][y]" "c"} {"a" {"x" ["b" {"y" "c"}]}} | ||
| + {"a[][x]" "c", "a[][y]" "d"} {"a" [{"x" "c"} {"y" "d"}]})))) |
0 comments on commit
8a7620c