io.pedestal.interceptor.chain

The implementation of the interceptor pattern, where a context map is passed through callbacks supplied by an extensible queue of interceptors, with provisions for error handling, observations, asynchronous executions, and other factors.

add-observer

added in 0.7.0

(add-observer context observer-fn)

Adds an observer function to the execution; observer functions are notified after each interceptor executes. If the interceptor is asynchronous, the notification occurs once the new context is conveyed through the returned channel.

The function is passed an event map:

Key Type Description
:execution-id integer Unique per-process id for the execution
:stage :enter, :leave, or :error
:interceptor-name keyword or string The interceptor that was invoked (either its :name or a string)
:context-in map The context passed to the interceptor
:context-out map The context returned from the interceptor

The observer is only invoked for interceptor executions; when an interceptor does not provide a callback for a stage, the interceptor is not invoked, and so the observer is not invoked.

The value returned by the observer is ignored.

If an observer throws an exception, it is associated with the interceptor, exactly as if the interceptor had thrown the exception.

When multiple observer functions are added, they are invoked in an unspecified order.

The debug-observer function is used to create an observer function; this observer can be used to log each interceptor that executes, in what stage it executes, and how it modifies the context map.

bind

macro

added in 0.7.0

(bind context var value)

Updates the context to add a binding of the given var and value. This is a convenience on modifying the :bindings key (a map of Vars and values).

Bound values will be available in subsequent interceptors.

enqueue

(enqueue context interceptors)

Adds interceptors to the end of context’s execution queue. Creates the queue if necessary. Returns updated context.

enqueue*

(enqueue* context & interceptors-and-seq)

Like enqueue but accepting a variable number of arguments. If the last argument is itself a sequence of interceptors, they’re unpacked and added to the context’s execution queue.

execute

(execute context)(execute context interceptors)

Executes a queue of Interceptors attached to the context. Context must be a map, Interceptors are added with ‘enqueue’.

An Interceptor is record with the keys :enter, :leave, and :error. The value of each key is a callback function; missing keys or nil values are ignored.

Each Interceptor may also have a :name, which is used when logging. This is encouraged.

When executing a context, first all the :enter functions are invoked in order.

When :enter execution reaches the end of the queue, it switches to the :leave stage. Each Interceptor’s :leave function is invoked, in the opposite order from the :enter phase.

Both the :enter and :leave functions are passed a single argument, the context map, and return an updated context.

If any Interceptor function throws an exception, execution immediately switches to the :error stage. Interceptors are now considered in reverse order (like the :leave stage), but the :error callback (if non-nil) is passed the context and a wrapped version of the caught exception.

The :error callback may either handle the exception, in which case the execution switches to the :leave stage; or the callback may re-throw the exception, or attach it as the ::error key.

If the exception reaches the end of the stack without being handled, execute will throw it.

Interceptor callbacks may return a core.async channel; this represents an asynchronous process. When this happens, the initial call to execute returns nil immediately, with the process exepected to write an updated context into the channel when its work completes.

The function on-enter-async is used to provide a callback for when an interceptor chain execution first switches from in-thread to asynchronous execution.

Processing continues in core.async threads - including even when a later interceptor returns an immediate context, rather than a channel.

on-enter-async

added in 0.7.0

(on-enter-async context f)

Adds a callback function to be executed if the execution goes async, which occurs when an interceptor returns a channel rather than a context map.

The supplied function is appended to the list of such functions. All the functions are invoked, but only invoked once (a subsequent interceptor also returning a channel does not have this side effect.

The callback function will be passed the context, but any returned value from the function is ignored.

queue

added in 0.7.0

(queue context)

Returns the contents of the queue, the as-yet uninvoked interceptors during the :enter phase of chain execution.

Prior to 0.7.0, this was achieved by accessing the :io.pedestal.interceptor.chain/queue key; future enhancements may change how the interceptor queue and stack are stored.

terminate

(terminate context)

Removes all remaining interceptors from context’s execution queue. This effectively short-circuits execution of Interceptors’ :enter functions and begins executing the :leave functions.

terminate-when

(terminate-when context pred)

Adds a predicate establishing a terminating condition for execution of the interceptor chain.

pred is a function that takes a context as its argument. It will be invoked after every Interceptor’s :enter function. If pred returns logical true, execution will stop at that Interceptor.

unbind

macro

added in 0.7.0

(unbind context var)

Updates the context to remove a previous binding.