diff --git a/CHANGELOG.md b/CHANGELOG.md index 70ce29b6..0c9c6d4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## UNRELEASED +## 0.2.4-SNAPSHOT ## `reitit-ring` @@ -6,10 +6,14 @@ ## `reitit-spec` +* Latest goodies from [spec-tools](https://github.com/metosin/spec-tools) + * Swagger parameter fixes + * Better spec coercion via `st/coerce` using spec walking & inference: many simple specs (core predicates, `spec-tools.core/spec`, `s/and`, `s/or`, `s/coll-of`, `s/keys`, `s/map-of`, `s/nillable` and `s/every`) can be transformed without needing spec to be wrapped. Fallbacks to old conformed based approach. + * updated deps: ```clj -[metosin/spec-tools "0.7.2"] is available but we use "0.7.1" +[metosin/spec-tools "0.8.0-SNAPSHOT"] is available but we use "0.7.1" ``` ## 0.2.3 (2018-09-24) diff --git a/doc/coercion/clojure_spec_coercion.md b/doc/coercion/clojure_spec_coercion.md index a35cb1a2..96467530 100644 --- a/doc/coercion/clojure_spec_coercion.md +++ b/doc/coercion/clojure_spec_coercion.md @@ -4,11 +4,13 @@ The [clojure.spec](https://clojure.org/guides/spec) library specifies the struct ## Warning -`clojure.spec` by itself doesn't support coercion. `reitit` uses [spec-tools](https://github.com/metosin/spec-tools) that adds coercion to spec. Like `clojure.spec`, it's alpha as it leans on `clojure.spec.alpha/conform`, which is concidered a spec internal, that might be changed or removed later. +`clojure.spec` by itself doesn't support coercion. `reitit` uses [spec-tools](https://github.com/metosin/spec-tools) that adds coercion to spec. Like `clojure.spec`, it's alpha as it leans both on spec walking and `clojure.spec.alpha/conform`, which is concidered a spec internal, that might be changed or removed later. -For now, all leaf specs need to be wrapped into [Spec Records](https://github.com/metosin/spec-tools/blob/master/README.md#spec-records) to get the coercion working. +## Usage -There are [CLJ-2116](https://dev.clojure.org/jira/browse/CLJ-2116) and [CLJ-2251](https://dev.clojure.org/jira/browse/CLJ-2251) that would help solve this elegantly. +For simple specs (core predicates, `spec-tools.core/spec`, `s/and`, `s/or`, `s/coll-of`, `s/keys`, `s/map-of`, `s/nillable` and `s/every`), the transformation is inferred using [spec-walker](https://github.com/metosin/spec-tools#spec-walker) and is automatic. To support all specs (like regex-specs), specs need to be wrapped into [Spec Records](https://github.com/metosin/spec-tools/blob/master/README.md#spec-records). + +There are [CLJ-2116](https://dev.clojure.org/jira/browse/CLJ-2116) and [CLJ-2251](https://dev.clojure.org/jira/browse/CLJ-2251) that would help solve this elegantly. Go vote 'em up. ## Example @@ -19,9 +21,9 @@ There are [CLJ-2116](https://dev.clojure.org/jira/browse/CLJ-2116) and [CLJ-2251 (require '[clojure.spec.alpha :as s]) (require '[reitit.core :as r]) -;; need to wrap the primitives! -(s/def ::company spec/string?) -(s/def ::user-id spec/int?) +;; simple specs, inferred +(s/def ::company string?) +(s/def ::user-id int?) (s/def ::path-params (s/keys :req-un [::company ::user-id])) (def router diff --git a/examples/ring-spec-swagger/project.clj b/examples/ring-spec-swagger/project.clj index 333cbf7d..222af873 100644 --- a/examples/ring-spec-swagger/project.clj +++ b/examples/ring-spec-swagger/project.clj @@ -2,5 +2,5 @@ :description "Reitit Ring App with Swagger" :dependencies [[org.clojure/clojure "1.9.0"] [ring/ring-jetty-adapter "1.7.0"] - [metosin/reitit "0.2.3"]] + [metosin/reitit "0.2.4-SNAPSHOT"]] :repl-options {:init-ns example.server}) diff --git a/examples/ring-spec-swagger/src/example/server.clj b/examples/ring-spec-swagger/src/example/server.clj index 3cfdc946..514cb8f8 100644 --- a/examples/ring-spec-swagger/src/example/server.clj +++ b/examples/ring-spec-swagger/src/example/server.clj @@ -11,19 +11,18 @@ [ring.adapter.jetty :as jetty] [muuntaja.core :as m] [clojure.spec.alpha :as s] - [spec-tools.spec :as spec] [clojure.java.io :as io])) (s/def ::file multipart/temp-file-part) (s/def ::file-params (s/keys :req-un [::file])) -(s/def ::name spec/string?) -(s/def ::size spec/int?) +(s/def ::name string?) +(s/def ::size int?) (s/def ::file-response (s/keys :req-un [::name ::size])) -(s/def ::x spec/int?) -(s/def ::y spec/int?) -(s/def ::total spec/int?) +(s/def ::x int?) +(s/def ::y int?) +(s/def ::total int?) (s/def ::math-request (s/keys :req-un [::x ::y])) (s/def ::math-response (s/keys :req-un [::total])) diff --git a/modules/reitit-spec/src/reitit/coercion/spec.cljc b/modules/reitit-spec/src/reitit/coercion/spec.cljc index 7de3f423..12553c39 100644 --- a/modules/reitit-spec/src/reitit/coercion/spec.cljc +++ b/modules/reitit-spec/src/reitit/coercion/spec.cljc @@ -122,13 +122,16 @@ {:keys [formats default]} (transformers type)] (fn [value format] (if-let [transformer (or (get formats format) default)] - (let [transformed (st/conform spec value transformer)] - (if (s/invalid? transformed) - (let [problems (st/explain-data spec value transformer)] - (coercion/map->CoercionError - {:spec spec - :problems (::s/problems problems)})) - (s/unform spec transformed))) + (let [coerced (st/coerce spec value transformer)] + (if (s/valid? spec coerced) + coerced + (let [transformed (st/conform spec value transformer)] + (if (s/invalid? transformed) + (let [problems (st/explain-data spec value transformer)] + (coercion/map->CoercionError + {:spec spec + :problems (::s/problems problems)})) + (s/unform spec transformed))))) value)))) (-response-coercer [this spec] (if (coerce-response? spec)