Open ikitommi opened 5 years ago
Interceptor enter differs from handler in that interceptor enter is called and is expected to returns ctx, where as the handler is called with request and expected to return response.
If we change IntoInterceptor
for functions to returns interceptor with the function as enter, then we need some other way to handle handler.
Perhaps what ever is last in queue is assumed to be handler?
Or, we could make it so that every element in queue is an interceptor, and we just provide utility to convert handler into interceptor and require that the caller does the conversion. Something like:
(s/execute [some-interceptor other-interceptor (handler->interceptor my-handler)])
What do you think?
I think it's a good idea. And for ergonomics, s/handler
would be better handler->interceptor
. Also, like discussed in #15, the request-response could be a extra pattern on top of the core, which just runs contexts e2e.
It would be more symmetric and principled to have both enter-fn->interceptor
and leave-fn->interceptor
(or perhaps preprocessor
and postprocessor
) and having the handler be just a function. It would be more verbose but usually there aren't that many interceptors and the code is probably not modified as often as read.
Forcing the handler to be of interceptor type does not make much sense to me since it is a special case anyway because the context turns around and/or request is switched to response.
One thing I've been wondering: does the interceptor pattern inherently imply that the context should contain a :request
key, "make a request" then return with a :response
key? Are these specific keys part of the pattern? Or, if not the specific keys, the idea that something (besides the context) goes through a chain of functions, then something else comes back, and both these things are separated from the surrounding context?
Or is it more general, does the interceptor pattern only imply a context goes through the interceptors one way, then back another, and :request
/:response
just happen to often be useful?
I think answering this question would help answer how a function should be interpreted as an interceptor. If there is no separate concept of request/response, then clearly a function shouldn't work be a request->response handler.
I have been thinking about using the interceptor pattern. I currently have the request just as subset of the context itself. There is no real "standard" defining my request so I didn't initially see a need to seperate it from surrounding context. The context is built up from the chain of interceptors, then at the end I pick out what I need to make the request and return back up the chain. For what it's worth, I'm beginning to think maybe this is bad and I more clearly define my "request" and put it into a :request
key.
The handler could also be a function ctx -> ctx
... but really the handler should work like in prior art (Pedestal, re-frame). The request-response convention is nice, but not the only reasonable one (e.g. re-frame has coeffects and effects instead), so a general purpose library like this should not force its use.
There is a related issue of making the request & response optional: https://github.com/metosin/sieppari/issues/18
If functions would be mapped into interceptor enter, instead of special handler case, it would be easier to write one-way chains:
Instead of:
one could say:
... inlined:
https://github.com/metosin/sieppari/blob/develop/src/sieppari/interceptor.cljc#L30-L35