Permalink
Browse files

Added nested-params middleware

  • Loading branch information...
1 parent 451c591 commit 8a7620c884b2719355a0db1870054b742dcfd06d @weavejester weavejester committed Sep 25, 2010
View
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))))
View
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

Please sign in to comment.