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 %)))
(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
"Given routing tree and current path, return match with possibly
coerced parameters. Returns nil if no match found."
[router path]
(let [uri (.parse Uri path)
path (.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 "/"))))))]
(let [uri (.parse Uri path)]
(if-let [match (r/match-by-path router (.getPath uri))]
;; User can update browser location in on-navigate call using replace-state
(let [q (query-params uri)
match (assoc match :query-params q)
@ -62,7 +78,8 @@
([raw-routes]
(router raw-routes {}))
([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!
"Logs problems using console.warn"

View file

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