From fa37e3e198927906dc83eac2374d742a8c6e3cf5 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 13 Aug 2017 15:44:49 +0300 Subject: [PATCH] Initial sketch for a ring-router --- src/reitit/ring.cljc | 43 +++++++++++++++++++++ test/cljc/reitit/ring_test.cljc | 64 ++++++++++++++++++++++++++++++++ test/cljs/reitit/doo_runner.cljs | 6 ++- 3 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 src/reitit/ring.cljc create mode 100644 test/cljc/reitit/ring_test.cljc diff --git a/src/reitit/ring.cljc b/src/reitit/ring.cljc new file mode 100644 index 00000000..ac078877 --- /dev/null +++ b/src/reitit/ring.cljc @@ -0,0 +1,43 @@ +(ns reitit.ring + (: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 compile-handler [[path {:keys [middleware handler] :as meta}]] + (when-not handler + (throw (ex-info + (str "path '" path "' doesn't have a :handler defined") + {:path path, :meta meta}))) + (let [wrap (->> middleware + (keep identity) + (map expand-middleware) + (apply comp identity))] + (wrap handler))) + +(defn router [data] + (reitit/router data {:compile compile-handler})) + +(defn ring-handler [router] + (fn + ([request] + (if-let [match (reitit/match-by-path router (:uri request))] + ((:handler match) request))) + ([request respond raise] + (if-let [match (reitit/match-by-path router (:uri request))] + ((:handler match) request respond raise))))) diff --git a/test/cljc/reitit/ring_test.cljc b/test/cljc/reitit/ring_test.cljc new file mode 100644 index 00000000..cbf95424 --- /dev/null +++ b/test/cljc/reitit/ring_test.cljc @@ -0,0 +1,64 @@ +(ns reitit.ring-test + (:require [clojure.test :refer [deftest testing is are]] + [reitit.core :as reitit] + [reitit.ring :as ring]) + #?(:clj + (:import (clojure.lang ExceptionInfo)))) + +(defn mw [handler name] + (fn + ([request] + (-> request + (update ::mw (fnil conj []) name) + (handler) + (update :body (fnil conj []) name))) + ([request respond raise] + (handler + (update request ::mw (fnil conj []) name) + #(respond (update % :body (fnil conj []) name)) + raise)))) + +(deftest ring-test + + (testing "all paths should have a handler" + (is (thrown-with-msg? + ExceptionInfo + #"^path '/ping' doesn't have a :handler defined$" + (ring/router ["/ping"])))) + + (testing "ring-handler" + (let [api-mw #(mw % :api) + handler (fn handle + ([{:keys [::mw]}] + {:status 200 :body (conj mw :ok)}) + ([request respond raise] + (respond (handle request)))) + app (ring/ring-handler + (ring/router + [["/ping" handler] + ["/api" {:middleware [api-mw]} + ["/ping" handler] + ["/admin" {:middleware [[mw :admin]]} + ["/ping" handler]]]]))] + + (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 nested middleware" + (is (= {:status 200, :body [:api :admin :ok :admin :api]} + (app {:uri "/api/admin/ping"})))) + + (testing "not found" + (is (= nil (app {:uri "/favicon.ico"})))) + + (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))))))) diff --git a/test/cljs/reitit/doo_runner.cljs b/test/cljs/reitit/doo_runner.cljs index cc33a68e..ff3bd70e 100644 --- a/test/cljs/reitit/doo_runner.cljs +++ b/test/cljs/reitit/doo_runner.cljs @@ -1,7 +1,9 @@ (ns reitit.doo-runner (:require [doo.runner :refer-macros [doo-tests]] - reitit.core-test)) + reitit.core-test + reitit.ring-test)) (enable-console-print!) -(doo-tests 'reitit.core-test) +(doo-tests 'reitit.core-test + 'reitit.ring-test)