/
nested_params.clj
91 lines (78 loc) · 2.6 KB
/
nested_params.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
(ns ring.middleware.nested-params
"Middleware to convert a single-depth map of parameters to a nested map."
(:require [ring.util.codec :refer [assoc-conj]]))
(defn parse-nested-keys
"Parse a parameter name into a list of keys using a 'C'-like index
notation.
For example:
\"foo[bar][][baz]\"
=> [\"foo\" \"bar\" \"\" \"baz\"]"
[param-name]
(let [[_ k ks] (re-matches #"(?s)(.*?)((?:\[.*?\])*)" (name param-name))
keys (if ks (map second (re-seq #"\[(.*?)\]" ks)))]
(cons k keys)))
(defn- assoc-vec [m k v]
(let [m (if (contains? m k) m (assoc m k []))]
(assoc-conj m k v)))
(defn- assoc-nested
"Similar to assoc-in, but treats values of blank keys as elements in a
list."
[m [k & ks] v]
(if k
(if ks
(let [[j & js] ks]
(if (= j "")
(assoc-vec m k (assoc-nested {} js v))
(assoc m k (assoc-nested (get m k {}) ks v))))
(if (map? m)
(assoc-conj m k v)
{k v}))
v))
(defn- param-pairs
"Return a list of name-value pairs for a parameter map."
[params]
(mapcat
(fn [[name value]]
(if (and (sequential? value) (not (coll? (first 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 nested-params-request
"Converts a request with a flat map of parameters to a nested map.
See: wrap-nested-params."
{:added "1.2"}
([request]
(nested-params-request request {}))
([request options]
(let [parse (:key-parser options parse-nested-keys)]
(update-in request [:params] nest-params parse))))
(defn wrap-nested-params
"Middleware to converts a flat map of parameters into a nested map.
Accepts the following options:
:key-parser - the function to use to parse the parameter names into a list
of keys. Keys that are empty strings are treated as elements in
a vector, non-empty keys are treated as elements in a map.
Defaults to the parse-nested-keys function.
For example:
{\"foo[bar]\" \"baz\"}
=> {\"foo\" {\"bar\" \"baz\"}}
{\"foo[]\" \"bar\"}
=> {\"foo\" [\"bar\"]}"
([handler]
(wrap-nested-params handler {}))
([handler options]
(fn
([request]
(handler (nested-params-request request options)))
([request respond raise]
(handler (nested-params-request request options) respond raise)))))