Welcome segment-router!

This commit is contained in:
Tommi Reiman 2017-11-24 09:01:05 +02:00
parent 102fd35f04
commit 5d7670de60
3 changed files with 95 additions and 33 deletions

View file

@ -2,6 +2,7 @@
(:require [meta-merge.core :refer [meta-merge]]
[clojure.string :as str]
[reitit.trie :as trie]
[reitit.segment :as segment]
[reitit.impl :as impl #?@(:cljs [:refer [Route]])])
#?(:clj
(:import (reitit.impl Route))))
@ -260,6 +261,47 @@
(if-let [match (impl/fast-get lookup name)]
(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
"Creates a fast router of 1 static route(s) and optional
expanded options. See [[router]] for available options"

View file

@ -39,11 +39,14 @@
(some #(-lookup (impl/fast-get children' %) ps (assoc params % p)) wilds)
(-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]
(reduce
(fn [segment [p data]]
(-insert segment (impl/segments p) (map->Match {:data data})))
(segment) paths))
(insert segment p data))
nil paths))
(defn lookup [segment ^String path]
(-lookup segment (.split path "/") {}))

View file

@ -9,41 +9,57 @@
(testing "routers handling wildcard paths"
(are [r name]
(let [router (r/router ["/api" ["/ipa" ["/:size" ::beer]]] {:router r})]
(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 "wild"
(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 "simple"
(let [router (r/router ["/api" ["/ipa" ["/:size" ::beer]]] {:router r})]
(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"
(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/prefix-tree-router :prefix-tree-router
r/segment-router :segment-router
r/mixed-router :mixed-router))
(testing "routers handling static paths"
@ -80,6 +96,7 @@
r/single-static-path-router :single-static-path-router
r/linear-router :linear-router
r/prefix-tree-router :prefix-tree-router
r/segment-router :segment-router
r/mixed-router :mixed-router))
(testing "route coercion & compilation"