Welcome :mixed-router (-20% on rest-test) & custom routers

This commit is contained in:
Tommi Reiman 2017-08-22 08:40:21 +03:00
parent bf0d327570
commit 3dc1cdfbe2
4 changed files with 72 additions and 28 deletions

View file

@ -400,7 +400,8 @@ Routers can be configured via options. Options allow things like [`clojure.spec`
| `:expand` | Function of `arg opts => meta` to expand route arg to route meta-data (default `reitit.core/expand`) | `: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` | `: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 | `: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!`)" | `:conflicts` | Function of `{route #{route}} => side-effect` to handle conflicting routes (default `reitit.core/throw-on-conflicts!`)
| `:router` | Function of `routes opts => router` to override the actual router implementation
## Special thanks ## Special thanks

View file

@ -120,7 +120,7 @@
["/v1/orgs/:org-id/errors" {:handler handler, :name :test/route17}] ["/v1/orgs/:org-id/errors" {:handler handler, :name :test/route17}]
["/v1/public/orgs/:org-id" {:handler handler, :name :test/route18}] ["/v1/public/orgs/:org-id" {:handler handler, :name :test/route18}]
["/v1/orgs/:org-id/invitations" {:handler handler, :name :test/route19}] ["/v1/orgs/:org-id/invitations" {:handler handler, :name :test/route19}]
["/v2/public/messages/dataset/bulk" {:handler handler, :name :test/route20}] #_["/v2/public/messages/dataset/bulk" {:handler handler, :name :test/route20}]
#_["/v1/users/:user-id/devices/bulk" {:handler handler, :name :test/route21}] #_["/v1/users/:user-id/devices/bulk" {:handler handler, :name :test/route21}]
["/v1/users/:user-id/device-errors" {:handler handler, :name :test/route22}] ["/v1/users/:user-id/device-errors" {:handler handler, :name :test/route22}]
["/v2/login" {:handler handler, :name :test/route23}] ["/v2/login" {:handler handler, :name :test/route23}]
@ -208,7 +208,7 @@
"" :test/route45}} "" :test/route45}}
"public/" {["projects/" :project-id] {"/datasets" :test/route3 "public/" {["projects/" :project-id] {"/datasets" :test/route3
"" :test/route27} "" :test/route27}
"messages/dataset/bulk" :test/route20 #_#_"messages/dataset/bulk" :test/route20
["datasets/" :dataset-id] :test/route28 ["datasets/" :dataset-id] :test/route28
["messages/dataset/" :dataset-id] :test/route53} ["messages/dataset/" :dataset-id] :test/route53}
["datasets/" :dataset-id] :test/route11 ["datasets/" :dataset-id] :test/route11
@ -268,7 +268,7 @@
"" [:test/route45 user-id]}} "" [:test/route45 user-id]}}
"public/" {["projects/" project-id] {"/datasets" [:test/route3 project-id] "public/" {["projects/" project-id] {"/datasets" [:test/route3 project-id]
"" [:test/route27 project-id]} "" [:test/route27 project-id]}
"messages/dataset/bulk" [:test/route20] #_#_"messages/dataset/bulk" [:test/route20]
["datasets/" dataset-id] [:test/route28 dataset-id] ["datasets/" dataset-id] [:test/route28 dataset-id]
["messages/dataset/" dataset-id] [:test/route53 dataset-id]} ["messages/dataset/" dataset-id] [:test/route53 dataset-id]}
["datasets/" dataset-id] [:test/route11 dataset-id] ["datasets/" dataset-id] [:test/route11 dataset-id]
@ -344,7 +344,7 @@
(context "/projects/:project-id" [] (context "/projects/:project-id" []
(ANY "/datasets" [] {:name :test/route3} handler) (ANY "/datasets" [] {:name :test/route3} handler)
(ANY "/" [] {:name :test/route27} handler)) (ANY "/" [] {:name :test/route27} handler))
(ANY "/messages/dataset/bulk" [] {:name :test/route20} handler) #_(ANY "/messages/dataset/bulk" [] {:name :test/route20} handler)
(ANY "/datasets/:dataset-id" [] {:name :test/route28} handler) (ANY "/datasets/:dataset-id" [] {:name :test/route28} handler)
(ANY "/messages/dataset/:dataset-id" [] {:name :test/route53} handler)) (ANY "/messages/dataset/:dataset-id" [] {:name :test/route53} handler))
(ANY "/datasets/:dataset-id" [] {:name :test/route11} handler) (ANY "/datasets/:dataset-id" [] {:name :test/route11} handler)
@ -376,7 +376,7 @@
["/v1/orgs/:org-id/errors" :get handler :route-name :test/route17] ["/v1/orgs/:org-id/errors" :get handler :route-name :test/route17]
["/v1/public/orgs/:org-id" :get handler :route-name :test/route18] ["/v1/public/orgs/:org-id" :get handler :route-name :test/route18]
["/v1/orgs/:org-id/invitations" :get handler :route-name :test/route19] ["/v1/orgs/:org-id/invitations" :get handler :route-name :test/route19]
["/v2/public/messages/dataset/bulk" :get handler :route-name :test/route20] #_["/v2/public/messages/dataset/bulk" :get handler :route-name :test/route20]
#_["/v1/users/:user-id/devices/bulk" :get handler :route-name :test/route21] #_["/v1/users/:user-id/devices/bulk" :get handler :route-name :test/route21]
["/v1/users/:user-id/device-errors" :get handler :route-name :test/route22] ["/v1/users/:user-id/device-errors" :get handler :route-name :test/route22]
["/v2/login" :get handler :route-name :test/route23] ["/v2/login" :get handler :route-name :test/route23]
@ -493,12 +493,12 @@
compojure-api-f #(opensensors-compojure-api-routes {:uri % :request-method :get}) compojure-api-f #(opensensors-compojure-api-routes {:uri % :request-method :get})
pedestal-f #(pedestal/find-route opensensors-pedestal-routes {:path-info % :request-method :get})] pedestal-f #(pedestal/find-route opensensors-pedestal-routes {:path-info % :request-method :get})]
(bench! routes true "reitit" reitit-f) ;; 2538ns 10% (bench! routes true "reitit" reitit-f) ;; 2538ns -> 2028ns
(bench! routes true "pedestal" pedestal-f) ;; 2737ns 11% (bench! routes true "reitit-ring" reitit-ring-f) ;; 2845ns -> 2299ns
(bench! routes true "reitit-ring" reitit-ring-f) ;; 2845ns 11% (bench! routes true "pedestal" pedestal-f) ;; 2737ns
(bench! routes true "compojure-api" compojure-api-f) ;; 10215ns 41% (bench! routes true "compojure-api" compojure-api-f) ;; 9823ns
(bench! routes true "bidi" bidi-f) ;; 19298ns 77% (bench! routes true "bidi" bidi-f) ;; 16716ns
(bench! routes true "ataraxy" ataraxy-f) ;; 24950ns 100% (bench! routes true "ataraxy" ataraxy-f) ;; 24467ns
)) ))

