Better handling of nil in routing

This commit is contained in:
Tommi Reiman 2018-06-04 10:30:48 +03:00
parent 56203ba11d
commit 7a544cd52d
5 changed files with 43 additions and 28 deletions

View file

@ -2,14 +2,17 @@
### `reitit-core` ### `reitit-core`
* Better handling of `nil` routes - they filtered away from route syntax before routes are expanded: * Better handling of `nil` in route syntax:
* explicit `nil` after path string is always handled as `nil` route
* `nil` as path string causes the whole route to be `nil`
* `nil` as child route is stripped away
```clj ```clj
(testing "nil routes are allowed ans stripped" (testing "nil routes are stripped"
(is (= [] (r/routes (r/router nil)))) (is (= [] (r/routes (r/router nil))))
(is (= [] (r/routes (r/router [nil ["/ping"]]))))
(is (= [] (r/routes (r/router [nil [nil] [[nil nil nil]]])))) (is (= [] (r/routes (r/router [nil [nil] [[nil nil nil]]]))))
(is (= [["/ping" {} nil]] (r/routes (r/router [nil [nil] ["/ping"]])))) (is (= [] (r/routes (r/router ["/ping" [nil "/pong"]])))))
(is (= [["/ping" {} nil]] (r/routes (r/router [[[nil [nil] ["/ping"]]]])))))
``` ```
### `reitit-schema` ### `reitit-schema`

View file

@ -37,17 +37,16 @@
[(walk-many [p m r] [(walk-many [p m r]
(reduce #(into %1 (walk-one p m %2)) [] r)) (reduce #(into %1 (walk-one p m %2)) [] r))
(walk-one [pacc macc routes] (walk-one [pacc macc routes]
(if-let [routes (seq (keep identity routes))] (if (vector? (first routes))
(if (vector? (first routes)) (walk-many pacc macc routes)
(walk-many pacc macc routes) (if (string? (first routes))
(let [[path & [maybe-arg :as args]] routes (let [[path & [maybe-arg :as args]] routes
[data childs] (if (vector? maybe-arg) [data childs] (if (or (vector? maybe-arg) (nil? maybe-arg))
[{} args] [{} args]
[maybe-arg (rest args)]) [maybe-arg (rest args)])
macc (into macc (expand data opts))] macc (into macc (expand data opts))
(if (seq childs) child-routes (walk-many (str pacc path) macc (keep identity childs))]
(walk-many (str pacc path) macc childs) (if (seq childs) (seq child-routes) [[(str pacc path) macc]])))))]
[[(str pacc path) macc]])))))]
(walk-one path (mapv identity data) raw-routes))) (walk-one path (mapv identity data) raw-routes)))
(defn map-data [f routes] (defn map-data [f routes]

View file

@ -9,18 +9,19 @@
(s/def ::path (s/with-gen string? #(gen/fmap (fn [s] (str "/" s)) (s/gen string?)))) (s/def ::path (s/with-gen string? #(gen/fmap (fn [s] (str "/" s)) (s/gen string?))))
(s/def ::arg (s/and any? (complement vector?))) (s/def ::arg (s/and some? (complement vector?)))
(s/def ::data (s/map-of keyword? any?)) (s/def ::data (s/map-of keyword? any?))
(s/def ::result any?) (s/def ::result any?)
(s/def ::raw-route (s/def ::raw-route
(s/cat :path ::path (s/nilable
:arg (s/? ::arg) (s/cat :path ::path
:childs (s/* (s/and (s/nilable ::raw-route))))) :arg (s/? ::arg)
:childs (s/* (s/and (s/nilable ::raw-routes))))))
(s/def ::raw-routes (s/def ::raw-routes
(s/or :route ::raw-route (s/or :route ::raw-route
:routes (s/coll-of ::raw-route :into []))) :routes (s/coll-of ::raw-routes :into [])))
(s/def ::route (s/def ::route
(s/cat :path ::path (s/cat :path ::path

View file

@ -108,11 +108,11 @@
r/segment-router :segment-router r/segment-router :segment-router
r/mixed-router :mixed-router)) r/mixed-router :mixed-router))
(testing "nil routes are allowed ans stripped" (testing "nil routes are stripped"
(is (= [] (r/routes (r/router nil)))) (is (= [] (r/routes (r/router nil))))
(is (= [] (r/routes (r/router [nil ["/ping"]]))))
(is (= [] (r/routes (r/router [nil [nil] [[nil nil nil]]])))) (is (= [] (r/routes (r/router [nil [nil] [[nil nil nil]]]))))
(is (= [["/ping" {} nil]] (r/routes (r/router [nil [nil] ["/ping"]])))) (is (= [] (r/routes (r/router ["/ping" [nil "/pong"]])))))
(is (= [["/ping" {} nil]] (r/routes (r/router [[[nil [nil] ["/ping"]]]])))))
(testing "route coercion & compilation" (testing "route coercion & compilation"
@ -246,3 +246,9 @@
[["/a"] ["/a"]])))) [["/a"] ["/a"]]))))
(testing "can be configured to ignore" (testing "can be configured to ignore"
(is (not (nil? (r/router [["/a"] ["/a"]] {:conflicts (constantly nil)}))))))) (is (not (nil? (r/router [["/a"] ["/a"]] {:conflicts (constantly nil)})))))))
(testing "nil routes are stripped"
(is (= [] (r/routes (r/router nil))))
(is (= [] (r/routes (r/router [nil ["/ping"]]))))
(is (= [] (r/routes (r/router [nil [nil] [[nil nil nil]]]))))
(is (= [] (r/routes (r/router ["/ping" [nil "/pong"]])))))

View file

@ -1,14 +1,20 @@
(ns reitit.spec-test (ns reitit.spec-test
(:require [clojure.test :refer [deftest testing is are]] (:require [clojure.test :refer [deftest testing is are use-fixtures]]
[#?(:clj clojure.spec.test.alpha :cljs cljs.spec.test.alpha) :as stest] [#?(:clj clojure.spec.test.alpha :cljs cljs.spec.test.alpha) :as stest]
[clojure.spec.alpha :as s] [clojure.spec.alpha :as s]
[reitit.core :as r] [reitit.core :as r]
[reitit.spec :as rs] [reitit.spec :as rs])
[expound.alpha :as e])
#?(:clj #?(:clj
(:import (clojure.lang ExceptionInfo)))) (:import (clojure.lang ExceptionInfo))))
(stest/instrument) (defn instrument-all [f]
(try
(stest/instrument)
(f)
(finally
(stest/unstrument))))
(use-fixtures :each instrument-all)
(deftest router-spec-test (deftest router-spec-test
@ -40,9 +46,9 @@
;; path ;; path
[:invalid {}] [:invalid {}]
;; vector data ;; nested path
["/api" [] ["/api"
["/ipa"]]))) [:ipa]])))
(testing "routes conform to spec (can't spec protocol functions)" (testing "routes conform to spec (can't spec protocol functions)"
(is (s/valid? ::rs/routes (r/routes (r/router ["/ping"]))))) (is (s/valid? ::rs/routes (r/routes (r/router ["/ping"])))))
@ -60,7 +66,7 @@
{:conflicts (fn [_])} {:conflicts (fn [_])}
{:router r/linear-router}) {:router r/linear-router})
(are [opts] #_(are [opts]
(is (thrown-with-msg? (is (thrown-with-msg?
ExceptionInfo ExceptionInfo
#"Call to #'reitit.core/router did not conform to spec" #"Call to #'reitit.core/router did not conform to spec"