From f5f110482636061a4c12b10b5bd529df18444893 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 21 Aug 2017 09:02:03 +0300 Subject: [PATCH] Router option to handle conflicts --- README.md | 17 +++++++++-------- src/reitit/core.cljc | 29 ++++++++++++++++++++--------- test/cljc/reitit/core_test.cljc | 21 ++++++++++++++++++--- 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 7cad9a20..ca5a865f 100644 --- a/README.md +++ b/README.md @@ -392,14 +392,15 @@ Authorized access to guarded route: Routers can be configured via options. Options allow things like [`clojure.spec`](https://clojure.org/about/spec) validation for meta-data and fast, compiled handlers. The following options are available for the `reitit.core/router`: - | key | description | - | -----------|-------------| - | `:path` | Base-path for routes (default `""`) - | `:routes` | Initial resolved routes (default `[]`) - | `:meta` | Initial expanded route-meta vector (default `[]`) - | `:expand` | Function of `arg opts => meta` to expand route arg to route meta-data (default `reitit.core/expand`) - | `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil` - | `:compile` | Function of `route opts => handler` to compile a route handler + | key | description | + | -------------|-------------| + | `:path` | Base-path for routes (default `""`) + | `:routes` | Initial resolved routes (default `[]`) + | `:meta` | Initial expanded route-meta vector (default `[]`) + | `:expand` | Function of `arg opts => meta` to expand route arg to route meta-data (default `reitit.core/expand`) + | `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil` + | `:compile` | Function of `route opts => handler` to compile a route handler + | `:conflicts` | Function of `[route route] => side-effect` to handle conflicting routes (default `reitit.core/throw-on-conflicts!`)" ## Special thanks diff --git a/src/reitit/core.cljc b/src/reitit/core.cljc index 3f8db58b..fc8b3342 100644 --- a/src/reitit/core.cljc +++ b/src/reitit/core.cljc @@ -66,6 +66,12 @@ (or (some #(if (impl/conflicting-routes? r %) [r %]) rest) (recur rest))))) +(defn throw-on-conflicts! [routes] + (throw + (ex-info + (str "router contains conflicting routes: " routes) + {:routes routes}))) + (defn name-lookup [[_ {:keys [name]}] opts] (if name #{name})) @@ -103,7 +109,8 @@ {:lookup name-lookup :expand expand :coerce (fn [route _] route) - :compile (fn [[_ {:keys [handler]}] _] handler)}) + :compile (fn [[_ {:keys [handler]}] _] handler) + :conflicts throw-on-conflicts!}) (defn linear-router "Creates a [[LinearRouter]] from resolved routes and optional @@ -191,18 +198,22 @@ If routes contain wildcards, a [[LinearRouter]] is used, otherwise a [[LookupRouter]]. The following options are available: - | key | description | - | -----------|-------------| - | `:path` | Base-path for routes (default `\"\"`) - | `:routes` | Initial resolved routes (default `[]`) - | `:meta` | Initial expanded route-meta vector (default `[]`) - | `:expand` | Function of `arg opts => meta` to expand route arg to route meta-data (default `reitit.core/expand`) - | `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil` - | `:compile` | Function of `route opts => handler` to compile a route handler" + | key | description | + | -------------|-------------| + | `:path` | Base-path for routes (default `\"\"`) + | `:routes` | Initial resolved routes (default `[]`) + | `:meta` | Initial expanded route-meta vector (default `[]`) + | `:expand` | Function of `arg opts => meta` to expand route arg to route meta-data (default `reitit.core/expand`) + | `:coerce` | Function of `route opts => route` to coerce resolved route, can throw or return `nil` + | `:compile` | Function of `route opts => handler` to compile a route handler + | `:conflicts` | Function of `[route route] => side-effect` to handle conflicting routes (default `reitit.core/throw-on-conflicts!`)" ([data] (router data {})) ([data opts] (let [opts (meta-merge default-router-options opts) routes (resolve-routes data opts)] + (when-let [conflicts (:conflicts opts)] + (when-let [conflicting-routes (first-conflicting-routes routes)] + (conflicts conflicting-routes))) ((if (some impl/contains-wilds? (map first routes)) linear-router lookup-router) routes opts)))) diff --git a/test/cljc/reitit/core_test.cljc b/test/cljc/reitit/core_test.cljc index 854b02ed..b099233d 100644 --- a/test/cljc/reitit/core_test.cljc +++ b/test/cljc/reitit/core_test.cljc @@ -1,8 +1,8 @@ (ns reitit.core-test (:require [clojure.test :refer [deftest testing is are]] - [reitit.core :as reitit #?@(:cljs [:refer [Match]])]) + [reitit.core :as reitit #?@(:cljs [:refer [Match Routing]])]) #?(:clj - (:import (reitit.core Match) + (:import (reitit.core Match Routing) (clojure.lang ExceptionInfo)))) (deftest reitit-test @@ -171,4 +171,19 @@ ["/a/*b"]] true [["/v2/public/messages/dataset/bulk"] - ["/v2/public/messages/dataset/:dataset-id"]])) + ["/v2/public/messages/dataset/:dataset-id"]]) + + (testing "router with conflicting routes" + (testing "throws by default" + (is (thrown-with-msg? + ExceptionInfo + #"router contains conflicting routes" + (reitit/router + [["/a"] ["/a"]])))) + (testing "can be configured to ignore" + (is (instance? + Routing + (reitit/router + [["/a"] ["/a"]] + {:conflicts (constantly nil)})))))) +