mirror of
https://github.com/metosin/reitit.git
synced 2025-12-18 08:51:12 +00:00
welcome, first class data-driven Middleware.
This commit is contained in:
parent
c7c4013f97
commit
76f7f28591
5 changed files with 210 additions and 112 deletions
88
README.md
88
README.md
|
|
@ -7,7 +7,7 @@ A friendly data-driven router for Clojure(Script).
|
||||||
* Generic, not tied to HTTP
|
* Generic, not tied to HTTP
|
||||||
* [Route conflict resolution](#route-conflicts)
|
* [Route conflict resolution](#route-conflicts)
|
||||||
* [Pluggable coercion](#parameter-coercion) ([clojure.spec](https://clojure.org/about/spec))
|
* [Pluggable coercion](#parameter-coercion) ([clojure.spec](https://clojure.org/about/spec))
|
||||||
* Middleware & Interceptors
|
* both Middleware & Interceptors
|
||||||
* Extendable
|
* Extendable
|
||||||
* Fast
|
* Fast
|
||||||
|
|
||||||
|
|
@ -233,7 +233,7 @@ Route trees should not have multiple routes that match to a single (request) pat
|
||||||
|
|
||||||
## Ring
|
## Ring
|
||||||
|
|
||||||
[Ring](https://github.com/ring-clojure/ring)-router adds support for [handlers](https://github.com/ring-clojure/ring/wiki/Concepts#handlers), [middleware](https://github.com/ring-clojure/ring/wiki/Concepts#middleware) and routing based on `:request-method`.
|
[Ring](https://github.com/ring-clojure/ring)-router adds support for ring [handlers](https://github.com/ring-clojure/ring/wiki/Concepts#handlers), [middleware](https://github.com/ring-clojure/ring/wiki/Concepts#middleware) and routing based on `:request-method`. Ring-router is created with `reitit.ring/router` function. It validates that all paths have a `:handler` defined and expands `:middleware` to create accumulated handlers for all request-methods. `reitit.ring/ring-handler` creates an actual ring handler out of a ring-router.
|
||||||
|
|
||||||
Simple Ring app:
|
Simple Ring app:
|
||||||
|
|
||||||
|
|
@ -295,6 +295,11 @@ Reverse routing:
|
||||||
|
|
||||||
### Middleware
|
### Middleware
|
||||||
|
|
||||||
|
`:middleware` should be a vector of either of the following (expanded via the `reitit.middleware/ExpandMiddleware`:
|
||||||
|
|
||||||
|
1. a ring middleware function of `handler -> request -> response`
|
||||||
|
2. a vector of middleware function (`handler args -> request -> response`) and it's args - actial middleware is created by applying function with handler and args
|
||||||
|
|
||||||
Let's define some middleware and a handler:
|
Let's define some middleware and a handler:
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
|
|
@ -335,6 +340,50 @@ Middleware is applied correctly:
|
||||||
; {:status 200, :body [:api :admin :db :delete :handler]}
|
; {:status 200, :body [:api :admin :db :delete :handler]}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Middleware Records
|
||||||
|
|
||||||
|
Besides just being opaque functions, middleware can be presented as first-class data entries, `reitit.middleware/Middleware` records. They are created with `reitit.middleware/create` function and must have a `:name` and either `:wrap` or `:gen` key with the actual middleware function or a [middleware generator function](#compiling-middleware).
|
||||||
|
|
||||||
|
When routes are compiled, middleware records are unwrapped into normal middleware functions producing no runtime performance penalty. Thanks to the `ExpandMiddleware` protocol, plain clojure(script) maps can also be used - they get expanded into middleware records.
|
||||||
|
|
||||||
|
The previous middleware re-written as records:
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(require '[reitit.middleware :as middleware])
|
||||||
|
|
||||||
|
(def wrap2
|
||||||
|
(middleware/create
|
||||||
|
{:name ::wrap
|
||||||
|
:description "a nice little mw, takes 1 arg."
|
||||||
|
:wrap wrap}))
|
||||||
|
|
||||||
|
(def wrap2-api
|
||||||
|
{:name ::wrap-api
|
||||||
|
:description "a nice little mw, :api as arg"
|
||||||
|
:wrap (fn [handler]
|
||||||
|
(wrap handler :api))})
|
||||||
|
```
|
||||||
|
|
||||||
|
Or as maps:
|
||||||
|
|
||||||
|
```clj
|
||||||
|
(require '[reitit.middleware :as middleware])
|
||||||
|
|
||||||
|
(def wrap3
|
||||||
|
{:name ::wrap
|
||||||
|
:description "a nice little mw, takes 1 arg."
|
||||||
|
:wrap wrap})
|
||||||
|
|
||||||
|
(def wrap3-api
|
||||||
|
{:name ::wrap-api
|
||||||
|
:description "a nice little mw, :api as arg"
|
||||||
|
:wrap (fn [handler]
|
||||||
|
(wrap handler :api))})
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Async Ring
|
### 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 the 2 and 3-arity, so they work with [Async Ring](https://www.booleanknot.com/blog/2016/07/15/asynchronous-ring.html) too.
|
||||||
|
|
@ -399,9 +448,9 @@ Reitit ships with pluggable parameter coercion via `reitit.coercion.protocol/Coe
|
||||||
**NOTE**: to use the spec-coercion, one needs to add the following dependencies manually to the project:
|
**NOTE**: to use the spec-coercion, one needs to add the following dependencies manually to the project:
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
[org.clojure/clojure "1.9.0-alpha17"]
|
[org.clojure/clojure "1.9.0-alpha19"]
|
||||||
[org.clojure/spec.alpha "0.1.123"]
|
[org.clojure/spec.alpha "0.1.123"]
|
||||||
[metosin/spec-tools "0.3.2"]
|
[metosin/spec-tools "0.3.3"]
|
||||||
```
|
```
|
||||||
|
|
||||||
### Ring request and response coercion
|
### Ring request and response coercion
|
||||||
|
|
@ -489,11 +538,11 @@ If either request or response coercion fails, an descriptive error is thrown.
|
||||||
|
|
||||||
The [meta-data extensions](#meta-data-based-extensions) are a easy way to extend the system. Routes meta-data can be trasnformed into any shape (records, functions etc.) in route compilation, enabling easy access at request-time.
|
The [meta-data extensions](#meta-data-based-extensions) are a easy way to extend the system. Routes meta-data can be trasnformed into any shape (records, functions etc.) in route compilation, enabling easy access at request-time.
|
||||||
|
|
||||||
Still, we can do better. As we know the exact route interceptor/middleware is linked to, we can pass the (compiled) route information into the interceptor/middleware at creation-time. It can extract and transform relevant data just for it and pass it into the actual request-handler via a closure. We can do all the static local computations forehand, yielding much lighter runtime processing.
|
Still, we can do better. As we know the exact route interceptor/middleware is linked to, we can pass the (compiled) route information into the interceptor/middleware at creation-time. It can extract and transform relevant data just for it and pass it into the actual request-handler via a closure. We can do all the static local computations forehand, yielding faster runtime processing.
|
||||||
|
|
||||||
For middleware, there is a helper `reitit.middleware/gen` for this. It takes a function of `route-meta router-opts => middleware` and returns a special record extending the internal middleware protocols so it can be mounted as normal middleware. The compiled middleware can also decide no to mount itsef byt returning `nil`. Why mount `wrap-enforce-roles` if there are no roles required for that route?
|
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`. Instead of returning the actual middleware function, the middleware record can also decide no to mount itsef byt returning `nil`. Why mount `wrap-enforce-roles` for a route if there are no roles required for it?
|
||||||
|
|
||||||
To demonstrate the two approaches, below are response coercion middleware written in both ways (found in `reitit.coercion`):
|
To demonstrate the two approaches, below are response coercion middleware written as normal ring middleware function and as middleware record with `:gen`. The actual codes are from `reitit.coercion`:
|
||||||
|
|
||||||
### Naive
|
### Naive
|
||||||
|
|
||||||
|
|
@ -543,18 +592,21 @@ To demonstrate the two approaches, below are response coercion middleware writte
|
||||||
"Generator for pluggable response coercion middleware.
|
"Generator for pluggable response coercion middleware.
|
||||||
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
||||||
and :responses from route meta, otherwise does not mount."
|
and :responses from route meta, otherwise does not mount."
|
||||||
(middleware/gen
|
(middleware/create
|
||||||
(fn [{:keys [responses coercion opts]} _]
|
{:name ::coerce-response
|
||||||
(if (and coercion responses)
|
:gen (fn [{:keys [responses coercion opts]} _]
|
||||||
(let [coercers (response-coercers coercion responses opts)]
|
(if (and coercion responses)
|
||||||
(fn [handler]
|
(let [coercers (response-coercers coercion responses opts)]
|
||||||
(fn
|
(fn [handler]
|
||||||
([request]
|
(fn
|
||||||
(coerce-response coercers request (handler request)))
|
([request]
|
||||||
([request respond raise]
|
(coerce-response coercers request (handler request)))
|
||||||
(handler request #(respond (coerce-response coercers request %)) raise)))))))))
|
([request respond raise]
|
||||||
|
(handler request #(respond (coerce-response coercers request %)) raise)))))))}))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The `:gen` -version is both much easier to understand but also 2-4x faster on basic perf tests.
|
||||||
|
|
||||||
## Merging route-trees
|
## Merging route-trees
|
||||||
|
|
||||||
*TODO*
|
*TODO*
|
||||||
|
|
@ -577,7 +629,7 @@ Routers can be configured via options. Options allow things like [`clojure.spec`
|
||||||
|
|
||||||
| key | description |
|
| key | description |
|
||||||
| -------------|-------------|
|
| -------------|-------------|
|
||||||
| `:path` | Base-path for routes (default `""`)
|
| `:path` | Base-path for routes
|
||||||
| `:routes` | Initial resolved routes (default `[]`)
|
| `:routes` | Initial resolved routes (default `[]`)
|
||||||
| `:meta` | Initial expanded route-meta vector (default `[]`)
|
| `:meta` | Initial expanded route-meta vector (default `[]`)
|
||||||
| `:expand` | Function of `arg opts => meta` to expand route arg to route meta-data (default `reitit.core/expand`)
|
| `:expand` | Function of `arg opts => meta` to expand route arg to route meta-data (default `reitit.core/expand`)
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,10 @@
|
||||||
[lein-cloverage "1.0.9"]
|
[lein-cloverage "1.0.9"]
|
||||||
[lein-codox "0.10.3"]]
|
[lein-codox "0.10.3"]]
|
||||||
:jvm-opts ^:replace ["-server"]
|
:jvm-opts ^:replace ["-server"]
|
||||||
:dependencies [[org.clojure/clojure "1.9.0-alpha17"]
|
:dependencies [[org.clojure/clojure "1.9.0-alpha19"]
|
||||||
[org.clojure/clojurescript "1.9.660"]
|
[org.clojure/clojurescript "1.9.660"]
|
||||||
|
|
||||||
[metosin/spec-tools "0.3.2"]
|
[metosin/spec-tools "0.3.3"]
|
||||||
[org.clojure/spec.alpha "0.1.123"]
|
[org.clojure/spec.alpha "0.1.123"]
|
||||||
|
|
||||||
[criterium "0.4.4"]
|
[criterium "0.4.4"]
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
[reitit.impl :as impl]))
|
[reitit.impl :as impl]))
|
||||||
|
|
||||||
#_(defn get-apidocs [coercion spec info]
|
#_(defn get-apidocs [coercion spec info]
|
||||||
(protocol/get-apidocs coercion spec info))
|
(protocol/get-apidocs coercion spec info))
|
||||||
|
|
||||||
;;
|
;;
|
||||||
;; coercer
|
;; coercer
|
||||||
|
|
@ -62,8 +62,8 @@
|
||||||
result))))))
|
result))))))
|
||||||
|
|
||||||
#_(defn muuntaja-response-format [request response]
|
#_(defn muuntaja-response-format [request response]
|
||||||
(or (-> response :muuntaja/content-type)
|
(or (-> response :muuntaja/content-type)
|
||||||
(some-> request :muuntaja/response :format)))
|
(some-> request :muuntaja/response :format)))
|
||||||
|
|
||||||
(defn response-coercer [coercion model {:keys [extract-response-format]
|
(defn response-coercer [coercion model {:keys [extract-response-format]
|
||||||
:or {extract-response-format (constantly nil)}}]
|
:or {extract-response-format (constantly nil)}}]
|
||||||
|
|
@ -134,18 +134,19 @@
|
||||||
"Generator for pluggable request coercion middleware.
|
"Generator for pluggable request coercion middleware.
|
||||||
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
||||||
and :parameters from route meta, otherwise does not mount."
|
and :parameters from route meta, otherwise does not mount."
|
||||||
(middleware/gen
|
(middleware/create
|
||||||
(fn [{:keys [parameters coercion]} _]
|
{:name ::coerce-parameters
|
||||||
(if (and coercion parameters)
|
:gen (fn [{:keys [parameters coercion]} _]
|
||||||
(let [coercers (request-coercers coercion parameters)]
|
(if (and coercion parameters)
|
||||||
(fn [handler]
|
(let [coercers (request-coercers coercion parameters)]
|
||||||
(fn
|
(fn [handler]
|
||||||
([request]
|
(fn
|
||||||
(let [coerced (coerce-parameters coercers request)]
|
([request]
|
||||||
(handler (impl/fast-assoc request :parameters coerced))))
|
(let [coerced (coerce-parameters coercers request)]
|
||||||
([request respond raise]
|
(handler (impl/fast-assoc request :parameters coerced))))
|
||||||
(let [coerced (coerce-parameters coercers request)]
|
([request respond raise]
|
||||||
(handler (impl/fast-assoc request :parameters coerced) respond raise))))))))))
|
(let [coerced (coerce-parameters coercers request)]
|
||||||
|
(handler (impl/fast-assoc request :parameters coerced) respond raise))))))))}))
|
||||||
|
|
||||||
(defn wrap-coerce-response
|
(defn wrap-coerce-response
|
||||||
"Pluggable response coercion middleware.
|
"Pluggable response coercion middleware.
|
||||||
|
|
@ -182,14 +183,15 @@
|
||||||
"Generator for pluggable response coercion middleware.
|
"Generator for pluggable response coercion middleware.
|
||||||
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
Expects a :coercion of type `reitit.coercion.protocol/Coercion`
|
||||||
and :responses from route meta, otherwise does not mount."
|
and :responses from route meta, otherwise does not mount."
|
||||||
(middleware/gen
|
(middleware/create
|
||||||
(fn [{:keys [responses coercion opts]} _]
|
{:name ::coerce-response
|
||||||
(if (and coercion responses)
|
:gen (fn [{:keys [responses coercion opts]} _]
|
||||||
(let [coercers (response-coercers coercion responses opts)]
|
(if (and coercion responses)
|
||||||
(fn [handler]
|
(let [coercers (response-coercers coercion responses opts)]
|
||||||
(fn
|
(fn [handler]
|
||||||
([request]
|
(fn
|
||||||
(coerce-response coercers request (handler request)))
|
([request]
|
||||||
([request respond raise]
|
(coerce-response coercers request (handler request)))
|
||||||
(handler request #(respond (coerce-response coercers request %)) raise)))))))))
|
([request respond raise]
|
||||||
|
(handler request #(respond (coerce-response coercers request %)) raise)))))))}))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,22 @@
|
||||||
(ns reitit.middleware
|
(ns reitit.middleware
|
||||||
(:require [meta-merge.core :refer [meta-merge]]
|
(:require [meta-merge.core :refer [meta-merge]]
|
||||||
[reitit.core :as reitit])
|
[reitit.core :as reitit]))
|
||||||
#?(:clj
|
|
||||||
(:import (clojure.lang IFn AFn))))
|
|
||||||
|
|
||||||
(defprotocol ExpandMiddleware
|
(defprotocol ExpandMiddleware
|
||||||
(expand-middleware [this meta opts]))
|
(expand-middleware [this meta opts]))
|
||||||
|
|
||||||
(defrecord MiddlewareGenerator [f args]
|
(defrecord Middleware [name wrap create])
|
||||||
IFn
|
|
||||||
(invoke [_]
|
(defn create [{:keys [name gen wrap] :as m}]
|
||||||
(f nil nil))
|
(when-not name
|
||||||
(invoke [_ meta]
|
(throw
|
||||||
(f meta nil))
|
(ex-info
|
||||||
(invoke [_ meta opts]
|
(str "Middleware must have :name defined " m) m)))
|
||||||
(f meta opts))
|
(when (and gen wrap)
|
||||||
#?(:clj
|
(throw
|
||||||
(applyTo [this args]
|
(ex-info
|
||||||
(AFn/applyToHelper this args))))
|
(str "Middleware can't both :wrap and :gen defined " m) m)))
|
||||||
|
(map->Middleware m))
|
||||||
|
|
||||||
(extend-protocol ExpandMiddleware
|
(extend-protocol ExpandMiddleware
|
||||||
|
|
||||||
|
|
@ -32,11 +31,24 @@
|
||||||
:cljs function)
|
:cljs function)
|
||||||
(expand-middleware [this _ _] this)
|
(expand-middleware [this _ _] this)
|
||||||
|
|
||||||
MiddlewareGenerator
|
#?(:clj clojure.lang.PersistentArrayMap
|
||||||
|
:cljs cljs.core.PersistentArrayMap)
|
||||||
(expand-middleware [this meta opts]
|
(expand-middleware [this meta opts]
|
||||||
(if-let [mw (this meta opts)]
|
(expand-middleware (create this) meta opts))
|
||||||
|
|
||||||
|
#?(:clj clojure.lang.PersistentHashMap
|
||||||
|
:cljs cljs.core.PersistentHashMap)
|
||||||
|
(expand-middleware [this meta opts]
|
||||||
|
(expand-middleware (create this) meta opts))
|
||||||
|
|
||||||
|
Middleware
|
||||||
|
(expand-middleware [{:keys [wrap gen]} meta opts]
|
||||||
|
(if gen
|
||||||
|
(if-let [wrap (gen meta opts)]
|
||||||
|
(fn [handler & args]
|
||||||
|
(apply wrap handler args)))
|
||||||
(fn [handler & args]
|
(fn [handler & args]
|
||||||
(apply mw handler args))))
|
(apply wrap handler args))))
|
||||||
|
|
||||||
nil
|
nil
|
||||||
(expand-middleware [_ _ _]))
|
(expand-middleware [_ _ _]))
|
||||||
|
|
@ -56,9 +68,6 @@
|
||||||
(keep identity)
|
(keep identity)
|
||||||
(apply comp identity)))
|
(apply comp identity)))
|
||||||
|
|
||||||
(defn gen [f & args]
|
|
||||||
(->MiddlewareGenerator f args))
|
|
||||||
|
|
||||||
(defn compile-handler
|
(defn compile-handler
|
||||||
([route opts]
|
([route opts]
|
||||||
(compile-handler route opts nil))
|
(compile-handler route opts nil))
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
(ns reitit.middleware-test
|
(ns reitit.middleware-test
|
||||||
(:require [clojure.test :refer [deftest testing is]]
|
(:require [clojure.test :refer [deftest testing is are]]
|
||||||
[reitit.middleware :as middleware]
|
[reitit.middleware :as middleware]
|
||||||
[clojure.set :as set]
|
[clojure.set :as set]
|
||||||
[reitit.core :as reitit])
|
[reitit.core :as reitit])
|
||||||
|
|
@ -26,61 +26,96 @@
|
||||||
(respond (handler request))))
|
(respond (handler request))))
|
||||||
|
|
||||||
(deftest expand-middleware-test
|
(deftest expand-middleware-test
|
||||||
(testing "middleware generators"
|
|
||||||
(let [calls (atom 0)]
|
|
||||||
|
|
||||||
(testing "record generator"
|
(testing "middleware records"
|
||||||
(reset! calls 0)
|
|
||||||
(let [syntax [(middleware/gen
|
(testing ":name is mandatory"
|
||||||
(fn [meta _]
|
(is (thrown-with-msg?
|
||||||
|
ExceptionInfo
|
||||||
|
#"Middleware must have :name defined"
|
||||||
|
(middleware/create
|
||||||
|
{:wrap identity
|
||||||
|
:gen (constantly identity)}))))
|
||||||
|
|
||||||
|
(testing ":wrap & :gen are exclusive"
|
||||||
|
(is (thrown-with-msg?
|
||||||
|
ExceptionInfo
|
||||||
|
#"Middleware can't both :wrap and :gen defined"
|
||||||
|
(middleware/create
|
||||||
|
{:name ::test
|
||||||
|
:wrap identity
|
||||||
|
:gen (constantly identity)}))))
|
||||||
|
|
||||||
|
(testing ":wrap"
|
||||||
|
(let [calls (atom 0)
|
||||||
|
data {:name ::test
|
||||||
|
:wrap (fn [handler value]
|
||||||
(swap! calls inc)
|
(swap! calls inc)
|
||||||
(fn [handler value]
|
(fn [request]
|
||||||
(swap! calls inc)
|
[value request]))}]
|
||||||
(fn [request]
|
|
||||||
[meta value request]))))]
|
|
||||||
app ((middleware/compose-middleware syntax :meta {}) identity :value)]
|
|
||||||
(dotimes [_ 10]
|
|
||||||
(is (= [:meta :value :request] (app :request)))
|
|
||||||
(is (= 2 @calls)))))
|
|
||||||
|
|
||||||
(testing "middleware generator as function"
|
(testing "as map"
|
||||||
(reset! calls 0)
|
(reset! calls 0)
|
||||||
(let [syntax (middleware/gen
|
(let [app ((middleware/compose-middleware [data] :meta {}) identity :value)]
|
||||||
(fn [meta _]
|
(dotimes [_ 10]
|
||||||
|
(is (= [:value :request] (app :request)))
|
||||||
|
(is (= 1 @calls)))))
|
||||||
|
|
||||||
|
(testing "direct"
|
||||||
|
(reset! calls 0)
|
||||||
|
(let [app ((middleware/compose-middleware [(middleware/create data)] :meta {}) identity :value)]
|
||||||
|
(dotimes [_ 10]
|
||||||
|
(is (= [:value :request] (app :request)))
|
||||||
|
(is (= 1 @calls)))))
|
||||||
|
|
||||||
|
(testing "vector"
|
||||||
|
(reset! calls 0)
|
||||||
|
(let [app ((middleware/compose-middleware [[(middleware/create data) :value]] :meta {}) identity)]
|
||||||
|
(dotimes [_ 10]
|
||||||
|
(is (= [:value :request] (app :request)))
|
||||||
|
(is (= 1 @calls)))))))
|
||||||
|
|
||||||
|
(testing ":gen"
|
||||||
|
(let [calls (atom 0)
|
||||||
|
data {:name ::test
|
||||||
|
:gen (fn [meta _]
|
||||||
(swap! calls inc)
|
(swap! calls inc)
|
||||||
(fn [handler value]
|
(fn [handler value]
|
||||||
(swap! calls inc)
|
(swap! calls inc)
|
||||||
(fn [request]
|
(fn [request]
|
||||||
[meta value request]))))
|
[meta value request])))}]
|
||||||
app ((syntax :meta nil) identity :value)]
|
|
||||||
(dotimes [_ 10]
|
(testing "as map"
|
||||||
|
(reset! calls 0)
|
||||||
|
(let [app ((middleware/compose-middleware [data] :meta {}) identity :value)]
|
||||||
|
(dotimes [_ 10]
|
||||||
|
(is (= [:meta :value :request] (app :request)))
|
||||||
|
(is (= 2 @calls)))))
|
||||||
|
|
||||||
|
(testing "direct"
|
||||||
|
(reset! calls 0)
|
||||||
|
(let [app ((middleware/compose-middleware [(middleware/create data)] :meta {}) identity :value)]
|
||||||
|
(dotimes [_ 10]
|
||||||
|
(is (= [:meta :value :request] (app :request)))
|
||||||
|
(is (= 2 @calls)))))
|
||||||
|
|
||||||
|
(testing "vector"
|
||||||
|
(reset! calls 0)
|
||||||
|
(let [app ((middleware/compose-middleware [[(middleware/create data) :value]] :meta {}) identity)]
|
||||||
(is (= [:meta :value :request] (app :request)))
|
(is (= [:meta :value :request] (app :request)))
|
||||||
(is (= 2 @calls)))))
|
(dotimes [_ 10]
|
||||||
|
(is (= [:meta :value :request] (app :request)))
|
||||||
(testing "generator vector"
|
(is (= 2 @calls)))))
|
||||||
(reset! calls 0)
|
|
||||||
(let [syntax [[(middleware/gen
|
|
||||||
(fn [meta _]
|
|
||||||
(swap! calls inc)
|
|
||||||
(fn [handler value]
|
|
||||||
(swap! calls inc)
|
|
||||||
(fn [request]
|
|
||||||
[meta value request])))) :value]]
|
|
||||||
app ((middleware/compose-middleware syntax :meta {}) identity)]
|
|
||||||
(is (= [:meta :value :request] (app :request)))
|
|
||||||
(dotimes [_ 10]
|
|
||||||
(is (= [:meta :value :request] (app :request)))
|
|
||||||
(is (= 2 @calls)))))
|
|
||||||
|
|
||||||
(testing "generator can return nil"
|
|
||||||
(reset! calls 0)
|
|
||||||
(let [syntax [[(middleware/gen
|
|
||||||
(fn [meta _])) :value]]
|
|
||||||
app ((middleware/compose-middleware syntax :meta {}) identity)]
|
|
||||||
(is (= :request (app :request)))
|
|
||||||
(dotimes [_ 10]
|
|
||||||
(is (= :request (app :request)))))))))
|
|
||||||
|
|
||||||
|
(testing "nil unmounts the middleware"
|
||||||
|
(reset! calls 0)
|
||||||
|
(let [syntax [[(middleware/create
|
||||||
|
{:name ::test
|
||||||
|
:gen (fn [meta _])}) :value]]
|
||||||
|
app ((middleware/compose-middleware syntax :meta {}) identity)]
|
||||||
|
(is (= :request (app :request)))
|
||||||
|
(dotimes [_ 10]
|
||||||
|
(is (= :request (app :request))))))))))
|
||||||
|
|
||||||
(deftest middleware-router-test
|
(deftest middleware-router-test
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue