Files
integreat/src/clj/auto_ap/ssr/nested_form_params.clj

95 lines
2.7 KiB
Clojure

(ns auto-ap.ssr.nested-form-params
(:require [ring.util.codec :refer [assoc-conj]]))
;; ADAPTED FROM ring.middleware.nested-params
(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)]
(-> request
(assoc :raw-form-params (:form-params request))
(update-in [:form-params] nest-params parse)))))
(defn wrap-nested-form-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-form-params handler {}))
([handler options]
(fn
([request]
(handler (nested-params-request request options)))
([request respond raise]
(handler (nested-params-request request options) respond raise)))))