From 478ee18a32e99ee6ff5b16a8cc784c9c87ebb2cb Mon Sep 17 00:00:00 2001 From: Phil Hofmann Date: Tue, 27 Jul 2021 18:33:17 +0200 Subject: [PATCH] improve some docs --- doc/ring/RESTful_form_methods.md | 12 ++++++------ doc/ring/coercion.md | 8 ++++---- doc/ring/compiling_middleware.md | 6 +++--- doc/ring/content_negotiation.md | 12 ++++++------ doc/ring/data_driven_middleware.md | 22 +++++++++++----------- doc/ring/default_handler.md | 2 +- doc/ring/default_middleware.md | 4 ++-- doc/ring/dynamic_extensions.md | 4 ++-- doc/ring/exceptions.md | 10 +++++----- doc/ring/ring.md | 10 +++++----- 10 files changed, 45 insertions(+), 45 deletions(-) diff --git a/doc/ring/RESTful_form_methods.md b/doc/ring/RESTful_form_methods.md index 5faaeb3c..61f6bfd7 100644 --- a/doc/ring/RESTful_form_methods.md +++ b/doc/ring/RESTful_form_methods.md @@ -1,10 +1,10 @@ # RESTful form methods -When designing RESTful applications you will be doing a lot of "PATCH" and "DELETE" request, but most browsers don't support methods other than "GET" and "POST" when it comes to submitting forms. +When designing RESTful applications you will be doing a lot of "PATCH" and "DELETE" request, but most browsers don't support methods other than "GET" and "POST" when it comes to submitting forms. There is a pattern to solve this (pioneered by Rails) using a hidden "_method" field in the form and swapping out the "POST" method for whatever is in that field. -We can do this with middleware in reitit like this: +We can do this with middleware in reitit like this: ```clj (defn- hidden-method [request] @@ -18,20 +18,20 @@ We can do this with middleware in reitit like this: :wrap (fn [handler] (fn [request] (if-let [fm (and (= :post (:request-method request)) ;; if this is a :post request - (hidden-method request))] ;; and there is a "_method" field + (hidden-method request))] ;; and there is a "_method" field (handler (assoc request :request-method fm)) ;; replace :request-method (handler request))))}) ``` -And apply the middleware like this: +And apply the middleware like this: ```clj (reitit.ring/ring-handler (reitit.ring/router ...) (reitit.ring/create-default-handler) - {:middleware + {:middleware [reitit.ring.middleware.parameters/parameters-middleware ;; needed to have :form-params in the request map reitit.ring.middleware.multipart/multipart-middleware ;; needed to have :multipart-params in the request map wrap-hidden-method]}) ;; our hidden method wrapper ``` -(NOTE: This middleware must be placed here and not inside the route data given to `reitit.ring/handler`. +(NOTE: This middleware must be placed here and not inside the route data given to `reitit.ring/handler`. This is so that our middleware is applied before reitit matches the request with a specific handler using the wrong method.) diff --git a/doc/ring/coercion.md b/doc/ring/coercion.md index a8ddae6f..faed3a8b 100644 --- a/doc/ring/coercion.md +++ b/doc/ring/coercion.md @@ -39,7 +39,7 @@ Responses are defined in route data under `:responses` key. It's value should be Below is an example with [Plumatic Schema](https://github.com/plumatic/schema). It defines schemas for `:query`, `:body` and `:path` parameters and for http 200 response `:body`. -Handler can access the coerced parameters can be read under `:parameters` key in the request. +Handlers can access the coerced parameters via the `:parameters` key in the request. ```clj (require '[reitit.coercion.schema]) @@ -71,7 +71,7 @@ Defining a coercion for a route data doesn't do anything, as it's just data. We ### Full example -Here's an full example for applying coercion with Reitit, Ring and Schema: +Here is a full example for applying coercion with Reitit, Ring and Schema: ```clj (require '[reitit.ring.coercion :as rrc]) @@ -150,7 +150,7 @@ Invalid response: ## Pretty printing spec errors -Spec problems are exposed as-is into request & response coercion errors, enabling pretty-printers like [expound](https://github.com/bhb/expound) to be used: +Spec problems are exposed as is in request & response coercion errors. Pretty-printers like [expound](https://github.com/bhb/expound) can be enabled like this: ```clj (require '[reitit.ring :as ring]) @@ -216,7 +216,7 @@ Spec problems are exposed as-is into request & response coercion errors, enablin ### Optimizations -The coercion middleware are [compiled against a route](compiling_middleware.md). In the middleware compilation step the actual coercer implementations are constructed for the defined models. Also, the middleware doesn't mount itself if a route doesn't have `:coercion` and `:parameters` or `:responses` defined. +The coercion middlewares are [compiled against a route](compiling_middleware.md). In the middleware compilation step the actual coercer implementations are constructed for the defined models. Also, the middleware doesn't mount itself if a route doesn't have `:coercion` and `:parameters` or `:responses` defined. We can query the compiled middleware chain for the routes: diff --git a/doc/ring/compiling_middleware.md b/doc/ring/compiling_middleware.md index 1e100f3b..f9404cc9 100644 --- a/doc/ring/compiling_middleware.md +++ b/doc/ring/compiling_middleware.md @@ -1,12 +1,12 @@ # Compiling Middleware -The [dynamic extensions](dynamic_extensions.md) is a easy way to extend the system. To enable fast lookups into route data, we can compile them into any shape (records, functions etc.) we want, enabling fast access at request-time. +The [dynamic extensions](dynamic_extensions.md) are an easy way to extend the system. To enable fast lookup of route data, we can compile them into any shape (records, functions etc.), enabling fast access at request-time. -But, we can do much better. As we know the exact route that middleware/interceptor is linked to, we can pass the (compiled) route information into the middleware at creation-time. It can do local reasoning: extract and transform relevant data just for it and pass the optimized data into the actual request-handler via a closure - yielding much faster runtime processing. Middleware can also decide not to mount itself by returning `nil`. Why mount a `wrap-enforce-roles` middleware for a route if there are no roles required for it? +But, we can do much better. As we know the exact route that a middleware/interceptor is linked to, we can pass the (compiled) route information into the middleware at creation-time. It can do local reasoning: Extract and transform relevant data just for it and pass the optimized data into the actual request-handler via a closure - yielding much faster runtime processing. A middleware can also decide not to mount itself by returning `nil`. (E.g. Why mount a `wrap-enforce-roles` middleware for a route if there are no roles required for it?) To enable this we use [middleware records](data_driven_middleware.md) `:compile` key instead of the normal `:wrap`. `:compile` expects a function of `route-data router-opts => ?IntoMiddleware`. -To demonstrate the two approaches, below are response coercion middleware written as normal ring middleware function and as middleware record with `:compile`. +To demonstrate the two approaches, below is the response coercion middleware written as normal ring middleware function and as middleware record with `:compile`. ## Normal Middleware diff --git a/doc/ring/content_negotiation.md b/doc/ring/content_negotiation.md index 3c73202d..fb696c10 100644 --- a/doc/ring/content_negotiation.md +++ b/doc/ring/content_negotiation.md @@ -1,8 +1,8 @@ # Content Negotiation -Wrapper for [Muuntaja](https://github.com/metosin/muuntaja) middleware for content-negotiation, request decoding and response encoding. Takes explicit configuration via `:muuntaja` key in route data. Emit's [swagger](swagger.md) `:produces` and `:consumes` definitions automatically based on the Muuntaja configuration. +Wrapper for [Muuntaja](https://github.com/metosin/muuntaja) middleware for content negotiation, request decoding and response encoding. Takes explicit configuration via `:muuntaja` key in route data. Emits [swagger](swagger.md) `:produces` and `:consumes` definitions automatically based on the Muuntaja configuration. -Negotiates a request body based on `Content-Type` header and response body based on `Accept`, `Accept-Charset` headers. Publishes the negotiation results as `:muuntaja/request` and `:muuntaja/response` keys into the request. +Negotiates a request body based on `Content-Type` header and response body based on `Accept` and `Accept-Charset` headers. Publishes the negotiation results as `:muuntaja/request` and `:muuntaja/response` keys into the request. Decodes the request body into `:body-params` using the `:muuntaja/request` key in request if the `:body-params` doesn't already exist. @@ -87,7 +87,7 @@ Server: Jetty(9.2.21.v20170120) ## Changing default parameters -The current JSON formatter used by `reitit` already have the option to parse keys as `keyword` which is a sane default in Clojure. However, if you would like to parse all the `double` as `bigdecimal` you'd need to change an option of the [JSON formatter](https://github.com/metosin/jsonista) +The current JSON formatter used by `reitit` already has the option to parse keys as `keyword` which is a sane default in Clojure. However, if you would like to parse all the `double` as `bigdecimal` you'd need to change an option of the [JSON formatter](https://github.com/metosin/jsonista) ```clj @@ -102,7 +102,7 @@ The current JSON formatter used by `reitit` already have the option to parse key Now you should change the `m/instance` installed in the router with the `new-muuntaja-instance`. -You can find more options for [JSON](https://cljdoc.org/d/metosin/jsonista/0.2.5/api/jsonista.core#object-mapper) and [EDN]. +Here you can find more options for [JSON](https://cljdoc.org/d/metosin/jsonista/0.2.5/api/jsonista.core#object-mapper) and EDN. ## Adding custom encoder @@ -125,9 +125,9 @@ The example below is from `muuntaja` explaining how to add a custom encoder to p ``` -## Adding all together +## Putting it all together -If you inspect `m/default-options` it's only a map, therefore you can compose your new muuntaja instance with as many options as you need it. +If you inspect `m/default-options` you'll find it's only a map. This means you can compose your new muuntaja instance with as many options as you need. ```clj (def new-muuntaja diff --git a/doc/ring/data_driven_middleware.md b/doc/ring/data_driven_middleware.md index e11be838..2d310ff4 100644 --- a/doc/ring/data_driven_middleware.md +++ b/doc/ring/data_driven_middleware.md @@ -1,19 +1,19 @@ # Data-driven Middleware -Ring [defines middleware](https://github.com/ring-clojure/ring/wiki/Concepts#middleware) as a function of type `handler & args => request => response`. It's relatively easy to understand and enables good performance. Downside is that the middleware-chain is just a opaque function, making things like debugging and composition hard. It's too easy to apply the middleware in wrong order. +Ring [defines middleware](https://github.com/ring-clojure/ring/wiki/Concepts#middleware) as a function of type `handler & args => request => response`. It is relatively easy to understand and allows for good performance. A downside is that the middleware chain is just a opaque function, making things like debugging and composition hard. It is too easy to apply the middlewares in wrong order. Reitit defines middleware as data: -1. Middleware can be defined as first-class data entries -2. Middleware can be mounted as a [duct-style](https://github.com/duct-framework/duct/wiki/Configuration) vector (of middleware) -4. Middleware can be optimized & [compiled](compiling_middleware.md) against an endpoint -3. Middleware chain can be transformed by the router +1. A middleware can be defined as first-class data entries +2. A middleware can be mounted as a [duct-style](https://github.com/duct-framework/duct/wiki/Configuration) vector (of middlewares) +4. A middleware can be optimized & [compiled](compiling_middleware.md) against an endpoint +3. A middleware chain can be transformed by the router ## Middleware as data -All values in the `:middleware` vector in the route data are expanded into `reitit.middleware/Middleware` Records with using the `reitit.middleware/IntoMiddleware` Protocol. By default, functions, maps and `Middleware` records are allowed. +All values in the `:middleware` vector of route data are expanded into `reitit.middleware/Middleware` Records by using the `reitit.middleware/IntoMiddleware` Protocol. By default, functions, maps and `Middleware` records are allowed. -Records can have arbitrary keys, but the following keys have a special purpose: +Records can have arbitrary keys, but the following keys have special purpose: | key | description | | ---------------|-------------| @@ -22,13 +22,13 @@ Records can have arbitrary keys, but the following keys have a special purpose: | `:wrap` | The actual middleware function of `handler & args => request => response` | `:compile` | Middleware compilation function, see [compiling middleware](compiling_middleware.md). -Middleware Records are accessible in their raw form in the compiled route results, thus available for inventories, creating api-docs etc. +Middleware Records are accessible in their raw form in the compiled route results, and thus are available for inventories, creating api-docs, etc. For the actual request processing, the Records are unwrapped into normal functions and composed into a middleware function chain, yielding zero runtime penalty. ### Creating Middleware -The following produce identical middleware runtime function. +The following examples produce identical middleware runtime functions. ### Function @@ -77,7 +77,7 @@ The following produce identical middleware runtime function. :handler handler}}]]))) ``` -All the middleware are applied correctly: +All the middlewares are applied correctly: ```clj (app {:request-method :get, :uri "/api/ping"}) @@ -86,7 +86,7 @@ All the middleware are applied correctly: ## Compiling middleware -Middleware can be optimized against an endpoint using [middleware compilation](compiling_middleware.md). +Middlewares can be optimized against an endpoint using [middleware compilation](compiling_middleware.md). ## Ideas for the future diff --git a/doc/ring/default_handler.md b/doc/ring/default_handler.md index 40fda56b..890dcb9c 100644 --- a/doc/ring/default_handler.md +++ b/doc/ring/default_handler.md @@ -1,6 +1,6 @@ # Default handler -By default, if no routes match, `nil` is returned, which is not valid response in Ring: +By default, if no routes match, `nil` is returned, which is not a valid response in Ring: ```clj (require '[reitit.ring :as ring]) diff --git a/doc/ring/default_middleware.md b/doc/ring/default_middleware.md index 18f29d9d..eba975ae 100644 --- a/doc/ring/default_middleware.md +++ b/doc/ring/default_middleware.md @@ -4,7 +4,7 @@ [metosin/reitit-middleware "0.5.13"] ``` -Any Ring middleware can be used with `reitit-ring`, but using data-driven middleware is preferred as they are easier to manage and in many cases, yield better performance. `reitit-middleware` contains a set of common ring middleware, lifted into data-driven middleware. +Any Ring middleware can be used with `reitit-ring`, but using data-driven middleware is preferred as they are easier to manage and in many cases yield better performance. `reitit-middleware` contains a set of common ring middleware, lifted into data-driven middleware. * [Parameter Handling](#parameters-handling) * [Exception Handling](#exception-handling) @@ -32,7 +32,7 @@ See [Content Negotiation](content_negotiation.md). Wrapper for [Ring Multipart Middleware](https://github.com/ring-clojure/ring/blob/master/ring-core/src/ring/middleware/multipart_params.clj). Emits swagger `:consumes` definitions automatically. Expected route data: - + | key | description | | -------------|-------------| | `[:parameters :multipart]` | mounts only if defined for a route. diff --git a/doc/ring/dynamic_extensions.md b/doc/ring/dynamic_extensions.md index 3f922317..1308dde1 100644 --- a/doc/ring/dynamic_extensions.md +++ b/doc/ring/dynamic_extensions.md @@ -1,8 +1,8 @@ # Dynamic 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 ad-hoc extensions to the system. +`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 ad hoc extensions to the system. -Example middleware to guard routes based on user roles: +This example shows a middleware to guard routes based on user roles: ```clj (require '[reitit.ring :as ring]) diff --git a/doc/ring/exceptions.md b/doc/ring/exceptions.md index d3a45fa2..4fea7d17 100644 --- a/doc/ring/exceptions.md +++ b/doc/ring/exceptions.md @@ -4,7 +4,7 @@ [metosin/reitit-middleware "0.5.13"] ``` -Exceptions thrown in router creation can be [handled with custom exception handler](../basics/error_messages.md). By default, exceptions thrown at runtime from a handler or a middleware are not caught by the `reitit.ring/ring-handler`. A good practise is a have an top-level exception handler to log and format the errors for clients. +Exceptions thrown in router creation can be [handled with custom exception handler](../basics/error_messages.md). By default, exceptions thrown at runtime from a handler or a middleware are not caught by the `reitit.ring/ring-handler`. A good practice is to have a top-level exception handler to log and format errors for clients. ```clj (require '[reitit.ring.middleware.exception :as exception]) @@ -36,7 +36,7 @@ A preconfigured middleware using `exception/default-handlers`. Catches: ### `exception/create-exception-middleware` -Creates the exception-middleware with custom options. Takes a map of `identifier => exception request => response` that is used to select the exception handler for the thrown/raised exception identifier. Exception identifier is either a `Keyword` or a Exception Class. +Creates the exception-middleware with custom options. Takes a map of `identifier => exception request => response` that is used to select the exception handler for the thrown/raised exception identifier. Exception identifier is either a `Keyword` or an Exception Class. The following handlers are available by default: @@ -55,7 +55,7 @@ The handler is selected from the options map by exception identifier in the foll 2) Class of exception 3) `:type` ancestors of exception ex-data 4) Super Classes of exception -5) The ::default handler +5) The `::default` handler ```clj ;; type hierarchy @@ -94,7 +94,7 @@ The handler is selected from the options map by exception identifier in the foll (def app (ring/ring-handler (ring/router - ["/fail" (fn [_] (throw (ex-info "fail" {:type ::failue})))] + ["/fail" (fn [_] (throw (ex-info "fail" {:type ::failure})))] {:data {:middleware [exception-middleware]}}))) (app {:request-method :get, :uri "/fail"}) @@ -102,6 +102,6 @@ The handler is selected from the options map by exception identifier in the foll ; => {:status 500, ; :body {:message "default" ; :exception clojure.lang.ExceptionInfo -; :data {:type :user/failue} +; :data {:type :user/failure} ; :uri "/fail"}} ``` diff --git a/doc/ring/ring.md b/doc/ring/ring.md index 3f282f0a..b80c51eb 100644 --- a/doc/ring/ring.md +++ b/doc/ring/ring.md @@ -11,8 +11,8 @@ Read more about the [Ring Concepts](https://github.com/ring-clojure/ring/wiki/Co ## `reitit.ring/ring-router` `ring-router` is a higher order router, which adds support for `:request-method` based routing, [handlers](https://github.com/ring-clojure/ring/wiki/Concepts#handlers) and [middleware](https://github.com/ring-clojure/ring/wiki/Concepts#middleware). - - It accepts the following options: + +It accepts the following options: | key | description | | ----------------------------------------|-------------| @@ -53,7 +53,7 @@ Given a `ring-router`, optional default-handler & options, `ring-handler` functi | key | description | | ------------------|-------------| -| `:middleware` | Optional sequence of middleware that wrap the ring-handler" +| `:middleware` | Optional sequence of middlewares that wrap the ring-handler | `:inject-match?` | Boolean to inject `match` into request under `:reitit.core/match` key (default true) | `:inject-router?` | Boolean to inject `router` into request under `:reitit.core/router` key (default true) @@ -91,7 +91,7 @@ The router can be accessed via `get-router`: # Request-method based routing -Handlers can be placed either to the top-level (all methods) or under a specific method (`:get`, `:head`, `:patch`, `:delete`, `:options`, `:post`, `:put` or `:trace`). Top-level handler is used if request-method based handler is not found. +Handlers can be placed either to the top-level (all methods) or under a specific method (`:get`, `:head`, `:patch`, `:delete`, `:options`, `:post`, `:put` or `:trace`). Top-level handler is used if request-method based handler is not found. By default, the `:options` route is generated for all paths - to enable thing like [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing). @@ -196,7 +196,7 @@ Top-level middleware, applied before any routing is done: (ring/router ["/api" {:middleware [[mw :api]]} ["/get" {:get handler}]]) - nil + nil {:middleware [[mw :top]]})) (app {:request-method :get, :uri "/api/get"})