diff --git a/README.md b/README.md index 067cafd0..c5705d6b 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ Only a partial match. Let's provide the path-parameters: There is also a exception throwing version: -``` +```clj (reitit/match-by-name! router ::user) ; ExceptionInfo missing path-params for route /api/user/:id: #{:id} ``` @@ -266,7 +266,7 @@ The expanded routes: ```clj (-> app (ring/get-router) (reitit/routes)) ; [["/ping" -; {:handler #object[user$handler 0x5c312d6f "user$handler@5c312d6f"]} +; {:handler #object[...]} ; #Methods{:any #Endpoint{:meta {:handler #object[...]}, ; :handler #object[...], ; :middleware []}}]] @@ -307,10 +307,10 @@ Reverse routing: Middleware can be added with a `:middleware` key, with a vector value of the following: -1. ring middleware function (`handler -> request -> response`) -2. vector of middleware function (`handler ?args -> request -> response`) and optinally it's args. +1. ring middleware function `handler -> request -> response` +2. vector of middleware function `handler ?args -> request -> response` and optinally it's args. -Let's define some middleware and a handler: +A middleware and a handler: ```clj (defn wrap [handler id] @@ -357,7 +357,7 @@ Reitit supports first-class data-driven middleware via `reitit.middleware/Middle | `:wrap` | The actual middleware function of `handler args? => request => response` | `:gen` | Middleware compile function, see [compiling middleware](#compiling-middleware). -Behind the scenes, when routes are compiled, all middleware are first expanded into `Middleware` and stored as such in compilation results to be used for api-docs etc. For actual request processing, they are unwrapped into normal middleware functions producing zero runtime performance penalty. Thanks to the `reitit.middleware/IntoMiddleware` protocol, plain clojure(script) maps can also be used. +When routes are compiled, all middleware are expanded (and optionally compiled) into `Middleware` and stored in compilation results for later use (api-docs etc). For actual request processing, they are unwrapped into normal middleware functions producing zero runtime performance penalty. Middleware expansion is backed by `reitit.middleware/IntoMiddleware` protocol, enabling plain clojure(script) maps to be used. A Record: @@ -384,13 +384,13 @@ As plain map: ### Async Ring -All built-in middleware provide both the 2 and 3-arity, so they work with [Async Ring](https://www.booleanknot.com/blog/2016/07/15/asynchronous-ring.html) too. +All built-in middleware provide both 2 and 3-arity and are compiled for both Clojure & ClojureScript, so they work with [Async Ring](https://www.booleanknot.com/blog/2016/07/15/asynchronous-ring.html) and [Node.js](https://nodejs.org) too. ### Meta-data based extensions `ring-handler` injects the `Match` into a request and it can be extracted at runtime with `reitit.ring/get-match`. This can be used to build dynamic extensions to the system. -A middleware to guard routes based on user roles: +Example middleware to guard routes based on user roles: ```clj (require '[clojure.set :as set]) @@ -540,7 +540,7 @@ Still, we can do better. As we know the exact route that interceptor/middleware To do this we use [middleware records](#middleware-records) `:gen` hook instead of the normal `:wrap`. `:gen` expects a function of `route-meta router-opts => wrap`. Middleware can also return `nil`, which effective unmounts the middleware. Why mount a `wrap-enforce-roles` middleware for a route if there are no roles required for it? -To demonstrate the two approaches, below are response coercion middleware written as normal ring middleware function and as middleware record with `:gen`. These are the actual codes are from `reitit.coercion`: +To demonstrate the two approaches, below are response coercion middleware written as normal ring middleware function and as middleware record with `:gen`. These are the actual codes are from [`reitit.coercion`](https://github.com/metosin/reitit/blob/master/src/reitit/coercion.cljc): ### Naive diff --git a/perf-test/clj/reitit/coercion_perf_test.clj b/perf-test/clj/reitit/coercion_perf_test.clj index 61adcb1b..d556736d 100644 --- a/perf-test/clj/reitit/coercion_perf_test.clj +++ b/perf-test/clj/reitit/coercion_perf_test.clj @@ -33,7 +33,7 @@ (s/def ::y (s/and (s/conformer #(if (string? %) (Long/parseLong %) %) identity) int?)) (s/def ::k (s/keys :req-un [::x ::y])) - (let [spec (spec/specify {:x int?, :y int?} ::jeah) + (let [spec (spec/into-spec {:x int?, :y int?} ::jeah) coercers (#'coercion/request-coercers spec/coercion {:body spec}) params {:x "1", :y "2"} request {:body-params {:x "1", :y "2"}}] diff --git a/src/reitit/coercion/spec.cljc b/src/reitit/coercion/spec.cljc index 4d8603e3..f1f2fb00 100644 --- a/src/reitit/coercion/spec.cljc +++ b/src/reitit/coercion/spec.cljc @@ -23,31 +23,32 @@ (def default-conforming ::default) -(defprotocol Specify - (specify [this name])) +(defprotocol IntoSpec + (into-spec [this name])) -(extend-protocol Specify +(extend-protocol IntoSpec #?(:clj clojure.lang.PersistentArrayMap :cljs cljs.core.PersistentArrayMap) - (specify [this name] + (into-spec [this name] (ds/spec name this)) #?(:clj clojure.lang.PersistentHashMap :cljs cljs.core.PersistentHashMap) - (specify [this name] + (into-spec [this name] (ds/spec name this)) Spec - (specify [this _] this) + (into-spec [this _] this) - Object - (specify [this _] + #?(:clj Object + :cljs default) + (into-spec [this _] (st/create-spec {:spec this}))) ;; TODO: proper name! -(def memoized-specify - (memoize #(specify %1 (gensym "spec")))) +(def memoized-into-spec + (memoize #(into-spec %1 (gensym "spec")))) (defmulti coerce-response? identity :default ::default) (defmethod coerce-response? ::default [_] true) @@ -58,7 +59,7 @@ (get-name [_] name) (compile [_ model _] - (memoized-specify model)) + (memoized-into-spec model)) (get-apidocs [_ _ {:keys [parameters responses] :as info}] (cond-> (dissoc info :parameters :responses) @@ -67,13 +68,13 @@ (into (empty parameters) (for [[k v] parameters] - [k memoized-specify]))) + [k memoized-into-spec]))) responses (assoc ::swagger/responses (into (empty responses) (for [[k response] responses] - [k (update response :schema memoized-specify)]))))) + [k (update response :schema memoized-into-spec)]))))) (make-open [_ spec] spec) @@ -81,7 +82,7 @@ (update error :spec (comp str s/form))) (request-coercer [_ type spec] - (let [spec (memoized-specify spec) + (let [spec (memoized-into-spec spec) {:keys [formats default]} (conforming type)] (fn [value format] (if-let [conforming (or (get formats format) default)] @@ -101,9 +102,7 @@ (def default-options {:coerce-response? coerce-response? :conforming {:body {:default default-conforming - :formats {"application/json" json-conforming - "application/msgpack" json-conforming - "application/x-yaml" json-conforming}} + :formats {"application/json" json-conforming}} :string {:default string-conforming} :response {:default default-conforming}}}) diff --git a/test/cljs/reitit/doo_runner.cljs b/test/cljs/reitit/doo_runner.cljs index ff3bd70e..d426519d 100644 --- a/test/cljs/reitit/doo_runner.cljs +++ b/test/cljs/reitit/doo_runner.cljs @@ -1,9 +1,15 @@ (ns reitit.doo-runner (:require [doo.runner :refer-macros [doo-tests]] + reitit.coercion-test reitit.core-test - reitit.ring-test)) + reitit.middleware-test + reitit.ring-test + reitit.spec-test)) (enable-console-print!) -(doo-tests 'reitit.core-test - 'reitit.ring-test) +(doo-tests 'reitit.coercion-test + 'reitit.core-test + 'reitit.middleware-test + 'reitit.ring-test + 'reitit.spec-test)