View file

@ -203,6 +203,37 @@
(if-let [match (impl/fast-get lookup name)] (if-let [match (impl/fast-get lookup name)]
(match params))))))) (match params)))))))
(defn mixed-router
"Creates two routers: [[lookup-router]] for static routes and
[[linear-router]] for wildcard routes. All routes should be
non-conflicting. Takes resolved routes and optional
expanded options. See [[router]] for options."
([routes]
(mixed-router routes {}))
([routes opts]
(let [{linear true, lookup false} (group-by impl/wild-route? routes)
linear-router (linear-router linear opts)
lookup-router (lookup-router lookup opts)
names (find-names routes opts)]
(reify Router
(router-type [_]
:mixed-router)
(routes [_]
routes)
(options [_]
opts)
(route-names [_]
names)
(match-by-path [_ path]
(or (match-by-path lookup-router path)
(match-by-path linear-router path)))
(match-by-name [_ name]
(or (match-by-name lookup-router name)
(match-by-name linear-router name)))
(match-by-name [_ name params]
(or (match-by-name lookup-router name params)
(match-by-name linear-router name params)))))))
(defn router (defn router
"Create a [[Router]] from raw route data and optionally an options map. "Create a [[Router]] from raw route data and optionally an options map.
If routes contain wildcards, a [[LinearRouter]] is used, otherwise a If routes contain wildcards, a [[LinearRouter]] is used, otherwise a
@ -216,12 +247,21 @@
| `:expand` | Function of `arg opts => meta` to expand route arg to route meta-data (default `reitit.core/expand`) | `: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` | `: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 | `: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!`)" | `:conflicts` | Function of `{route #{route}} => side-effect` to handle conflicting routes (default `reitit.core/throw-on-conflicts!`)
| `:router` | Function of `routes opts => router` to override the actual router implementation"
([data] ([data]
(router data {})) (router data {}))
([data opts] ([data opts]
(let [opts (meta-merge default-router-options opts) (let [{:keys [router] :as opts} (meta-merge default-router-options opts)
routes (resolve-routes data opts)] routes (resolve-routes data opts)
conflicting (conflicting-routes routes)
wilds? (some impl/wild-route? routes)
router (cond
router router
(not wilds?) lookup-router
(not conflicting) mixed-router
:else linear-router)]
(when-let [conflicts (:conflicts opts)] (when-let [conflicts (:conflicts opts)]
(when conflicting (conflicts conflicting))) (when conflicting (conflicts conflicting)))

View file

@ -1,15 +1,15 @@
(ns reitit.core-test (ns reitit.core-test
(:require [clojure.test :refer [deftest testing is are]] (:require [clojure.test :refer [deftest testing is are]]
[reitit.core :as reitit #?@(:cljs [:refer [Match]])]) [reitit.core :as reitit #?@(:cljs [:refer [Match Router]])])
#?(:clj #?(:clj
(:import (reitit.core Match) (:import (reitit.core Match Router)
(clojure.lang ExceptionInfo)))) (clojure.lang ExceptionInfo))))
(deftest reitit-test (deftest reitit-test
(testing "linear router" (testing "mixed router"
(let [router (reitit/router ["/api" ["/ipa" ["/:size" ::beer]]])] (let [router (reitit/router ["/api" ["/ipa" ["/:size" ::beer]]])]
(is (= :linear-router (reitit/router-type router))) (is (= :mixed-router (reitit/router-type router)))
(is (= [["/api/ipa/:size" {:name ::beer}]] (is (= [["/api/ipa/:size" {:name ::beer}]]
(reitit/routes router))) (reitit/routes router)))
(is (= true (map? (reitit/options router)))) (is (= true (map? (reitit/options router))))
@ -106,6 +106,13 @@
(is handler) (is handler)
(is (= "ok" (handler))))))) (is (= "ok" (handler)))))))
(testing "custom router"
(let [router (reitit/router ["/ping"] {:router (fn [_ _]
(reify Router
(reitit/router-type [_]
::custom)))})]
(is (= ::custom (reitit/router-type router)))))
(testing "bide sample" (testing "bide sample"
(let [routes [["/auth/login" :auth/login] (let [routes [["/auth/login" :auth/login]
["/auth/recovery/token/:token" :auth/recovery] ["/auth/recovery/token/:token" :auth/recovery]
@ -175,10 +182,10 @@
(testing "all conflicts are returned" (testing "all conflicts are returned"
(is (= {["/a" {}] #{["/*d" {}] ["/:b" {}]}, (is (= {["/a" {}] #{["/*d" {}] ["/:b" {}]},
["/:b" {}] #{["/c" {}] ["/*d" {}]}, ["/:b" {}] #{["/c" {}] ["/*d" {}]},
["/c" {}] #{["/*d" {}]}})) ["/c" {}] #{["/*d" {}]}}
(-> [["/a"] ["/:b"] ["/c"] ["/*d"]] (-> [["/a"] ["/:b"] ["/c"] ["/*d"]]
(reitit/resolve-routes {}) (reitit/resolve-routes {})
(reitit/conflicting-routes))) (reitit/conflicting-routes)))))
(testing "router with conflicting routes" (testing "router with conflicting routes"
(testing "throws by default" (testing "throws by default"
@ -188,8 +195,4 @@
(reitit/router (reitit/router
[["/a"] ["/a"]])))) [["/a"] ["/a"]]))))
(testing "can be configured to ignore" (testing "can be configured to ignore"
(is (not (is (not (nil? (reitit/router [["/a"] ["/a"]] {:conflicts (constantly nil)})))))))
(nil?
(reitit/router
[["/a"] ["/a"]]
{:conflicts (constantly nil)})))))))