|
13 | 13 | [schema.core :as s] |
14 | 14 | [schema.coerce :as sc] |
15 | 15 | [schema.utils :as su] |
16 | | - [schema-tools.core :as st])) |
| 16 | + [schema-tools.core :as st] |
| 17 | + [linked.core :as linked] |
| 18 | + [compojure.api.impl.logging :as logging])) |
17 | 19 |
|
18 | 20 | ;; |
19 | 21 | ;; Meta Evil |
|
27 | 29 | "lexically bound meta-data for handlers." |
28 | 30 | '+compojure-api-meta+) |
29 | 31 |
|
| 32 | +(def +compojure-api-coercer+ |
| 33 | + "lexically bound (caching) coercer for handlers." |
| 34 | + '+compojure-api-coercer+) |
| 35 | + |
30 | 36 | (defmacro meta-container [meta & form] |
31 | 37 | `(let [accumulated-meta# (get-local +compojure-api-meta+) |
32 | 38 | ~'+compojure-api-meta+ (deep-merge accumulated-meta# ~meta)] |
|
47 | 53 | ;; Schema |
48 | 54 | ;; |
49 | 55 |
|
50 | | -(def memoized-coercer (memoize sc/coercer)) |
| 56 | +(defn memoized-coercer |
| 57 | + "Returns a memoized version of a referentially transparent coercer fn. The |
| 58 | + memoized version of the function keeps a cache of the mapping from arguments |
| 59 | + to results and, when calls with the same arguments are repeated often, has |
| 60 | + higher performance at the expense of higher memory use. FIFO with 100 entries. |
| 61 | + Cache will be filled if anonymous coercers are used (does not match the cache)" |
| 62 | + [] |
| 63 | + (let [cache (atom (linked/map)) |
| 64 | + cache-size 100] |
| 65 | + (fn [& args] |
| 66 | + (or (@cache args) |
| 67 | + (let [coercer (apply sc/coercer args)] |
| 68 | + (swap! cache (fn [mem] |
| 69 | + (let [mem (assoc mem args coercer)] |
| 70 | + (if (>= (count mem) cache-size) |
| 71 | + (dissoc mem (-> mem first first)) |
| 72 | + mem)))) |
| 73 | + coercer))))) |
51 | 74 |
|
52 | 75 | (defn strict [schema] |
53 | 76 | (dissoc schema 'schema.core/Keyword)) |
|
57 | 80 | (fnk-impl/letk-input-schema-and-body-form |
58 | 81 | nil (with-meta bind {:schema s/Any}) [] nil))) |
59 | 82 |
|
60 | | -(defn body-coercer-middleware [handler responses] |
| 83 | +(defn body-coercer-middleware [handler coercer responses] |
61 | 84 | (fn [request] |
62 | 85 | (if-let [{:keys [status] :as response} (handler request)] |
63 | 86 | (if-let [schema (:schema (responses status))] |
64 | 87 | (if-let [matcher (:response (mw/get-coercion-matcher-provider request))] |
65 | | - (let [coerce (memoized-coercer (value-of schema) matcher) |
| 88 | + (let [coerce (coercer (value-of schema) matcher) |
66 | 89 | body (coerce (:body response))] |
67 | 90 | (if (su/error? body) |
68 | 91 | (throw+ (assoc body :type ::ex/response-validation)) |
|
79 | 102 | (assert (not (#{:query :json} type)) (str type " is DEPRECATED since 0.22.0. Use :body or :string instead.")) |
80 | 103 | `(let [value# (keywordize-keys (~key ~+compojure-api-request+))] |
81 | 104 | (if-let [matcher# (~type (mw/get-coercion-matcher-provider ~+compojure-api-request+))] |
82 | | - (let [coerce# (memoized-coercer ~schema matcher#) |
| 105 | + (let [coerce# (~+compojure-api-coercer+ ~schema matcher#) |
83 | 106 | result# (coerce# value#)] |
84 | 107 | (if (su/error? result#) |
85 | 108 | (throw+ (assoc result# :type ::ex/request-validation)) |
|
116 | 139 |
|
117 | 140 | (defmulti restructure-param |
118 | 141 | "Restructures a key value pair in smart routes. By default the key |
119 | | - is consumed form the :parameters map in acc. k = given key, v = value." |
| 142 | + is consumed form the :parameters map in acc. k = given key, v = value." |
120 | 143 | (fn [k v acc] k)) |
121 | 144 |
|
122 | 145 | ;; |
|
337 | 360 | (map-of lets letks responses middlewares parameters body) |
338 | 361 | parameters) |
339 | 362 |
|
| 363 | + pre-lets [+compojure-api-coercer+ `(memoized-coercer)] |
| 364 | + |
340 | 365 | body `(~body-wrap ~@body) |
341 | 366 | body (if (seq letks) `(letk ~letks ~body) body) |
342 | 367 | body (if (seq lets) `(let ~lets ~body) body) |
343 | 368 | body (if (seq middlewares) `(route-middlewares ~middlewares ~body ~arg) body) |
344 | 369 | body (if (seq parameters) `(meta-container ~parameters ~body) body) |
345 | 370 | body `(~method-symbol ~path ~arg-with-request ~body) |
346 | | - body (if responses `(body-coercer-middleware ~body ~responses) body)] |
| 371 | + body (if responses `(body-coercer-middleware ~body ~+compojure-api-coercer+ ~responses) body) |
| 372 | + body (if (seq pre-lets) `(let ~pre-lets ~body) body)] |
347 | 373 | body)) |
0 commit comments