-
-
Notifications
You must be signed in to change notification settings - Fork 716
Writing Handler Middleware
This page has nothing to do with ring middleware. It is about re-frame middleware used in the client. It has nothing to do with servers handling requests.
Sometimes I feel like I’m just pretending to have imposter syndrome.
A Middleware is a function.
(handler) -> handler
It takes one parameter, an event handler. And it will return an event handler.
But here's the thing: it won't return a real handler - it will return something that pretends to be a handler.
This imposter handler will often modify the behavior of the original handler, or side-effect in some useful way.
Every Middleware function fits this template:
(defn my-middleware
[handler] ;; <--- middleware takes a handler argument
... in here we return a handler)
And almost 100% of Middleware matches this more fleshed-out template:
(defn my-middleware
[handler] ;; <--- Middleware takes a handler argument
(fn imposter-handler ;; <--- returns what looks like a handler to me
[db v] ;; <--- yep takes the right parameters
... something in here))) ;; <--- what happens in here ??
This is the simplest possible Middleware. But it isn't much of an imposter. It actually returns the original handler untouched. Duh! It was thrown out of middleware school.
(defn does-nothing-middleware
[handler]
handler)) ;; give back the same handler
Here's a slightly more complicated version of the do-nothing Middleware:
(defn almost-does-nothing-middleware
[handler]
(fn imposter-handler
[db v]
(handler db v))) ;; when called, just on-calls the original handler
Due to someone else's mistake (probably the guys in marketing), the following Middleware has a bug. It always forwards the wrong event.
(defn buggy-middleware
[handler]
(fn imposter-handler
[db v]
(handler db [:send-spam true]))) ;; yikes. on-calls with the wrong 'v'
Events (v
) always have the form: [:some-id some other parameters]
. Good if we could trim off that annoying first element :some-id
. How about this ...
(defn trim-v
[handler]
(fn imposter-handler
[db v]
(handler db (rest v)))) ;; get rid of the pesky first element of v
Simple, but slightly risky. v
comes in as a vector
, but is passed on as a list
. Perhaps we should put a vec
around the rest
.
I earlier claimed that Middleware should really be called Outsideware
because it normally does something either before or after the call to the real handler. Either side. Like the bread either side of ham in a sandwich.
Here is an more sophisticated template:
(defn my-middleware
[handler]
(fn imposter-handler
[db v]
(let [ ...... do something BEFORE the handler is called
val (handler db v) ;; <---- handler called here
...... do something AFTER the handler is called]
... return val ?)))
When Middleware does something in the BEFORE position, it is often altering db
or v
, ahead of passing them into handler
. trim-v
was this kind of Middleware - it altered v
before on-calling.
When Middleware does something in the AFTER position it is often modifying the value returned.
But not always. A great many Middleware don't change db
or v
at all, and instead they just need to be in the pipeline to produce a side effect, like logging.
Okay, now we get interesting.
By default, the re-frame router will pass app-db
as the first handler parameter. But that's an atom. If, in our handler, we we start using reset!
and swap!
on this atom, we'll be writing functions which side-effect. That's ugly. We want to write pure functions.
We'd like to write pure functions which take a map
(the value in the atom) and then return a modified map
.
(defn my-handler
[m v] ;; <-- if first position, not an atom, a map
(assoc m :driven :snow))
But how do we register
such a handler? The answer is that we have to wrap the handler in the right Middleware called pure
.
(register-handler
:white-event
(pure my-handler) ;; <--- pure is middleware which 'adapts' my-handler
Look at pure
in:
https://github.com/Day8/re-frame/blob/master/src/re_frame/middleware.cljs
A Middleware Factory
is a function which returns Middleware. It provides a way of parameterizing the behavior of a Middleware.
Every Middleware Factory
fits this template:
(defn a-factory
[params]
(fn middleware ;; <-- returns a middleware
[handler] ;; <--- middleware takes a handler argument
(fn imposter-handler ;; <--- returns what looks like a handler to me
[db v] ;; <--- yep takes the right parameters
... something in here))) ;; <--- what happens in here ??
Notice that our imposter-handler
closes over params
.
Look at path
in:
https://github.com/Day8/re-frame/blob/master/src/re_frame/middleware.cljs
Deprecated Tutorials:
Reagent: