From 55a97e604bd7ddedc4e4301829b7fb73e741d29f Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Tue, 15 Aug 2017 08:57:37 +0300 Subject: [PATCH] Welcome reitit.middleware --- src/reitit/middleware.cljc | 44 +++++++++ src/reitit/ring.cljc | 77 ++++------------ test/cljc/reitit/ring_test.cljc | 158 ++++++++++++++++---------------- 3 files changed, 142 insertions(+), 137 deletions(-) create mode 100644 src/reitit/middleware.cljc diff --git a/src/reitit/middleware.cljc b/src/reitit/middleware.cljc new file mode 100644 index 00000000..71af5d15 --- /dev/null +++ b/src/reitit/middleware.cljc @@ -0,0 +1,44 @@ +(ns reitit.middleware + (:require [reitit.core :as reitit])) + +(defprotocol ExpandMiddleware + (expand-middleware [this])) + +(extend-protocol ExpandMiddleware + + #?(:clj clojure.lang.APersistentVector + :cljs cljs.core.PersistentVector) + (expand-middleware [[f & args]] + (fn [handler] + (apply f handler args))) + + #?(:clj clojure.lang.Fn + :cljs function) + (expand-middleware [this] this) + + nil + (expand-middleware [_])) + +(defn- ensure-handler! [path meta scope] + (when-not (:handler meta) + (throw (ex-info + (str "path \"" path "\" doesn't have a :handler defined" + (if scope (str " for " scope))) + (merge {:path path, :meta meta} + (if scope {:scope scope})))))) + +(defn compose-middleware [middleware] + (->> middleware + (keep identity) + (map expand-middleware) + (apply comp identity))) + +(defn compile-handler + ([route opts] + (compile-handler route opts nil)) + ([[path {:keys [middleware handler] :as meta}] _ scope] + (ensure-handler! path meta scope) + ((compose-middleware middleware) handler))) + +(defn router [data] + (reitit/router data {:compile compile-handler})) diff --git a/src/reitit/ring.cljc b/src/reitit/ring.cljc index 681f3fad..56c98c17 100644 --- a/src/reitit/ring.cljc +++ b/src/reitit/ring.cljc @@ -1,47 +1,17 @@ (ns reitit.ring (:require [meta-merge.core :refer [meta-merge]] + [reitit.middleware :as middleware] [reitit.core :as reitit])) -(defprotocol ExpandMiddleware - (expand-middleware [this])) +(def http-methods #{:get :head :patch :delete :options :post :put}) +(defrecord MethodHandlers [get head patch delete options post put]) -(extend-protocol ExpandMiddleware - - #?(:clj clojure.lang.APersistentVector - :cljs cljs.core.PersistentVector) - (expand-middleware [[f & args]] - (fn [handler] - (apply f handler args))) - - #?(:clj clojure.lang.Fn - :cljs function) - (expand-middleware [this] this) - - nil - (expand-middleware [_])) - -(defn- ensure-handler! [path meta method] - (when-not (:handler meta) - (throw (ex-info - (str "path \"" path "\" doesn't have a :handler defined" - (if method (str " for method " method))) - {:path path, :method method, :meta meta})))) - -(defn- compose-middleware [middleware] - (->> middleware - (keep identity) - (map expand-middleware) - (apply comp identity))) - -(defn- compile-handler - ([route opts] - (compile-handler route opts nil)) - ([[path {:keys [middleware handler] :as meta}] _ method] - (ensure-handler! path meta method) - ((compose-middleware middleware) handler))) - -(defn simple-router [data] - (reitit/router data {:compile compile-handler})) +(defn- group-keys [meta] + (reduce-kv + (fn [[top childs] k v] + (if (http-methods k) + [top (assoc childs k v)] + [(assoc top k v) childs])) [{} {}] meta)) (defn ring-handler [router] (with-meta @@ -57,33 +27,24 @@ (defn get-router [handler] (some-> handler meta ::router)) -(def http-methods #{:get :head :patch :delete :options :post :put}) -(defrecord MethodHandlers [get head patch delete options post put]) - -(defn- group-keys [meta] - (reduce-kv - (fn [[top childs] k v] - (if (http-methods k) - [top (assoc childs k v)] - [(assoc top k v) childs])) [{} {}] meta)) - -(defn coerce-method-handler [[path meta] {:keys [expand]}] +(defn coerce-handler [[path meta] {:keys [expand]}] [path (reduce (fn [acc method] (if (contains? acc method) (update acc method expand) acc)) meta http-methods)]) -(defn compile-method-handler [[path meta] opts] +(defn compile-handler [[path meta] opts] (let [[top childs] (group-keys meta)] (if-not (seq childs) - (compile-handler [path meta] opts) + (middleware/compile-handler [path meta] opts) (let [handlers (map->MethodHandlers (reduce-kv - #(assoc %1 %2 (compile-handler [path (meta-merge top %3)] opts %2)) + #(assoc %1 %2 (middleware/compile-handler + [path (meta-merge top %3)] opts %2)) {} childs)) - default-handler (if (:handler top) (compile-handler [path meta] opts)) - resolved-handler (fn [method] (or (method handlers) default-handler))] + default-handler (if (:handler top) (middleware/compile-handler [path meta] opts)) + resolved-handler #(or (% handlers) default-handler)] (fn ([request] (if-let [handler (resolved-handler (:request-method request))] @@ -92,6 +53,6 @@ (if-let [handler (resolved-handler (:request-method request))] (handler request respond raise)))))))) -(defn method-router [data] - (reitit/router data {:coerce coerce-method-handler - :compile compile-method-handler})) +(defn router [data] + (reitit/router data {:coerce coerce-handler + :compile compile-handler})) diff --git a/test/cljc/reitit/ring_test.cljc b/test/cljc/reitit/ring_test.cljc index 4a7a1783..12dddc68 100644 --- a/test/cljc/reitit/ring_test.cljc +++ b/test/cljc/reitit/ring_test.cljc @@ -1,5 +1,6 @@ (ns reitit.ring-test (:require [clojure.test :refer [deftest testing is]] + [reitit.middleware :as middleware] [reitit.ring :as ring]) #?(:clj (:import (clojure.lang ExceptionInfo)))) @@ -23,102 +24,101 @@ ([request respond raise] (respond (handler request)))) -(deftest ring-test +(deftest middleware-router-test - (testing "simple-router" + (testing "all paths should have a handler" + (is (thrown-with-msg? + ExceptionInfo + #"path \"/ping\" doesn't have a :handler defined" + (middleware/router ["/ping"])))) - (testing "all paths should have a handler" - (is (thrown-with-msg? - ExceptionInfo - #"path \"/ping\" doesn't have a :handler defined" - (ring/simple-router ["/ping"])))) + (testing "ring-handler" + (let [api-mw #(mw % :api) + router (middleware/router + [["/ping" handler] + ["/api" {:middleware [api-mw]} + ["/ping" handler] + ["/admin" {:middleware [[mw :admin]]} + ["/ping" handler]]]]) + app (ring/ring-handler router)] - (testing "ring-handler" - (let [api-mw #(mw % :api) - router (ring/simple-router - [["/ping" handler] - ["/api" {:middleware [api-mw]} - ["/ping" handler] - ["/admin" {:middleware [[mw :admin]]} - ["/ping" handler]]]]) - app (ring/ring-handler router)] + (testing "router can be extracted" + (is (= router (ring/get-router app)))) - (testing "router can be extracted" - (is (= router (ring/get-router app)))) + (testing "not found" + (is (= nil (app {:uri "/favicon.ico"})))) - (testing "not found" - (is (= nil (app {:uri "/favicon.ico"})))) + (testing "normal handler" + (is (= {:status 200, :body [:ok]} + (app {:uri "/ping"})))) - (testing "normal handler" - (is (= {:status 200, :body [:ok]} - (app {:uri "/ping"})))) + (testing "with middleware" + (is (= {:status 200, :body [:api :ok :api]} + (app {:uri "/api/ping"})))) - (testing "with middleware" - (is (= {:status 200, :body [:api :ok :api]} - (app {:uri "/api/ping"})))) + (testing "with nested middleware" + (is (= {:status 200, :body [:api :admin :ok :admin :api]} + (app {:uri "/api/admin/ping"})))) - (testing "with nested middleware" + (testing "3-arity" + (let [result (atom nil) + respond (partial reset! result), raise ::not-called] + (app {:uri "/api/admin/ping"} respond raise) (is (= {:status 200, :body [:api :admin :ok :admin :api]} - (app {:uri "/api/admin/ping"})))) + @result))))))) - (testing "3-arity" - (let [result (atom nil) - respond (partial reset! result), raise ::not-called] - (app {:uri "/api/admin/ping"} respond raise) - (is (= {:status 200, :body [:api :admin :ok :admin :api]} - @result))))))) - (testing "method-router" +(deftest ring-router-test - (testing "all paths should have a handler" - (is (thrown-with-msg? - ExceptionInfo - #"path \"/ping\" doesn't have a :handler defined for method :get" - (ring/method-router ["/ping" {:get {}}])))) + (testing "all paths should have a handler" + (is (thrown-with-msg? + ExceptionInfo + #"path \"/ping\" doesn't have a :handler defined for :get" + (ring/router ["/ping" {:get {}}])))) - (testing "ring-handler" - (let [api-mw #(mw % :api) - router (ring/method-router - [["/api" {:middleware [api-mw]} - ["/all" handler] - ["/get" {:get handler}] - ["/users" {:middleware [[mw :users]] - :get handler - :post {:handler handler - :middleware [[mw :post]]} - :handler handler}]]]) - app (ring/ring-handler router)] + (testing "ring-handler" + (let [api-mw #(mw % :api) + router (ring/router + [["/api" {:middleware [api-mw]} + ["/all" handler] + ["/get" {:get handler}] + ["/users" {:middleware [[mw :users]] + :get handler + :post {:handler handler + :middleware [[mw :post]]} + :handler handler}]]]) + app (ring/ring-handler router)] - (testing "router can be extracted" - (is (= router (ring/get-router app)))) + (testing "router can be extracted" + (is (= router (ring/get-router app)))) - (testing "not found" - (is (= nil (app {:uri "/favicon.ico"})))) + (testing "not found" + (is (= nil (app {:uri "/favicon.ico"})))) - (testing "catch all handler" - (is (= {:status 200, :body [:api :ok :api]} - (app {:uri "/api/all" :request-method :get})))) + (testing "catch all handler" + (is (= {:status 200, :body [:api :ok :api]} + (app {:uri "/api/all" :request-method :get})))) - (testing "just get handler" - (is (= {:status 200, :body [:api :ok :api]} - (app {:uri "/api/get" :request-method :get}))) - (is (= nil (app {:uri "/api/get" :request-method :post})))) + (testing "just get handler" + (is (= {:status 200, :body [:api :ok :api]} + (app {:uri "/api/get" :request-method :get}))) + (is (= nil (app {:uri "/api/get" :request-method :post})))) - (testing "expanded method handler" - (is (= {:status 200, :body [:api :users :ok :users :api]} - (app {:uri "/api/users" :request-method :get})))) + (testing "expanded method handler" + (is (= {:status 200, :body [:api :users :ok :users :api]} + (app {:uri "/api/users" :request-method :get})))) - (testing "method handler with middleware" + (testing "method handler with middleware" + (is (= {:status 200, :body [:api :users :post :ok :post :users :api]} + (app {:uri "/api/users" :request-method :post})))) + + (testing "fallback handler" + (is (= {:status 200, :body [:api :users :ok :users :api]} + (app {:uri "/api/users" :request-method :put})))) + + (testing "3-arity" + (let [result (atom nil) + respond (partial reset! result), raise ::not-called] + (app {:uri "/api/users" :request-method :post} respond raise) (is (= {:status 200, :body [:api :users :post :ok :post :users :api]} - (app {:uri "/api/users" :request-method :post})))) - - (testing "fallback handler" - (is (= {:status 200, :body [:api :users :ok :users :api]} - (app {:uri "/api/users" :request-method :put})))) - - (testing "3-arity" - (let [result (atom nil) - respond (partial reset! result), raise ::not-called] - (app {:uri "/api/users" :request-method :post} respond raise) - (is (= {:status 200, :body [:api :users :post :ok :post :users :api]} - @result)))))))) + @result)))))))