Remove middleware/create & polish stuff

This commit is contained in:
Tommi Reiman 2017-12-12 22:27:00 +02:00
parent aab842af49
commit 927d4d4389
3 changed files with 121 additions and 113 deletions

View file

@ -9,13 +9,6 @@
(defrecord Middleware [name wrap]) (defrecord Middleware [name wrap])
(defrecord Endpoint [data handler middleware]) (defrecord Endpoint [data handler middleware])
(defn create [{:keys [wrap compile] :as m}]
(when (and wrap compile)
(throw
(ex-info
(str "Middleware can't have both :wrap and :compile defined " m) m)))
(map->Middleware m))
(def ^:dynamic *max-compile-depth* 10) (def ^:dynamic *max-compile-depth* 10)
(extend-protocol IntoMiddleware (extend-protocol IntoMiddleware
@ -35,12 +28,12 @@
#?(:clj clojure.lang.PersistentArrayMap #?(:clj clojure.lang.PersistentArrayMap
:cljs cljs.core.PersistentArrayMap) :cljs cljs.core.PersistentArrayMap)
(into-middleware [this data opts] (into-middleware [this data opts]
(into-middleware (create this) data opts)) (into-middleware (map->Middleware this) data opts))
#?(:clj clojure.lang.PersistentHashMap #?(:clj clojure.lang.PersistentHashMap
:cljs cljs.core.PersistentHashMap) :cljs cljs.core.PersistentHashMap)
(into-middleware [this data opts] (into-middleware [this data opts]
(into-middleware (create this) data opts)) (into-middleware (map->Middleware this) data opts))
Middleware Middleware
(into-middleware [{:keys [compile] :as this} data opts] (into-middleware [{:keys [compile] :as this} data opts]
@ -56,7 +49,7 @@
(if-let [middeware (into-middleware (compile data opts) data opts)] (if-let [middeware (into-middleware (compile data opts) data opts)]
(map->Middleware (map->Middleware
(merge (merge
(dissoc this :create) (dissoc this :compile)
(impl/strip-nils middeware))))))) (impl/strip-nils middeware)))))))
nil nil
@ -70,21 +63,38 @@
(merge {:path path, :data data} (merge {:path path, :data data}
(if scope {:scope scope})))))) (if scope {:scope scope}))))))
(defn expand [middleware data opts] (defn- expand-and-transform
[middleware data {:keys [::transform] :or {transform identity} :as opts}]
(->> middleware (->> middleware
(keep #(into-middleware % data opts))
(into [])
(transform)
(keep #(into-middleware % data opts)) (keep #(into-middleware % data opts))
(into []))) (into [])))
(defn compile-handler [middleware handler] (defn- compile-handler [middleware handler]
((apply comp identity (keep :wrap middleware)) handler)) ((apply comp identity (keep :wrap middleware)) handler))
;;
;; public api
;;
(defn chain
"Creates a Ring middleware chain out of sequence of IntoMiddleware
and Handler. Optional takes route data and (Router) opts."
([middleware handler]
(chain middleware handler nil))
([middleware handler data]
(chain middleware handler data nil))
([middleware handler data opts]
(compile-handler (expand-and-transform middleware data opts) handler)))
(defn compile-result (defn compile-result
([route opts] ([route opts]
(compile-result route opts nil)) (compile-result route opts nil))
([[path {:keys [middleware handler] :as data}] ([[path {:keys [middleware handler] :as data}] opts scope]
{:keys [::transform] :or {transform identity} :as opts} scope]
(ensure-handler! path data scope) (ensure-handler! path data scope)
(let [middleware (expand (transform (expand middleware data opts)) data opts)] (let [middleware (expand-and-transform middleware data opts)]
(map->Endpoint (map->Endpoint
{:handler (compile-handler middleware handler) {:handler (compile-handler middleware handler)
:middleware middleware :middleware middleware
@ -101,7 +111,13 @@
[\"/users\" {:middleware [wrap-delete] [\"/users\" {:middleware [wrap-delete]
:handler get-user}]]) :handler get-user}]])
See router options from [[reitit.core/router]]." Options:
| key | description |
| -------------------------------|-------------|
| `:reitit.middleware/transform` | Function of `[Middleware] => [Middleware]` to transform the expanded Middleware (default: identity).
See other router options from [[reitit.core/router]]."
([data] ([data]
(router data nil)) (router data nil))
([data opts] ([data opts]
@ -116,11 +132,3 @@
:result :result
:handler)) :handler))
{::router router})) {::router router}))
(defn chain
"Creates a vanilla ring middleware chain out of sequence of
IntoMiddleware thingies."
([middleware handler data]
(chain middleware handler data nil))
([middleware handler data opts]
(compile-handler (expand middleware data opts) handler)))

View file

@ -1,6 +1,5 @@
(ns reitit.ring.coercion-middleware (ns reitit.ring.coercion-middleware
(:require [reitit.middleware :as middleware] (:require [reitit.coercion :as coercion]
[reitit.coercion :as coercion]
[reitit.impl :as impl])) [reitit.impl :as impl]))
(defn handle-coercion-exception [e respond raise] (defn handle-coercion-exception [e respond raise]
@ -22,53 +21,50 @@
"Middleware for pluggable request coercion. "Middleware for pluggable request coercion.
Expects a :coercion of type `reitit.coercion/Coercion` Expects a :coercion of type `reitit.coercion/Coercion`
and :parameters from route data, otherwise does not mount." and :parameters from route data, otherwise does not mount."
(middleware/create {:name ::coerce-parameters
{:name ::coerce-parameters :compile (fn [{:keys [coercion parameters]} opts]
:compile (fn [{:keys [coercion parameters]} opts] (if (and coercion parameters)
(if (and coercion parameters) (let [coercers (coercion/request-coercers coercion parameters opts)]
(let [coercers (coercion/request-coercers coercion parameters opts)] (fn [handler]
(fn [handler] (fn
(fn ([request]
([request] (let [coerced (coercion/coerce-request coercers request)]
(let [coerced (coercion/coerce-request coercers request)] (handler (impl/fast-assoc request :parameters coerced))))
(handler (impl/fast-assoc request :parameters coerced)))) ([request respond raise]
([request respond raise] (let [coerced (coercion/coerce-request coercers request)]
(let [coerced (coercion/coerce-request coercers request)] (handler (impl/fast-assoc request :parameters coerced) respond raise))))))))})
(handler (impl/fast-assoc request :parameters coerced) respond raise))))))))}))
(def coerce-response-middleware (def coerce-response-middleware
"Middleware for pluggable response coercion. "Middleware for pluggable response coercion.
Expects a :coercion of type `reitit.coercion/Coercion` Expects a :coercion of type `reitit.coercion/Coercion`
and :responses from route data, otherwise does not mount." and :responses from route data, otherwise does not mount."
(middleware/create {:name ::coerce-response
{:name ::coerce-response :compile (fn [{:keys [coercion responses]} opts]
:compile (fn [{:keys [coercion responses]} opts] (if (and coercion responses)
(if (and coercion responses) (let [coercers (coercion/response-coercers coercion responses opts)]
(let [coercers (coercion/response-coercers coercion responses opts)] (fn [handler]
(fn [handler] (fn
(fn ([request]
([request] (coercion/coerce-response coercers request (handler request)))
(coercion/coerce-response coercers request (handler request))) ([request respond raise]
([request respond raise] (handler request #(respond (coercion/coerce-response coercers request %)) raise)))))))})
(handler request #(respond (coercion/coerce-response coercers request %)) raise)))))))}))
(def coerce-exceptions-middleware (def coerce-exceptions-middleware
"Middleware for handling coercion exceptions. "Middleware for handling coercion exceptions.
Expects a :coercion of type `reitit.coercion/Coercion` Expects a :coercion of type `reitit.coercion/Coercion`
and :parameters or :responses from route data, otherwise does not mount." and :parameters or :responses from route data, otherwise does not mount."
(middleware/create {:name ::coerce-exceptions
{:name ::coerce-exceptions :compile (fn [{:keys [coercion parameters responses]} _]
:compile (fn [{:keys [coercion parameters responses]} _] (if (and coercion (or parameters responses))
(if (and coercion (or parameters responses)) (fn [handler]
(fn [handler] (fn
(fn ([request]
([request] (try
(try (handler request)
(handler request) (catch #?(:clj Exception :cljs js/Error) e
(catch #?(:clj Exception :cljs js/Error) e (handle-coercion-exception e identity #(throw %)))))
(handle-coercion-exception e identity #(throw %))))) ([request respond raise]
([request respond raise] (try
(try (handler request respond #(handle-coercion-exception % respond raise))
(handler request respond #(handle-coercion-exception % respond raise)) (catch #?(:clj Exception :cljs js/Error) e
(catch #?(:clj Exception :cljs js/Error) e (handle-coercion-exception e respond raise))))))))})
(handle-coercion-exception e respond raise))))))))}))

View file

@ -1,75 +1,80 @@
(ns reitit.middleware-test (ns reitit.middleware-test
(:require [clojure.test :refer [deftest testing is are]] (:require [clojure.test :refer [deftest testing is are]]
[reitit.middleware :as middleware] [reitit.middleware :as middleware]
[clojure.set :as set]
[reitit.core :as r]) [reitit.core :as r])
#?(:clj #?(:clj
(:import (clojure.lang ExceptionInfo)))) (:import (clojure.lang ExceptionInfo))))
(def request [])
(defn handler [request]
(conj request :ok))
(defn create [middleware]
(middleware/chain
middleware
handler
:data
nil))
(deftest expand-middleware-test (deftest expand-middleware-test
(testing "middleware records" (testing "middleware records"
(testing ":wrap & :compile are exclusive"
(is (thrown-with-msg?
ExceptionInfo
#"Middleware can't have both :wrap and :compile defined"
(middleware/create
{:name ::test
:wrap identity
:compile (constantly identity)}))))
(testing "middleware" (testing "middleware"
(let [calls (atom 0) (let [calls (atom 0)
wrap (fn [handler value] wrap (fn [handler value]
(swap! calls inc) (swap! calls inc)
(fn [request] (fn [request]
[value request])) (handler (conj request value))))]
->app (fn [ast handler]
(middleware/compile-handler
(middleware/expand ast :data {})
handler))]
(testing "as middleware function" (testing "as function"
(reset! calls 0) (reset! calls 0)
(let [app (->app [[#(wrap % :value)]] identity)] (let [app (create [#(wrap % :value)])]
(dotimes [_ 10] (dotimes [_ 10]
(is (= [:value :request] (app :request))) (is (= [:value :ok] (app request)))
(is (= 1 @calls))))) (is (= 1 @calls)))))
(testing "as middleware vector" (testing "as function vector"
(reset! calls 0) (reset! calls 0)
(let [app (->app [[wrap :value]] identity)] (let [app (create [[#(wrap % :value)]])]
(dotimes [_ 10] (dotimes [_ 10]
(is (= [:value :request] (app :request))) (is (= [:value :ok] (app request)))
(is (= 1 @calls)))))
(testing "as function vector with value(s)"
(reset! calls 0)
(let [app (create [[wrap :value]])]
(dotimes [_ 10]
(is (= [:value :ok] (app request)))
(is (= 1 @calls))))) (is (= 1 @calls)))))
(testing "as map" (testing "as map"
(reset! calls 0) (reset! calls 0)
(let [app (->app [[{:wrap #(wrap % :value)}]] identity)] (let [app (create [[{:wrap #(wrap % :value)}]])]
(dotimes [_ 10] (dotimes [_ 10]
(is (= [:value :request] (app :request))) (is (= [:value :ok] (app request)))
(is (= 1 @calls))))) (is (= 1 @calls)))))
(testing "as map vector" (testing "as map vector"
(reset! calls 0) (reset! calls 0)
(let [app (->app [[{:wrap wrap} :value]] identity)] (let [app (create [[{:wrap wrap} :value]])]
(dotimes [_ 10] (dotimes [_ 10]
(is (= [:value :request] (app :request))) (is (= [:value :ok] (app request)))
(is (= 1 @calls))))) (is (= 1 @calls)))))
(testing "as Middleware" (testing "as Middleware"
(reset! calls 0) (reset! calls 0)
(let [app (->app [[(middleware/create {:wrap #(wrap % :value)})]] identity)] (let [app (create [[(middleware/map->Middleware {:wrap #(wrap % :value)})]])]
(dotimes [_ 10] (dotimes [_ 10]
(is (= [:value :request] (app :request))) (is (= [:value :ok] (app request)))
(is (= 1 @calls))))) (is (= 1 @calls)))))
(testing "as Middleware vector" (testing "as Middleware vector"
(reset! calls 0) (reset! calls 0)
(let [app (->app [[(middleware/create {:wrap wrap}) :value]] identity)] (let [app (create [[(middleware/map->Middleware {:wrap wrap}) :value]])]
(dotimes [_ 10] (dotimes [_ 10]
(is (= [:value :request] (app :request))) (is (= [:value :ok] (app request)))
(is (= 1 @calls))))))) (is (= 1 @calls)))))))
(testing "compiled Middleware" (testing "compiled Middleware"
@ -79,53 +84,52 @@
(fn [handler value] (fn [handler value]
(swap! calls inc) (swap! calls inc)
(fn [request] (fn [request]
[data value request])))} (handler (into request [data value])))))}
mw3 {:compile (fn [data _] mw3 {:compile (fn [data _]
(swap! calls inc) (swap! calls inc)
{:compile (fn [data _] {:compile (fn [data _]
(swap! calls inc) (swap! calls inc)
mw)})} mw)})}]
->app (fn [ast handler]
(middleware/compile-handler
(middleware/expand ast :data {})
handler))]
(testing "as map" (testing "as map"
(reset! calls 0) (reset! calls 0)
(let [app (->app [[mw :value]] identity)] (let [app (create [[mw :value]])]
(dotimes [_ 10] (dotimes [_ 10]
(is (= [:data :value :request] (app :request))) (is (= [:data :value :ok] (app request)))
(is (= 2 @calls))))) (is (= 2 @calls)))))
(testing "as Middleware" (testing "as Middleware"
(reset! calls 0) (reset! calls 0)
(let [app (->app [[(middleware/create mw) :value]] identity)] (let [app (create [[(middleware/map->Middleware mw) :value]])]
(dotimes [_ 10] (dotimes [_ 10]
(is (= [:data :value :request] (app :request))) (is (= [:data :value :ok] (app request)))
(is (= 2 @calls))))) (is (= 2 @calls)))))
(testing "deeply compiled Middleware" (testing "deeply compiled Middleware"
(reset! calls 0) (reset! calls 0)
(let [app (->app [[(middleware/create mw3) :value]] identity)] (let [app (create [[(middleware/map->Middleware mw3) :value]])]
(dotimes [_ 10] (dotimes [_ 10]
(is (= [:data :value :request] (app :request))) (is (= [:data :value :ok] (app request)))
(is (= 4 @calls))))) (is (= 4 @calls)))))
(testing "too deeply compiled Middleware fails" (testing "too deeply compiled Middleware fails"
(binding [middleware/*max-compile-depth* 2] (binding [middleware/*max-compile-depth* 2]
(is (thrown? ExceptionInfo (->app [[(middleware/create mw3) :value]] identity))))) (is (thrown?
ExceptionInfo
#"Too deep Middleware compilation"
(create [[(middleware/map->Middleware mw3) :value]])))))
(testing "nil unmounts the middleware" (testing "nil unmounts the middleware"
(let [app (->app [{:compile (constantly nil)} (let [app (create [{:compile (constantly nil)}
{:compile (constantly nil)}] identity)] {:compile (constantly nil)}])]
(dotimes [_ 10] (dotimes [_ 10]
(is (= :request (app :request)))))))))) (is (= [:ok] (app request))))))))))
(defn create-app [router] (defn create-app [router]
(let [h (middleware/middleware-handler router)] (let [h (middleware/middleware-handler router)]
(fn [path] (fn [path]
(if-let [f (h path)] (if-let [f (h path)]
(f []))))) (f request)))))
(deftest middleware-handler-test (deftest middleware-handler-test
@ -188,7 +192,7 @@
(map :name)))))))))) (map :name))))))))))
(deftest chain-test (deftest chain-test
(testing "chain can produce middlware chain of any IntoMiddleware" (testing "chain can produce middleware chain of any IntoMiddleware"
(let [mw (fn [handler value] (let [mw (fn [handler value]
#(conj (handler (conj % value)) value)) #(conj (handler (conj % value)) value))
handler #(conj % :ok) handler #(conj % :ok)