From 39e2e1b11edc49416294f657d82912406edc594c Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Sun, 10 Dec 2017 17:46:29 +0200 Subject: [PATCH] Updated docs --- doc/SUMMARY.md | 3 + doc/coercion/README.md | 3 + doc/coercion/clojure_spec_coercion.md | 50 +++++++++++++ doc/coercion/coercion2.md | 104 -------------------------- doc/coercion/data_spec_coercion.md | 43 +++++++++++ doc/coercion/schema_coercion.md | 44 +++++++++++ 6 files changed, 143 insertions(+), 104 deletions(-) create mode 100644 doc/coercion/clojure_spec_coercion.md delete mode 100644 doc/coercion/coercion2.md create mode 100644 doc/coercion/data_spec_coercion.md create mode 100644 doc/coercion/schema_coercion.md diff --git a/doc/SUMMARY.md b/doc/SUMMARY.md index 4f70c225..bf23d439 100644 --- a/doc/SUMMARY.md +++ b/doc/SUMMARY.md @@ -10,6 +10,9 @@ * [Route Conflicts](basics/route_conflicts.md) * [Coercion](coercion/README.md) * [Coercion Explained](coercion/coercion.md) + * [Plumatic Schema](coercion/schema_coercion.md) + * [Clojure.spec](coercion/clojure_spec_coercion.md) + * [Data-specs](coercion/data_spec_coercion.md) * [Advanced](advanced/README.md) * [Configuring Routers](advanced/configuring_routers.md) * [Different Routers](advanced/different_routers.md) diff --git a/doc/coercion/README.md b/doc/coercion/README.md index 15fe241b..2cc3d479 100644 --- a/doc/coercion/README.md +++ b/doc/coercion/README.md @@ -1,3 +1,6 @@ # Coercion * [Coercion Explained](coercion.md) +* [Plumatic Schema](schema_coercion.md) +* [Clojure.spec](clojure_spec_coercion.md) +* [Data-specs](data_spec_coercion.md) diff --git a/doc/coercion/clojure_spec_coercion.md b/doc/coercion/clojure_spec_coercion.md new file mode 100644 index 00000000..9aece914 --- /dev/null +++ b/doc/coercion/clojure_spec_coercion.md @@ -0,0 +1,50 @@ +# Clojure.spec Coercion + +The [clojure.spec](https://clojure.org/guides/spec) library specifies the structure of data, validates or destructures it, and can generate data based on the spec. + +**NOTE**: Currently, `clojure.spec` [doesn't support runtime transformations via conforming](https://dev.clojure.org/jira/browse/CLJ-2116), so one needs to wrap all specs into [Spec Records](https://github.com/metosin/spec-tools/blob/master/README.md#spec-records) to get the coercion working. + +```clj +(require '[reitit.coercion.spec]) +(require '[reitit.coercion :as coercion]) +(require '[spec-tools.spec :as spec]) +(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?) +(s/def ::path-params (s/keys :req-un [::company ::user-id])) + +(def router + (r/router + ["/:company/users/:user-id" {:name ::user-view + :coercion reitit.coercion.spec/coercion + :parameters {:path ::path-params}}] + {:compile coercion/compile-request-coercers})) + +(defn route-and-coerce! [path] + (if-let [match (r/match-by-path router path)] + (assoc match :parameters (coercion/coerce! match)))) +``` + +Successful coercion: + +```clj +(route-and-coerce! "/metosin/users/123") +; #Match{:template "/:company/users/:user-id", +; :data {:name :user/user-view, +; :coercion #SpecCoercion{...} +; :parameters {:path ::path-params}}, +; :result {:path #object[reitit.coercion$request_coercer$]}, +; :params {:company "metosin", :user-id "123"}, +; :parameters {:path {:company "metosin", :user-id 123}} +; :path "/metosin/users/123"} +``` + +Failing coercion: + +```clj +(route-and-coerce! "/metosin/users/ikitommi") +; => ExceptionInfo Request coercion failed... +``` diff --git a/doc/coercion/coercion2.md b/doc/coercion/coercion2.md deleted file mode 100644 index e1c6a40f..00000000 --- a/doc/coercion/coercion2.md +++ /dev/null @@ -1,104 +0,0 @@ -# Coercion - -Coercion is a process of transforming parameters from one format into another. - -By default, all wildcard and catch-all parameters are parsed as Strings: - -```clj -(require '[reitit.core :as r]) - -(def router - (r/router - ["/:company/users/:user-id" ::user-view])) -``` - -Here's a match: - -```clj -(r/match-by-path r "/metosin/users/123") -; #Match{:template "/:company/users/:user-id", -; :data {:name :user/user-view}, -; :result nil, -; :params {:company "metosin", :user-id "123"}, -; :path "/metosin/users/123"} -``` - -To enable parameter coercion, we need to do few things: - -1. Choose a `Coercion` for the routes -2. Defined types for the parameters -3. Compile coercers for the types -4. Apply the coercion - -## Coercion - -`Coercion` is a protocol defining how types can be defined, coerced and inventoried. - -Reitit ships with the following coercion modules: - -* `reitit.coercion.schema/coercion` for [plumatic schema](https://github.com/plumatic/schema). -* `reitit.coercion.spec/coercion` for both [clojure.spec](https://clojure.org/about/spec) and [data-specs](https://github.com/metosin/spec-tools#data-specs). - -Coercion can be attached to routes using a `:coercion` key in the route data. There can be multiple `Coercion` implementation into a single router, normal [scoping rules](../basics/route_data.html#nested-route-data) apply here too. - -## Defining types for parameters - -Route parameters can be defined via route data `:parameters`. It can be submaps to define different types of parameters: `:query`, `:body`, `:form`, `:header` and `:path`. Syntax for the actual parameters is defined by the `Coercion` being used. - -#### Schema - -```clj -(require '[reitit.coercion.schema]) -(require '[schema.core :as schema]) - -(def router - (r/router - ["/:company/users/:user-id" {:name ::user-view - :coercion reitit.coercion.schema/coercion - :parameters {:path {:company schema/Str - :user-id schema/Int}])) -``` - -#### Clojure.spec - -Currently, `clojure.spec` [doesn't support runtime transformations via conforming](https://dev.clojure.org/jira/browse/CLJ-2116), so one needs to wrap all specs with `spec-tools.core/spec` to get this working. - - -```clj -(require '[reitit.coercion.spec]) -(require '[spec-tools.spec :as spec]) -(require '[clojure.spec :as s]) - -(s/def ::company spec/string? -(s/def ::user-id spec/int? -(s/def ::path-params (s/keys :req-un [::company ::user-id])) - -(def router - (r/router - ["/:company/users/:user-id" {:name ::user-view - :coercion reitit.coercion.spec/coercion - :parameters {:path ::path-params])) -``` - -#### Data-specs - -```clj -(require '[reitit.coercion.spec]) - -(def router - (r/router - ["/:company/users/:user-id" {:name ::user-view - :coercion reitit.coercion.spec/coercion - :parameters {:path {:company str? - :user-id int?}])) -``` - -So, now we have our - - -### Thanks to - -Most of the thing are just redefined version of the original implementation. Big thanks to: - -* [compojure-api](https://clojars.org/metosin/compojure-api) for the initial `Coercion` protocol -* [ring-swagger](https://github.com/metosin/ring-swagger#more-complete-example) for the syntax for the `:paramters` (and `:responses`). diff --git a/doc/coercion/data_spec_coercion.md b/doc/coercion/data_spec_coercion.md new file mode 100644 index 00000000..8487d89d --- /dev/null +++ b/doc/coercion/data_spec_coercion.md @@ -0,0 +1,43 @@ +# Data-spec Coercion + +[Data-specs](https://github.com/metosin/spec-tools#data-specs) is alternative, macro-free syntax to define `clojure.spec`s. As a bonus, supports the [runtime transformations via conforming](https://dev.clojure.org/jira/browse/CLJ-2116) out-of-the-box. + +```clj +(require '[reitit.coercion.spec]) +(require '[reitit.coercion :as coercion]) +(require '[reitit.core :as r]) + +(def router + (r/router + ["/:company/users/:user-id" {:name ::user-view + :coercion reitit.coercion.spec/coercion + :parameters {:path {:company string? + :user-id int?}}}] + {:compile coercion/compile-request-coercers})) + +(defn route-and-coerce! [path] + (if-let [match (r/match-by-path router path)] + (assoc match :parameters (coercion/coerce! match)))) +``` + +Successful coercion: + +```clj +(route-and-coerce! "/metosin/users/123") +; #Match{:template "/:company/users/:user-id", +; :data {:name :user/user-view, +; :coercion #SpecCoercion{...} +; :parameters {:path {:company string?, +; :user-id int?}}}, +; :result {:path #object[reitit.coercion$request_coercer$]}, +; :params {:company "metosin", :user-id "123"}, +; :parameters {:path {:company "metosin", :user-id 123}} +; :path "/metosin/users/123"} +``` + +Failing coercion: + +```clj +(route-and-coerce! "/metosin/users/ikitommi") +; => ExceptionInfo Request coercion failed... +``` diff --git a/doc/coercion/schema_coercion.md b/doc/coercion/schema_coercion.md new file mode 100644 index 00000000..c42374f8 --- /dev/null +++ b/doc/coercion/schema_coercion.md @@ -0,0 +1,44 @@ +# Plumatic Schema Coercion + +[Plumatic Schema](https://github.com/plumatic/schema) is a Clojure(Script) library for declarative data description and validation. + +```clj +(require '[reitit.coercion.schema]) +(require '[reitit.coercion :as coercion]) +(require '[schema.core :as s]) +(require '[reitit.core :as r]) + +(def router + (r/router + ["/:company/users/:user-id" {:name ::user-view + :coercion reitit.coercion.schema/coercion + :parameters {:path {:company s/Str + :user-id s/Int}}}] + {:compile coercion/compile-request-coercers})) + +(defn route-and-coerce! [path] + (if-let [match (r/match-by-path router path)] + (assoc match :parameters (coercion/coerce! match)))) +``` + +Successful coercion: + +```clj +(route-and-coerce! "/metosin/users/123") +; #Match{:template "/:company/users/:user-id", +; :data {:name :user/user-view, +; :coercion #SchemaCoercion{...} +; :parameters {:path {:company java.lang.String, +; :user-id Int}}}, +; :result {:path #object[reitit.coercion$request_coercer$]}, +; :params {:company "metosin", :user-id "123"}, +; :parameters {:path {:company "metosin", :user-id 123}} +; :path "/metosin/users/123"} +``` + +Failing coercion: + +```clj +(route-and-coerce! "/metosin/users/ikitommi") +; => ExceptionInfo Request coercion failed... +```