mirror of
https://github.com/metosin/reitit.git
synced 2025-12-17 16:31:11 +00:00
commit
5646494388
4 changed files with 128 additions and 39 deletions
|
|
@ -96,8 +96,9 @@
|
||||||
(call)))
|
(call)))
|
||||||
|
|
||||||
;; 1.0µs (-94%)
|
;; 1.0µs (-94%)
|
||||||
|
;; 770ns (-95%, -23%)
|
||||||
(title "reitit")
|
(title "reitit")
|
||||||
(let [call #(reitit/match-route reitit-routes "/workspace/1/1")]
|
(let [call #(reitit/match reitit-routes "/workspace/1/1")]
|
||||||
(assert (call))
|
(assert (call))
|
||||||
(cc/quick-bench
|
(cc/quick-bench
|
||||||
(call))))
|
(call))))
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
(ns reitit.core
|
(ns reitit.core
|
||||||
(:require [meta-merge.core :refer [meta-merge]]
|
(:require [meta-merge.core :refer [meta-merge]]
|
||||||
[reitit.regex :as regex]))
|
[reitit.impl :as impl #?@(:cljs [:refer [Route]])])
|
||||||
|
#?(:clj
|
||||||
|
(:import (reitit.impl Route))))
|
||||||
|
|
||||||
(defprotocol Expand
|
(defprotocol Expand
|
||||||
(expand [this]))
|
(expand [this]))
|
||||||
|
|
@ -57,31 +59,65 @@
|
||||||
(->> (walk data opts) (map-meta merge-meta)))
|
(->> (walk data opts) (map-meta merge-meta)))
|
||||||
|
|
||||||
(defprotocol Routing
|
(defprotocol Routing
|
||||||
(match-route [this path])
|
(routes [this])
|
||||||
(path-for [this name] [this name parameters]))
|
(match [this path])
|
||||||
|
(by-name [this name] [this name parameters]))
|
||||||
|
|
||||||
(defrecord LinearRouter [routes]
|
(defrecord Match [template meta path params])
|
||||||
|
|
||||||
|
(defrecord LinearRouter [routes data lookup]
|
||||||
Routing
|
Routing
|
||||||
(match-route [_ path]
|
(routes [_]
|
||||||
|
routes)
|
||||||
|
(match [_ path]
|
||||||
(reduce
|
(reduce
|
||||||
(fn [acc [p m matcher]]
|
(fn [acc ^Route route]
|
||||||
(if-let [params (matcher path)]
|
(if-let [params ((:matcher route) path)]
|
||||||
(reduced (assoc m :route-params params))))
|
(reduced (->Match (:path route) (:meta route) path params))))
|
||||||
nil routes)))
|
nil data))
|
||||||
|
(by-name [_ name]
|
||||||
|
((lookup name) nil))
|
||||||
|
(by-name [_ name params]
|
||||||
|
((lookup name) params)))
|
||||||
|
|
||||||
(defrecord LookupRouter [routes]
|
(defn linear-router [routes]
|
||||||
|
(->LinearRouter
|
||||||
|
routes
|
||||||
|
(mapv (partial apply impl/create) routes)
|
||||||
|
(->> (for [[p {:keys [name] :as meta}] routes
|
||||||
|
:when name
|
||||||
|
:let [route (impl/create p meta)]]
|
||||||
|
[name (fn [params]
|
||||||
|
(->Match p meta (impl/path-for route params) params))])
|
||||||
|
(into {}))))
|
||||||
|
|
||||||
|
(defrecord LookupRouter [routes data lookup]
|
||||||
Routing
|
Routing
|
||||||
(match-route [_ path]
|
(routes [_]
|
||||||
(routes path)))
|
routes)
|
||||||
|
(match [_ path]
|
||||||
|
(data path))
|
||||||
|
(by-name [_ name]
|
||||||
|
((lookup name) nil))
|
||||||
|
(by-name [_ name params]
|
||||||
|
((lookup name) params)))
|
||||||
|
|
||||||
|
(defn lookup-router [routes]
|
||||||
|
(->LookupRouter
|
||||||
|
routes
|
||||||
|
(->> (for [[p meta] routes]
|
||||||
|
[p (->Match p meta p {})])
|
||||||
|
(into {}))
|
||||||
|
(->> (for [[p {:keys [name] :as meta}] routes
|
||||||
|
:when name]
|
||||||
|
[name (fn [params]
|
||||||
|
(->Match p meta p params))])
|
||||||
|
(into {}))))
|
||||||
|
|
||||||
(defn router
|
(defn router
|
||||||
([data]
|
([data]
|
||||||
(router data {}))
|
(router data {}))
|
||||||
([data opts]
|
([data opts]
|
||||||
(let [routes (resolve-routes data opts)]
|
(let [routes (resolve-routes data opts)]
|
||||||
(if (some regex/contains-wilds? (map first routes))
|
((if (some impl/contains-wilds? (map first routes))
|
||||||
(->LinearRouter
|
linear-router lookup-router) routes))))
|
||||||
(for [[p m] routes]
|
|
||||||
[p m (regex/matcher p)]))
|
|
||||||
(->LookupRouter
|
|
||||||
(into {} routes))))))
|
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,9 @@
|
||||||
;
|
;
|
||||||
; You must not remove this notice, or any other, from this software.
|
; You must not remove this notice, or any other, from this software.
|
||||||
|
|
||||||
(ns reitit.regex
|
(ns reitit.impl
|
||||||
(:require [clojure.string :as str])
|
(:require [clojure.string :as str]
|
||||||
|
[clojure.set :as set])
|
||||||
(:import #?(:clj (java.util.regex Pattern))))
|
(:import #?(:clj (java.util.regex Pattern))))
|
||||||
|
|
||||||
;;
|
;;
|
||||||
|
|
@ -97,12 +98,34 @@
|
||||||
(wild? (first parts)))))
|
(wild? (first parts)))))
|
||||||
|
|
||||||
;;
|
;;
|
||||||
;; Routing
|
;; Routing (c) Metosin
|
||||||
;;
|
;;
|
||||||
|
|
||||||
(defn matcher [path]
|
(defrecord Route [path matcher parts params meta])
|
||||||
|
|
||||||
|
(defn create [path meta]
|
||||||
(if (contains-wilds? path)
|
(if (contains-wilds? path)
|
||||||
(as-> (parse-path path) $
|
(as-> (parse-path path) $
|
||||||
(assoc $ :path-re (path-regex $))
|
(assoc $ :path-re (path-regex $))
|
||||||
(path-matcher $))
|
(merge $ {:path path
|
||||||
#(if (= path %) {})))
|
:matcher (path-matcher $)
|
||||||
|
:meta meta})
|
||||||
|
(dissoc $ :path-re :path-constraints)
|
||||||
|
(update $ :path-params set)
|
||||||
|
(set/rename-keys $ {:path-parts :parts
|
||||||
|
:path-params :params})
|
||||||
|
(map->Route $))
|
||||||
|
(map->Route {:path path
|
||||||
|
:meta meta
|
||||||
|
:matcher #(if (= path %) {})})))
|
||||||
|
|
||||||
|
(defn path-for [^Route route params]
|
||||||
|
(when-not (every? #(contains? params %) (:params route))
|
||||||
|
(let [defined (-> params keys set)
|
||||||
|
required (:params route)
|
||||||
|
missing (clojure.set/difference required defined)]
|
||||||
|
(throw
|
||||||
|
(ex-info
|
||||||
|
(str "missing path-params for route '" (:path route) "': " missing)
|
||||||
|
{:params params, :required required}))))
|
||||||
|
(str "/" (str/join \/ (map #(get params % %) (:parts route)))))
|
||||||
|
|
@ -1,25 +1,51 @@
|
||||||
(ns reitit.core-test
|
(ns reitit.core-test
|
||||||
(:require [clojure.test :refer [deftest testing is are]]
|
(:require [clojure.test :refer [deftest testing is are]]
|
||||||
#?(:clj [reitit.core :as reitit]
|
[reitit.core :as reitit #?@(:cljs [:refer [Match LinearRouter LookupRouter]])])
|
||||||
:cljs [reitit.core :as reitit :refer [LinearRouter LookupRouter]]))
|
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(:import (reitit.core LinearRouter LookupRouter))))
|
(:import (reitit.core Match LinearRouter LookupRouter)
|
||||||
|
(clojure.lang ExceptionInfo))))
|
||||||
|
|
||||||
(deftest reitit-test
|
(deftest reitit-test
|
||||||
|
|
||||||
(testing "linear router"
|
(testing "linear router"
|
||||||
(let [router (reitit/router ["/api"
|
(let [router (reitit/router ["/api" ["/ipa" ["/:size" ::beer]]])]
|
||||||
["/ipa"
|
|
||||||
["/:size"]]])]
|
|
||||||
(is (instance? LinearRouter router))
|
(is (instance? LinearRouter router))
|
||||||
(is (map? (reitit/match-route router "/api/ipa/large")))))
|
(is (= [["/api/ipa/:size" {:name ::beer}]]
|
||||||
|
(reitit/routes router)))
|
||||||
|
(is (= (reitit/map->Match
|
||||||
|
{:template "/api/ipa/:size"
|
||||||
|
:meta {:name ::beer}
|
||||||
|
:path "/api/ipa/large"
|
||||||
|
:params {:size "large"}})
|
||||||
|
(reitit/match router "/api/ipa/large")))
|
||||||
|
(is (= (reitit/map->Match
|
||||||
|
{:template "/api/ipa/:size"
|
||||||
|
:meta {:name ::beer}
|
||||||
|
:path "/api/ipa/large"
|
||||||
|
:params {:size "large"}})
|
||||||
|
(reitit/by-name router ::beer {:size "large"})))
|
||||||
|
(is (thrown-with-msg?
|
||||||
|
ExceptionInfo
|
||||||
|
#"^missing path-params for route '/api/ipa/:size': \#\{:size\}$"
|
||||||
|
(reitit/by-name router ::beer)))))
|
||||||
|
|
||||||
(testing "lookup router"
|
(testing "lookup router"
|
||||||
(let [router (reitit/router ["/api"
|
(let [router (reitit/router ["/api" ["/ipa" ["/large" ::beer]]])]
|
||||||
["/ipa"
|
|
||||||
["/large"]]])]
|
|
||||||
(is (instance? LookupRouter router))
|
(is (instance? LookupRouter router))
|
||||||
(is (map? (reitit/match-route router "/api/ipa/large")))))
|
(is (= [["/api/ipa/large" {:name ::beer}]]
|
||||||
|
(reitit/routes router)))
|
||||||
|
(is (= (reitit/map->Match
|
||||||
|
{:template "/api/ipa/large"
|
||||||
|
:meta {:name ::beer}
|
||||||
|
:path "/api/ipa/large"
|
||||||
|
:params {}})
|
||||||
|
(reitit/match router "/api/ipa/large")))
|
||||||
|
(is (= (reitit/map->Match
|
||||||
|
{:template "/api/ipa/large"
|
||||||
|
:meta {:name ::beer}
|
||||||
|
:path "/api/ipa/large"
|
||||||
|
:params {:size "large"}})
|
||||||
|
(reitit/by-name router ::beer {:size "large"})))))
|
||||||
|
|
||||||
(testing "bide sample"
|
(testing "bide sample"
|
||||||
(let [routes [["/auth/login" :auth/login]
|
(let [routes [["/auth/login" :auth/login]
|
||||||
|
|
@ -47,7 +73,10 @@
|
||||||
["/api/admin/db" {:mw [:api :admin :db], :roles #{:admin}}]]
|
["/api/admin/db" {:mw [:api :admin :db], :roles #{:admin}}]]
|
||||||
router (reitit/router routes)]
|
router (reitit/router routes)]
|
||||||
(is (= expected (reitit/resolve-routes routes {})))
|
(is (= expected (reitit/resolve-routes routes {})))
|
||||||
(is (= {:mw [:api], :parameters {:id String, :sub-id String}
|
(is (= (reitit/map->Match
|
||||||
:route-params {:id "1", :sub-id "2"}}
|
{:template "/api/user/:id/:sub-id"
|
||||||
(reitit/match-route router "/api/user/1/2"))))))
|
:meta {:mw [:api], :parameters {:id String, :sub-id String}}
|
||||||
|
:path "/api/user/1/2"
|
||||||
|
:params {:id "1", :sub-id "2"}})
|
||||||
|
(reitit/match router "/api/user/1/2"))))))
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue