Implement trailing slash handler as custom Router

This commit is contained in:
Juho Teperi 2019-09-20 11:03:19 +03:00
parent 54a26aded7
commit 5ab3e26f16
2 changed files with 55 additions and 33 deletions

View file

@ -15,21 +15,37 @@
(map (juxt keyword #(.get q %))) (map (juxt keyword #(.get q %)))
(into {})))) (into {}))))
(defn trailing-slash-router [parent method]
(if method
^{:type ::r/router}
(reify r/Router
(router-name [_]
:trailing-slash-handler)
(routes [_]
(r/routes parent))
(compiled-routes [_]
(r/compiled-routes parent))
(options [_]
(r/options parent))
(route-names [_]
(r/route-names parent))
(match-by-path [_ path]
(or (r/match-by-path parent path)
(if (str/ends-with? path "/")
(if (not= method :add)
(r/match-by-path parent (subs path 0 (dec (count path)))))
(if (not= method :remove)
(r/match-by-path parent (str path "/"))))))
(match-by-name [_ name]
(r/match-by-name parent name)))
parent))
(defn match-by-path (defn match-by-path
"Given routing tree and current path, return match with possibly "Given routing tree and current path, return match with possibly
coerced parameters. Returns nil if no match found." coerced parameters. Returns nil if no match found."
[router path] [router path]
(let [uri (.parse Uri path) (let [uri (.parse Uri path)]
path (.getPath uri)] (if-let [match (r/match-by-path router (.getPath uri))]
(if-let [match (or (r/match-by-path router path)
(if-let [trailing-slash-handling (:trailing-slash-handling (r/options router))]
;; TODO: Maybe the original path should be added under some key in match,
;; so it is easy for user to see if trailing slash "redirect" happened?
(if (str/ends-with? path "/")
(if (not= trailing-slash-handling :add)
(r/match-by-path router (subs path 0 (dec (count path)))))
(if (not= trailing-slash-handling :remove)
(r/match-by-path router (str path "/"))))))]
;; User can update browser location in on-navigate call using replace-state ;; User can update browser location in on-navigate call using replace-state
(let [q (query-params uri) (let [q (query-params uri)
match (assoc match :query-params q) match (assoc match :query-params q)
@ -62,7 +78,8 @@
([raw-routes] ([raw-routes]
(router raw-routes {})) (router raw-routes {}))
([raw-routes opts] ([raw-routes opts]
(r/router raw-routes (merge {:compile rc/compile-request-coercers} opts)))) (-> (r/router raw-routes (merge {:compile rc/compile-request-coercers} opts))
(trailing-slash-router (:trailing-slash-handling opts)))))
(defn match-by-name! (defn match-by-name!
"Logs problems using console.warn" "Logs problems using console.warn"

View file

@ -12,10 +12,11 @@
(deftest match-by-path-test (deftest match-by-path-test
(testing "simple" (testing "simple"
(let [router (r/router ["/" (let [router (rf/router
["" ::frontpage] ["/"
["foo" ::foo] ["" ::frontpage]
["bar" ::bar]])] ["foo" ::foo]
["bar" ::bar]])]
(is (= (r/map->Match (is (= (r/map->Match
{:template "/" {:template "/"
:data {:name ::frontpage} :data {:name ::frontpage}
@ -51,10 +52,11 @@
(rf/match-by-name! router ::asd))))))))) (rf/match-by-name! router ::asd)))))))))
(testing "schema coercion" (testing "schema coercion"
(let [router (r/router ["/" (let [router (rf/router
[":id" {:name ::foo ["/"
:parameters {:path {:id s/Int} [":id" {:name ::foo
:query {(s/optional-key :mode) s/Keyword}}}]] :parameters {:path {:id s/Int}
:query {(s/optional-key :mode) s/Keyword}}}]]
{:compile rc/compile-request-coercers {:compile rc/compile-request-coercers
:data {:coercion rsc/coercion}})] :data {:coercion rsc/coercion}})]
@ -108,11 +110,12 @@
(deftest trailing-slash-handling-test (deftest trailing-slash-handling-test
(testing ":both" (testing ":both"
(let [router (r/router ["/" (let [router (rf/router
["" ::frontpage] ["/"
["foo" ::foo] ["" ::frontpage]
["bar/" ::bar]] ["foo" ::foo]
{:trailing-slash-handling :both})] ["bar/" ::bar]]
{:trailing-slash-handling :both})]
(is (= (r/map->Match (is (= (r/map->Match
{:template "/foo" {:template "/foo"
:data {:name ::foo} :data {:name ::foo}
@ -136,10 +139,11 @@
(rf/match-by-path router "/bar"))) )) (rf/match-by-path router "/bar"))) ))
(testing ":add" (testing ":add"
(let [router (r/router ["/" (let [router (rf/router
["" ::frontpage] ["/"
["foo" ::foo] ["" ::frontpage]
["bar/" ::bar]] ["foo" ::foo]
["bar/" ::bar]]
{:trailing-slash-handling :add})] {:trailing-slash-handling :add})]
(is (= (r/map->Match (is (= (r/map->Match
{:template "/foo" {:template "/foo"
@ -164,10 +168,11 @@
(rf/match-by-path router "/bar"))))) (rf/match-by-path router "/bar")))))
(testing ":remove" (testing ":remove"
(let [router (r/router ["/" (let [router (rf/router
["" ::frontpage] ["/"
["foo" ::foo] ["" ::frontpage]
["bar/" ::bar]] ["foo" ::foo]
["bar/" ::bar]]
{:trailing-slash-handling :remove})] {:trailing-slash-handling :remove})]
(is (= (r/map->Match (is (= (r/map->Match
{:template "/foo" {:template "/foo"