From 5e7be28eb7e64b20867ed653634ca06aa8eab4a5 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 13 Aug 2018 08:17:55 +0300 Subject: [PATCH] fn -> handler in interceptors --- .../reitit-core/src/reitit/interceptor.cljc | 52 +++++----- test/cljc/reitit/interceptor_test.cljc | 94 +++++++++---------- 2 files changed, 70 insertions(+), 76 deletions(-) diff --git a/modules/reitit-core/src/reitit/interceptor.cljc b/modules/reitit-core/src/reitit/interceptor.cljc index 1e5af68d..9b085ec8 100644 --- a/modules/reitit-core/src/reitit/interceptor.cljc +++ b/modules/reitit-core/src/reitit/interceptor.cljc @@ -7,8 +7,12 @@ (defprotocol IntoInterceptor (into-interceptor [this data opts])) -(defrecord Interceptor [name enter leave error]) +(defrecord Interceptor [name handler? enter leave error]) (defrecord Endpoint [data interceptors]) +(defrecord Context [request response exception]) + +(defn context [request] + (map->Context {:request request})) (def ^:dynamic *max-compile-depth* 10) @@ -44,9 +48,12 @@ #?(:clj clojure.lang.Fn :cljs function) - (into-interceptor [this _ _] - (map->Interceptor - {:enter this})) + (into-interceptor [this data opts] + (into-interceptor + {:handler? true + :enter (fn [ctx] + (assoc ctx :response (this (:request ctx))))} + data opts)) #?(:clj clojure.lang.PersistentArrayMap :cljs cljs.core.PersistentArrayMap) @@ -78,43 +85,30 @@ nil (into-interceptor [_ _ _])) -(defn- ensure-handler! [path data scope] - (when-not (:handler data) - (throw (ex-info - (str "path \"" path "\" doesn't have a :handler defined" - (if scope (str " for " scope))) - (merge {:path path, :data data} - (if scope {:scope scope})))))) - -(defn- expand-and-transform - [interceptors data {:keys [::transform] :or {transform identity} :as opts}] - (->> interceptors - (keep #(into-interceptor % data opts)) - (transform) - (keep #(into-interceptor % data opts)) - (into []))) - ;; ;; public api ;; (defn chain "Creates a Interceptor chain out of sequence of IntoInterceptor - and optionally a handler. Optionally takes route data and (Router) opts." - ([interceptors handler data] - (chain interceptors handler data nil)) - ([interceptors handler data opts] - (let [interceptor (some-> (into-interceptor handler data opts) - (assoc :name (:name data)))] - (-> (expand-and-transform interceptors data opts) - (cond-> interceptor (conj interceptor)))))) + Optionally takes route data and (Router) opts." + ([interceptors] + (chain interceptors nil nil)) + ([interceptors data] + (chain interceptors data nil)) + ([interceptors data {:keys [::transform] :or {transform identity} :as opts}] + (->> interceptors + (keep #(into-interceptor % data opts)) + (transform) + (keep #(into-interceptor % data opts)) + (into [])))) (defn compile-result ([route opts] (compile-result route opts nil)) ([[_ {:keys [interceptors handler] :as data}] opts _] (map->Endpoint - {:interceptors (chain interceptors handler data opts) + {:interceptors (chain (into (vec interceptors) [handler]) data opts) :data data}))) (defn router diff --git a/test/cljc/reitit/interceptor_test.cljc b/test/cljc/reitit/interceptor_test.cljc index 2c374b7d..1af4f4fd 100644 --- a/test/cljc/reitit/interceptor_test.cljc +++ b/test/cljc/reitit/interceptor_test.cljc @@ -5,32 +5,39 @@ #?(:clj (:import (clojure.lang ExceptionInfo)))) +(def ctx (interceptor/context [])) + (defn execute [interceptors ctx] (as-> ctx $ (reduce #(%2 %1) $ (keep :enter interceptors)) - (reduce #(%2 %1) $ (reverse (keep :leave interceptors))))) + (reduce #(%2 %1) $ (reverse (keep :leave interceptors))) + (:response $))) -(def ctx []) +(defn f [value ctx] + (update ctx :request conj value)) + +(defn kws [k qk] + (keyword (namespace qk) (str (name k) "_" (name qk)))) (defn interceptor [value] {:name value - :enter #(conj % value) - :leave #(conj % value)}) + :enter #(update % :request (fnil conj []) (kws :enter value)) + :leave #(update % :response (fnil conj []) (kws :leave value))}) (defn enter [value] {:name value - :enter #(conj % value)}) + :enter (partial f value)}) -(defn handler [ctx] - (conj ctx :ok)) +(defn handler [request] + (conj request :ok)) (defn create ([interceptors] - (create interceptors nil)) + (create interceptors nil)) ([interceptors opts] (let [chain (interceptor/chain - interceptors - handler :data opts)] + (conj interceptors handler) + :data opts)] (partial execute chain)))) (deftest expand-interceptor-test @@ -41,8 +48,8 @@ (let [calls (atom 0) enter (fn [value] (swap! calls inc) - (fn [ctx] - (conj ctx value)))] + {:enter (fn [ctx] + (update ctx :request conj value))})] (testing "as function" (reset! calls 0) @@ -73,14 +80,14 @@ (testing "as map" (reset! calls 0) - (let [app (create [{:enter (enter :value)}])] + (let [app (create [{:enter (:enter (enter :value))}])] (dotimes [_ 10] (is (= [:value :ok] (app ctx))) (is (= 1 @calls))))) (testing "as Interceptor" (reset! calls 0) - (let [app (create [(interceptor/map->Interceptor {:enter (enter :value)})])] + (let [app (create [(interceptor/map->Interceptor {:enter (:enter (enter :value))})])] (dotimes [_ 10] (is (= [:value :ok] (app ctx))) (is (= 1 @calls))))))) @@ -90,12 +97,12 @@ i1 (fn [value] {:compile (fn [data _] (swap! calls inc) - (fn [ctx] - (into ctx [data value])))}) + {:enter (fn [ctx] + (update ctx :request into [data value]))})}) i3 (fn [value] - {:compile (fn [data _] + {:compile (fn [_ _] (swap! calls inc) - {:compile (fn [data _] + {:compile (fn [_ _] (swap! calls inc) (i1 value))})})] @@ -137,16 +144,10 @@ (let [handler (interceptor/interceptor-handler router)] (fn [path] (when-let [interceptors (handler path)] - (execute interceptors []))))) + (execute interceptors ctx))))) (deftest interceptor-handler-test - (testing "all paths should have a handler" - (is (thrown-with-msg? - ExceptionInfo - #"path \"/ping\" doesn't have a :handler defined" - (interceptor/router ["/ping"])))) - (testing "interceptor-handler" (let [api-interceptor (interceptor :api) router (interceptor/router @@ -164,36 +165,35 @@ (is (= [:ok] (app "/ping")))) (testing "with interceptor" - (is (= [:api :ok :api] (app "/api/ping")))) + (is (= [:enter_api :ok :leave_api] (app "/api/ping")))) (testing "with nested interceptor" - (is (= [:api :admin :ok :admin :api] (app "/api/admin/ping")))) + (is (= [:enter_api :enter_admin :ok :leave_admin :leave_api] (app "/api/admin/ping")))) (testing ":compile interceptor can be unmounted at creation-time" (let [i1 {:name ::i1, :compile (constantly (interceptor ::i1))} i2 {:name ::i2, :compile (constantly nil)} i3 (interceptor ::i3) router (interceptor/router - ["/api" {:name ::api - :interceptors [i1 i2 i3 i2] + ["/api" {:interceptors [i1 i2 i3 i2] :handler handler}]) app (create-app router)] - (is (= [::i1 ::i3 :ok ::i3 ::i1] (app "/api"))) + (is (= [::enter_i1 ::enter_i3 :ok ::leave_i3 ::leave_i1] (app "/api"))) (testing "routes contain list of actually applied interceptors" - (is (= [::i1 ::i3 ::api] (->> (r/compiled-routes router) - first - last - :interceptors - (map :name))))) + (is (= [::i1 ::i3 nil] (->> (r/compiled-routes router) + first + last + :interceptors + (map :name))))) (testing "match contains list of actually applied interceptors" - (is (= [::i1 ::i3 ::api] (->> "/api" - (r/match-by-path router) - :result - :interceptors - (map :name)))))))))) + (is (= [::i1 ::i3 nil] (->> "/api" + (r/match-by-path router) + :result + :interceptors + (map :name)))))))))) (deftest chain-test (testing "chain can produce interceptor chain of any IntoInterceptor" @@ -204,12 +204,12 @@ i5 {:compile (fn [{:keys [mount?]} _] (when mount? (interceptor ::i5)))} - chain1 (interceptor/chain [i1 i2 i3 i4 i5] handler {:mount? true}) - chain2 (interceptor/chain [i1 i2 i3 i4 i5] handler {:mount? false}) - chain3 (interceptor/chain [i1 i2 i3 i4 i5] nil {:mount? false})] - (is (= [::i1 ::i3 ::i4 ::i5 :ok ::i5 ::i4 ::i3 ::i1] (execute chain1 []))) - (is (= [::i1 ::i3 ::i4 :ok ::i4 ::i3 ::i1] (execute chain2 []))) - (is (= [::i1 ::i3 ::i4 ::i4 ::i3 ::i1] (execute chain3 [])))))) + chain1 (interceptor/chain [i1 i2 i3 i4 i5 handler] {:mount? true}) + chain2 (interceptor/chain [i1 i2 i3 i4 i5 handler] {:mount? false}) + chain3 (interceptor/chain [i1 i2 i3 i4 i5] {:mount? false})] + (is (= [::enter_i1 ::enter_i3 ::enter_i4 ::enter_i5 :ok ::leave_i5 ::leave_i4 ::leave_i3 ::leave_i1] (execute chain1 ctx))) + (is (= [::enter_i1 ::enter_i3 ::enter_i4 :ok ::leave_i4 ::leave_i3 ::leave_i1] (execute chain2 ctx))) + (is (= [::leave_i4 ::leave_i3 ::leave_i1] (execute chain3 ctx)))))) (deftest interceptor-transform-test (let [debug-i (enter ::debug) @@ -227,7 +227,7 @@ (is (= [::olipa ::kerran ::avaruus :ok] (app "/ping"))))) (testing "interceptors can be re-ordered" - (let [app (create {::interceptor/transform (partial sort-by :name)})] + (let [app (create {::interceptor/transform (partial sort-by (juxt :handler? :name))})] (is (= [::avaruus ::kerran ::olipa :ok] (app "/ping"))))) (testing "adding debug interceptor between interceptors"