Some Clojure/Script implementations:
- Pedestal, mother of all intercepting in cls?
- Re-frame - cljs
- Tripod - polished Pedestal version
- Kekkonen - polished Pedestal version
- Yada - just one way
- Eines - interceptors disguised as middleware
- Sieppari - Clean slate, for clj & cljs (upcoming)
- Reitit - uses the Sieppari model
- A Model of Interceptors by Erik Normand
- each work bit differently (
:enter
vs:before
) - there is no Interceptor Spec (like there is RING SPEC)
- Different async models: core.async, manifold & callbacks.
- Pedestal is clj only, opinionated towards
core.async
, uses pedestal-spesific keys, lot's of extra features yielding some overhead - Re-frame is bit different in all the ways
- Separate ways
- let's not agree on anything, there will many ways to do this. Each implementation will have it's own fans and support libs.
- not good for the Clojure/Script as a community.
- a new Interceptor SPEC
- a new and neutral Interceptor SPEC
- needs breaking changes (for Pedestal)
- support for both Clojure & ClojureScript
- support for pluggable async steps
- no implementation classes
- bare minimum
- e.g. extra keys like
:requires
and:provides
for dependent interceptors are out of the spec - same for
reitit
-spesific keys like:spec
and:compile
- e.g. extra keys like
:enter
,:leave
&:error
, as in Pedestal.
-
option 1:
- execution context is the request, response or error value. e.g. enters are
request->request
. - interceptors can't store local state, which is bad
- execution context is the request, response or error value. e.g. enters are
-
option 2:
- execution context is a map-like
context
, which holds:request
,:response
and:error
keys - allows interceptors to contain local state, e.g. in
:enter
a current time is stored, in:leave
we calculate how long time the execution took
- execution context is a map-like
key | description |
---|---|
:request |
optional request of anything, available on all stages |
:response |
optional response of anything, available on the :leave stage |
:error |
exact thwon Exception/Error, available on the :error stage |
==> option 2
Can be presented as a Map-like structure with:
key | description |
---|---|
:name |
optional Keyword name |
:enter |
optional Context => IntoContext function, going in, Context has the :request key |
:leave |
optional Context => IntoContext function, going out, Context has the :response key |
:error |
optional Context => IntoContext function, on error (out), Context has the :error key |
- Do we need a small shared namespace for the
IntoContext
protocol to make library interceptors support async? - bit different than in Pedestal
Not part of a spec: optional last interceptor in a chain that doesn't see the context but is a request
-> response
function. Provides bridge for existing non-interceptors impls like ring-handlers. Handlers could be defined as interceptor-type maps too. The runner implentations should convert functions into interceptors (see IntoInterceptor
).
Interceptor runner will run the given chain of interceptors with a given request or context. Runners are out of the scope of the interceptor spec.
(execute 1 [{:enter #(update % :request inc)}])
; => 2
;; the handler case, function of request->response as last step
(execute 1 [{:enter #(update % :request )} inc])
; => 3
- option 1:
:queue
ans:stack
as extra mandatory keys in context, "the Pedestal model / everything is an interceptor"- allows interceptors to modify the queue, e.g. router pushes route-spesific interceptors to the queue
- bit slower and more complex implementations
- most of the things could be done with static queue manipulation?
- is this really needed?
- option 2:
- static execution queue
- enables faster implementations
- is this ok?
- option 3:
- interceptors can define wether they want to manipultate the queue via a special key
:queue
, with valid values:static
or:dynamic
-> interceptor runner can optimize itself if no:dynamic
interceptors are mounted. - NOTE: most if not all of the library spesific interceptors can be
:static
!!
- interceptors can define wether they want to manipultate the queue via a special key
=> Comments!
- context-functions (
:enter
,:leave
&:error
) can return async context, which the interceptor runner will handle in async fashion. - out of spec how to interpret the async return values? a shared small lib with some protocols?
Implementation detail, how different Executors turn different clojure data into optimized Interceptors. e.g. reitit has a custom compilation stage, not part of the spec.
A Protocol to execute a interceptor chain on a given context, e.g. with a function of [Interceptor] Context => IntoContext
. Not needed in the spec?
- enables cross-use like
reitit
as a routing engine in Pedestal orsieppari
as a interceptor engine inreitit
.