From 7d4db18000c26dd6de69ed7bf95ef9997d39be6d Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Fri, 1 Sep 2017 11:32:08 +0300 Subject: [PATCH 1/3] Initial specs for reitit.core --- project.clj | 3 ++ src/reitit/core.cljc | 3 ++ src/reitit/spec.cljc | 81 +++++++++++++++++++++++++++++++++ test/cljc/reitit/spec_test.cljc | 67 +++++++++++++++++++++++++++ 4 files changed, 154 insertions(+) create mode 100644 src/reitit/spec.cljc create mode 100644 test/cljc/reitit/spec_test.cljc diff --git a/project.clj b/project.clj index 6c4926db..f843d35e 100644 --- a/project.clj +++ b/project.clj @@ -26,6 +26,9 @@ [metosin/spec-tools "0.3.3"] [org.clojure/spec.alpha "0.1.123"] + [expound "0.2.1"] + [orchestra "2017.08.13"] + [criterium "0.4.4"] [org.clojure/test.check "0.9.0"] [org.clojure/tools.namespace "0.2.11"] diff --git a/src/reitit/core.cljc b/src/reitit/core.cljc index ae1282fb..cdc3e85b 100644 --- a/src/reitit/core.cljc +++ b/src/reitit/core.cljc @@ -100,6 +100,9 @@ (match-by-path [this path]) (match-by-name [this name] [this name params])) +(defn router? [x] + (satisfies? Router x)) + (defrecord Match [template meta result params path]) (defrecord PartialMatch [template meta result params required]) diff --git a/src/reitit/spec.cljc b/src/reitit/spec.cljc new file mode 100644 index 00000000..5673ecb5 --- /dev/null +++ b/src/reitit/spec.cljc @@ -0,0 +1,81 @@ +(ns reitit.spec + (:require [clojure.spec.alpha :as s] + [clojure.spec.gen.alpha :as gen] + [clojure.string :as str] + [reitit.core :as reitit])) + +;; +;; routes +;; + +(s/def ::path (s/with-gen (s/and string? #(str/starts-with? % "/")) + #(gen/fmap (fn [s] (str "/" s)) (s/gen string?)))) + +(s/def ::arg (s/and any? (complement vector?))) +(s/def ::meta (s/map-of keyword? any?)) +(s/def ::result any?) + +(s/def ::raw-route + (s/cat :path ::path + :arg (s/? ::arg) + :childs (s/* (s/spec (s/and ::raw-route))))) + +(s/def ::raw-routes + (s/or :route ::raw-route + :routes (s/coll-of ::raw-route :into []))) + +(s/def ::route + (s/cat :path ::path + :meta ::meta)) + +(s/def ::routes + (s/or :route ::route + :routes (s/coll-of ::route :into []))) + +;; +;; router +;; + +(s/def ::router reitit/router?) + +(s/def :reitit.router/path (s/or :empty #{""} :path ::path)) + +(s/def :reitit.router/routes ::routes) + +(s/def :reitit.router/meta ::meta) + +(s/def :reitit.router/expand + (s/fspec :args (s/cat :arg ::arg, :opts ::opts) + :ret ::route)) + +(s/def :reitit.router/coerce + (s/fspec :args (s/cat :route (s/spec ::route), :opts ::opts) + :ret ::route)) + +(s/def :reitit.router/compile + (s/fspec :args (s/cat :route (s/spec ::route), :opts ::opts) + :ret ::result)) + +(s/def :reitit.router/conflicts + (s/fspec :args (s/cat :conflicts (s/map-of ::route (s/coll-of ::route :into #{}))))) + +(s/def :reitit.router/router + (s/fspec :args (s/cat :routes ::routes, :opts ::opts) + :ret ::router)) + +;; TODO: fspecs fail.. +(s/def ::opts + (s/nilable + (s/keys :opt-un [:reitit.router/path + :reitit.router/routes + :reitit.router/meta + #_:reitit.router/expand + #_:reitit.router/coerce + #_:reitit.router/compile + #_:reitit.router/conflicts + #_:reitit.router/router]))) + +(s/fdef reitit/router + :args (s/or :1arity (s/cat :data (s/spec ::raw-routes)) + :2arity (s/cat :data (s/spec ::raw-routes), :opts ::opts)) + :ret ::router) diff --git a/test/cljc/reitit/spec_test.cljc b/test/cljc/reitit/spec_test.cljc new file mode 100644 index 00000000..2221a966 --- /dev/null +++ b/test/cljc/reitit/spec_test.cljc @@ -0,0 +1,67 @@ +(ns reitit.spec-test + (:require [clojure.test :refer [deftest testing is are]] + [clojure.spec.test.alpha :as stest] + [clojure.spec.alpha :as s] + [reitit.core :as reitit] + [reitit.spec :as spec]) + #?(:clj + (:import (clojure.lang ExceptionInfo)))) + +(stest/instrument `reitit/router) + +(deftest router-spec-test + + (testing "router" + + (testing "route-data" + (are [data] + (is (= true (reitit/router? (reitit/router data)))) + + ["/api" {}] + + [["/api" {}]] + + ["/api" + ["/ipa" ::ipa] + ["/tea" + ["/room"]]]) + + (testing "with invalid routes" + (are [data] + (is (thrown-with-msg? + ExceptionInfo + #"Call to #'reitit.core/router did not conform to spec" + (reitit/router + data))) + + ;; missing slash + ["invalid" {}] + + ;; path + [:invalid {}] + + ;; vector meta + ["/api" [] + ["/ipa"]]))) + + (testing "options" + + (are [opts] + (is (= true (reitit/router? (reitit/router ["/api"] opts)))) + + {:path "/"} + + {:meta {}} + + #_{:coerce (fn [_ _] ["/"])} + ) + + + (are [opts] + (is (thrown-with-msg? + ExceptionInfo + #"Call to #'reitit.core/router did not conform to spec" + (reitit/router + ["/api"] opts))) + + {:meta 1})))) From b4855699b528116fab8c96e4a908c6adbbe2f257 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 3 Sep 2017 14:55:58 +0300 Subject: [PATCH 2/3] use fn? instead of fspec with router options fspec fails for some reason. related? http://github.com/walmartlabs/lacinia/pull/112 --- src/reitit/core.cljc | 4 ++-- src/reitit/spec.cljc | 41 ++++++++++++++++----------------- test/cljc/reitit/spec_test.cljc | 39 ++++++++++++++++++------------- 3 files changed, 45 insertions(+), 39 deletions(-) diff --git a/src/reitit/core.cljc b/src/reitit/core.cljc index cdc3e85b..bd1aa9da 100644 --- a/src/reitit/core.cljc +++ b/src/reitit/core.cljc @@ -30,7 +30,7 @@ (expand [_ _])) (defn walk [data {:keys [path meta routes expand] - :or {path "", meta [], routes [], expand expand} + :or {meta [], routes [], expand expand} :as opts}] (letfn [(walk-many [p m r] @@ -245,7 +245,7 @@ | key | description | | -------------|-------------| - | `:path` | Base-path for routes (default `\"\"`) + | `:path` | Base-path for routes | `:routes` | Initial resolved routes (default `[]`) | `:meta` | Initial route meta (default `{}`) | `:expand` | Function of `arg opts => meta` to expand route arg to route meta-data (default `reitit.core/expand`) diff --git a/src/reitit/spec.cljc b/src/reitit/spec.cljc index 5673ecb5..72bdf06d 100644 --- a/src/reitit/spec.cljc +++ b/src/reitit/spec.cljc @@ -38,42 +38,41 @@ (s/def ::router reitit/router?) -(s/def :reitit.router/path (s/or :empty #{""} :path ::path)) +(s/def :reitit.router/path ::path) (s/def :reitit.router/routes ::routes) (s/def :reitit.router/meta ::meta) -(s/def :reitit.router/expand - (s/fspec :args (s/cat :arg ::arg, :opts ::opts) - :ret ::route)) +(s/def :reitit.router/expand fn? + #_(s/fspec :args (s/cat :arg ::arg, :opts ::opts) + :ret ::route)) -(s/def :reitit.router/coerce - (s/fspec :args (s/cat :route (s/spec ::route), :opts ::opts) - :ret ::route)) +(s/def :reitit.router/coerce fn? + #_(s/fspec :args (s/cat :route (s/spec ::route), :opts ::opts) + :ret ::route)) -(s/def :reitit.router/compile - (s/fspec :args (s/cat :route (s/spec ::route), :opts ::opts) - :ret ::result)) +(s/def :reitit.router/compile fn? + #_(s/fspec :args (s/cat :route (s/spec ::route), :opts ::opts) + :ret ::result)) -(s/def :reitit.router/conflicts - (s/fspec :args (s/cat :conflicts (s/map-of ::route (s/coll-of ::route :into #{}))))) +(s/def :reitit.router/conflicts fn? + #_(s/fspec :args (s/cat :conflicts (s/map-of ::route (s/coll-of ::route :into #{}))))) -(s/def :reitit.router/router - (s/fspec :args (s/cat :routes ::routes, :opts ::opts) - :ret ::router)) +(s/def :reitit.router/router fn? + #_(s/fspec :args (s/cat :routes ::routes, :opts ::opts) + :ret ::router)) -;; TODO: fspecs fail.. (s/def ::opts (s/nilable (s/keys :opt-un [:reitit.router/path :reitit.router/routes :reitit.router/meta - #_:reitit.router/expand - #_:reitit.router/coerce - #_:reitit.router/compile - #_:reitit.router/conflicts - #_:reitit.router/router]))) + :reitit.router/expand + :reitit.router/coerce + :reitit.router/compile + :reitit.router/conflicts + :reitit.router/router]))) (s/fdef reitit/router :args (s/or :1arity (s/cat :data (s/spec ::raw-routes)) diff --git a/test/cljc/reitit/spec_test.cljc b/test/cljc/reitit/spec_test.cljc index 2221a966..cb3f18a6 100644 --- a/test/cljc/reitit/spec_test.cljc +++ b/test/cljc/reitit/spec_test.cljc @@ -46,22 +46,29 @@ (testing "options" - (are [opts] - (is (= true (reitit/router? (reitit/router ["/api"] opts)))) + (are [opts] + (is (= true (reitit/router? (reitit/router ["/api"] opts)))) - {:path "/"} + {:path "/"} + {:meta {}} + {:expand (fn [_ _] {})} + {:coerce (fn [route _] route)} + {:compile (fn [_ _])} + {:conflicts (fn [_])} + {:router reitit/linear-router}) - {:meta {}} + (are [opts] + (is (thrown-with-msg? + ExceptionInfo + #"Call to #'reitit.core/router did not conform to spec" + (reitit/router + ["/api"] opts))) - #_{:coerce (fn [_ _] ["/"])} - ) - - - (are [opts] - (is (thrown-with-msg? - ExceptionInfo - #"Call to #'reitit.core/router did not conform to spec" - (reitit/router - ["/api"] opts))) - - {:meta 1})))) + {:path ""} + {:path nil} + {:meta nil} + {:expand nil} + {:coerce nil} + {:compile nil} + {:conflicts nil} + {:router nil})))) From 750e2592df97d8ceb75d8b3534fa219fb3f06dc6 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Mon, 4 Sep 2017 16:09:19 +0300 Subject: [PATCH 3/3] simplify specs --- src/reitit/spec.cljc | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/src/reitit/spec.cljc b/src/reitit/spec.cljc index 72bdf06d..8953f3f7 100644 --- a/src/reitit/spec.cljc +++ b/src/reitit/spec.cljc @@ -18,7 +18,7 @@ (s/def ::raw-route (s/cat :path ::path :arg (s/? ::arg) - :childs (s/* (s/spec (s/and ::raw-route))))) + :childs (s/* (s/and ::raw-route)))) (s/def ::raw-routes (s/or :route ::raw-route @@ -37,31 +37,14 @@ ;; (s/def ::router reitit/router?) - (s/def :reitit.router/path ::path) - (s/def :reitit.router/routes ::routes) - (s/def :reitit.router/meta ::meta) - -(s/def :reitit.router/expand fn? - #_(s/fspec :args (s/cat :arg ::arg, :opts ::opts) - :ret ::route)) - -(s/def :reitit.router/coerce fn? - #_(s/fspec :args (s/cat :route (s/spec ::route), :opts ::opts) - :ret ::route)) - -(s/def :reitit.router/compile fn? - #_(s/fspec :args (s/cat :route (s/spec ::route), :opts ::opts) - :ret ::result)) - -(s/def :reitit.router/conflicts fn? - #_(s/fspec :args (s/cat :conflicts (s/map-of ::route (s/coll-of ::route :into #{}))))) - -(s/def :reitit.router/router fn? - #_(s/fspec :args (s/cat :routes ::routes, :opts ::opts) - :ret ::router)) +(s/def :reitit.router/expand fn?) +(s/def :reitit.router/coerce fn?) +(s/def :reitit.router/compile fn?) +(s/def :reitit.router/conflicts fn?) +(s/def :reitit.router/router fn?) (s/def ::opts (s/nilable