mirror of
https://github.com/metosin/reitit.git
synced 2026-02-06 12:03:13 +00:00
Welcome segment-router!
This commit is contained in:
parent
102fd35f04
commit
5d7670de60
3 changed files with 95 additions and 33 deletions
|
|
@ -2,6 +2,7 @@
|
||||||
(:require [meta-merge.core :refer [meta-merge]]
|
(:require [meta-merge.core :refer [meta-merge]]
|
||||||
[clojure.string :as str]
|
[clojure.string :as str]
|
||||||
[reitit.trie :as trie]
|
[reitit.trie :as trie]
|
||||||
|
[reitit.segment :as segment]
|
||||||
[reitit.impl :as impl #?@(:cljs [:refer [Route]])])
|
[reitit.impl :as impl #?@(:cljs [:refer [Route]])])
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(:import (reitit.impl Route))))
|
(:import (reitit.impl Route))))
|
||||||
|
|
@ -260,6 +261,47 @@
|
||||||
(if-let [match (impl/fast-get lookup name)]
|
(if-let [match (impl/fast-get lookup name)]
|
||||||
(match params)))))))
|
(match params)))))))
|
||||||
|
|
||||||
|
(defn segment-router
|
||||||
|
"Creates a special prefix-tree style segment router from resolved routes and optional
|
||||||
|
expanded options. See [[router]] for available options"
|
||||||
|
([routes]
|
||||||
|
(segment-router routes {}))
|
||||||
|
([routes opts]
|
||||||
|
(let [compiled (compile-routes routes opts)
|
||||||
|
names (find-names routes opts)
|
||||||
|
[pl nl] (reduce
|
||||||
|
(fn [[pl nl] [p {:keys [name] :as data} result]]
|
||||||
|
(let [{:keys [params] :as route} (impl/create [p data result])
|
||||||
|
f #(if-let [path (impl/path-for route %)]
|
||||||
|
(->Match p data result % path)
|
||||||
|
(->PartialMatch p data result % params))]
|
||||||
|
[(segment/insert pl p (->Match p data result nil nil))
|
||||||
|
(if name (assoc nl name f) nl)]))
|
||||||
|
[nil {}] compiled)
|
||||||
|
lookup (impl/fast-map nl)]
|
||||||
|
^{:type ::router}
|
||||||
|
(reify
|
||||||
|
Router
|
||||||
|
(router-name [_]
|
||||||
|
:segment-router)
|
||||||
|
(routes [_]
|
||||||
|
compiled)
|
||||||
|
(options [_]
|
||||||
|
opts)
|
||||||
|
(route-names [_]
|
||||||
|
names)
|
||||||
|
(match-by-path [_ path]
|
||||||
|
(if-let [match (segment/lookup pl path)]
|
||||||
|
(-> (:data match)
|
||||||
|
(assoc :params (:params match))
|
||||||
|
(assoc :path path))))
|
||||||
|
(match-by-name [_ name]
|
||||||
|
(if-let [match (impl/fast-get lookup name)]
|
||||||
|
(match nil)))
|
||||||
|
(match-by-name [_ name params]
|
||||||
|
(if-let [match (impl/fast-get lookup name)]
|
||||||
|
(match params)))))))
|
||||||
|
|
||||||
(defn single-static-path-router
|
(defn single-static-path-router
|
||||||
"Creates a fast router of 1 static route(s) and optional
|
"Creates a fast router of 1 static route(s) and optional
|
||||||
expanded options. See [[router]] for available options"
|
expanded options. See [[router]] for available options"
|
||||||
|
|
|
||||||
|
|
@ -39,11 +39,14 @@
|
||||||
(some #(-lookup (impl/fast-get children' %) ps (assoc params % p)) wilds)
|
(some #(-lookup (impl/fast-get children' %) ps (assoc params % p)) wilds)
|
||||||
(-catch-all catch-all data params p ps))))))))
|
(-catch-all catch-all data params p ps))))))))
|
||||||
|
|
||||||
|
(defn insert [root path data]
|
||||||
|
(-insert (or root (segment)) (impl/segments path) (map->Match {:data data})))
|
||||||
|
|
||||||
(defn create [paths]
|
(defn create [paths]
|
||||||
(reduce
|
(reduce
|
||||||
(fn [segment [p data]]
|
(fn [segment [p data]]
|
||||||
(-insert segment (impl/segments p) (map->Match {:data data})))
|
(insert segment p data))
|
||||||
(segment) paths))
|
nil paths))
|
||||||
|
|
||||||
(defn lookup [segment ^String path]
|
(defn lookup [segment ^String path]
|
||||||
(-lookup segment (.split path "/") {}))
|
(-lookup segment (.split path "/") {}))
|
||||||
|
|
|
||||||
|
|
@ -9,41 +9,57 @@
|
||||||
|
|
||||||
(testing "routers handling wildcard paths"
|
(testing "routers handling wildcard paths"
|
||||||
(are [r name]
|
(are [r name]
|
||||||
(let [router (r/router ["/api" ["/ipa" ["/:size" ::beer]]] {:router r})]
|
(testing "wild"
|
||||||
(is (= name (r/router-name router)))
|
|
||||||
(is (= [["/api/ipa/:size" {:name ::beer} nil]]
|
|
||||||
(r/routes router)))
|
|
||||||
(is (= true (map? (r/options router))))
|
|
||||||
(is (= (r/map->Match
|
|
||||||
{:template "/api/ipa/:size"
|
|
||||||
:data {:name ::beer}
|
|
||||||
:path "/api/ipa/large"
|
|
||||||
:params {:size "large"}})
|
|
||||||
(r/match-by-path router "/api/ipa/large")))
|
|
||||||
(is (= (r/map->Match
|
|
||||||
{:template "/api/ipa/:size"
|
|
||||||
:data {:name ::beer}
|
|
||||||
:path "/api/ipa/large"
|
|
||||||
:params {:size "large"}})
|
|
||||||
(r/match-by-name router ::beer {:size "large"})))
|
|
||||||
(is (= nil (r/match-by-name router "ILLEGAL")))
|
|
||||||
(is (= [::beer] (r/route-names router)))
|
|
||||||
|
|
||||||
(testing "name-based routing with missing parameters"
|
(testing "simple"
|
||||||
(is (= (r/map->PartialMatch
|
(let [router (r/router ["/api" ["/ipa" ["/:size" ::beer]]] {:router r})]
|
||||||
{:template "/api/ipa/:size"
|
(is (= name (r/router-name router)))
|
||||||
:data {:name ::beer}
|
(is (= [["/api/ipa/:size" {:name ::beer} nil]]
|
||||||
:required #{:size}
|
(r/routes router)))
|
||||||
:params nil})
|
(is (= true (map? (r/options router))))
|
||||||
(r/match-by-name router ::beer)))
|
(is (= (r/map->Match
|
||||||
(is (= true (r/partial-match? (r/match-by-name router ::beer))))
|
{:template "/api/ipa/:size"
|
||||||
(is (thrown-with-msg?
|
:data {:name ::beer}
|
||||||
ExceptionInfo
|
:path "/api/ipa/large"
|
||||||
#"^missing path-params for route /api/ipa/:size -> \#\{:size\}$"
|
:params {:size "large"}})
|
||||||
(r/match-by-name! router ::beer)))))
|
(r/match-by-path router "/api/ipa/large")))
|
||||||
|
(is (= (r/map->Match
|
||||||
|
{:template "/api/ipa/:size"
|
||||||
|
:data {:name ::beer}
|
||||||
|
:path "/api/ipa/large"
|
||||||
|
:params {:size "large"}})
|
||||||
|
(r/match-by-name router ::beer {:size "large"})))
|
||||||
|
(is (= nil (r/match-by-name router "ILLEGAL")))
|
||||||
|
(is (= [::beer] (r/route-names router)))
|
||||||
|
|
||||||
|
(testing "name-based routing with missing parameters"
|
||||||
|
(is (= (r/map->PartialMatch
|
||||||
|
{:template "/api/ipa/:size"
|
||||||
|
:data {:name ::beer}
|
||||||
|
:required #{:size}
|
||||||
|
:params nil})
|
||||||
|
(r/match-by-name router ::beer)))
|
||||||
|
(is (= true (r/partial-match? (r/match-by-name router ::beer))))
|
||||||
|
(is (thrown-with-msg?
|
||||||
|
ExceptionInfo
|
||||||
|
#"^missing path-params for route /api/ipa/:size -> \#\{:size\}$"
|
||||||
|
(r/match-by-name! router ::beer))))))
|
||||||
|
|
||||||
|
(testing "complex"
|
||||||
|
(let [router (r/router
|
||||||
|
[["/:abba" ::abba]
|
||||||
|
["/abba/1" ::abba1]
|
||||||
|
["/:abba/:dabba/doo" ::doo]
|
||||||
|
["/abba/:dabba/boo" ::boo]] {:router r})
|
||||||
|
matches #(-> router (r/match-by-path %) :data :name)]
|
||||||
|
(is (= ::abba (matches "/abba")))
|
||||||
|
(is (= ::abba1 (matches "/abba/1")))
|
||||||
|
(is (= ::doo (matches "/abba/1/doo")))
|
||||||
|
(is (= ::boo (matches "/abba/1/boo"))))))
|
||||||
|
|
||||||
r/linear-router :linear-router
|
r/linear-router :linear-router
|
||||||
r/prefix-tree-router :prefix-tree-router
|
r/prefix-tree-router :prefix-tree-router
|
||||||
|
r/segment-router :segment-router
|
||||||
r/mixed-router :mixed-router))
|
r/mixed-router :mixed-router))
|
||||||
|
|
||||||
(testing "routers handling static paths"
|
(testing "routers handling static paths"
|
||||||
|
|
@ -80,6 +96,7 @@
|
||||||
r/single-static-path-router :single-static-path-router
|
r/single-static-path-router :single-static-path-router
|
||||||
r/linear-router :linear-router
|
r/linear-router :linear-router
|
||||||
r/prefix-tree-router :prefix-tree-router
|
r/prefix-tree-router :prefix-tree-router
|
||||||
|
r/segment-router :segment-router
|
||||||
r/mixed-router :mixed-router))
|
r/mixed-router :mixed-router))
|
||||||
|
|
||||||
(testing "route coercion & compilation"
|
(testing "route coercion & compilation"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue