interceptor
interceptor copied to clipboard
Library to easily intercept Elixir function calls

The Interceptor library allows you to intercept function calls, by configuring
the interception functions and using the Interceptor.intercept/1 macro or the
@intercept true annotation.
Installation
The package can be installed by adding interceptor to your list of
dependencies in mix.exs:
def deps do
[
{:interceptor, "~> 0.5.4"}
]
end
Getting started
Create a module using the Interceptor.Configurator module:
defmodule Interception.Config do
use Interceptor.Configurator
intercept "Intercepted.abc/1",
before: "MyInterceptor.intercept_before/1",
after: "MyInterceptor.intercept_after/2"
# there's also `on_success`, `on_error`
# and `wrapper` callbacks available!
intercept "Intercepted.private_hello/1",
on_success: "MyInterceptor.intercept_on_success/3"
end
Point to the previous configuration module in your configuration:
# [...]
config :interceptor,
configuration: Interception.Config
Define your interceptor module, which contains the callback functions:
defmodule MyInterceptor do
def intercept_before(mfa),
do: IO.puts "Intercepted #{inspect(mfa)} before it started."
def intercept_after(mfa, result),
do: IO.puts "Intercepted #{inspect(mfa)} after it completed. Its result: #{inspect(result)}"
def intercept_on_success(mfa, result, _start_timestamp),
do: IO.puts "Intercepted #{inspect(mfa)} after it completed successfully. Its result: #{inspect(result)}"
end
In the module that you want to intercept (in our case, Intercepted), place
the functions that you want to intercept inside a Interceptor.intercept/1
block. If your functions are placed out of this block or if they don't have
a corresponding interceptor configuration, they won't be intercepted.
In the next snippet, the Intercepted.foo/0 function won't be intercepted
because it's out of the Interceptor.intercept/1 do-block. Notice that you
can also intercept private functions.
defmodule Intercepted do
require Interceptor, as: I
I.intercept do
def abc(x), do: "Got #{inspect(x)}"
defp private_hello(y), do: "Hello #{inspect(y)}"
end
def foo, do: "Hi there"
end
Alternatively, you can use the Interceptor.Annotated module and rely on
the @intercept true "annotation":
defmodule Intercepted do
use Interceptor.Annotated
@intercept true
def abc(x), do: "Got #{inspect(x)}"
@intercept true
defp private_hello(y), do: "Hello #{inspect(y)}"
def foo, do: "Hi there"
end
Now when you run your code, whenever the Intercepted.abc/1 function is
called, it will be intercepted before it starts and after it completes.
Whenever the Intercepted.private_hello/1 executes successfully, the
corresponding callback will also be called.
You also have on_error and wrapper callbacks. Check the full documentation
for further examples and other alternative configuration approaches.
Wildcarded interception configuration
If you want to intercept all the Intercepted module functions without
having to specify an intercept Intercepted.<function>/<arity>, ... entry for
each function on the Interception.Config module, you can now use wildcards 😎.
The following configuration lets us intercept every Intercepted function
(inside the Interceptor.intercept/1 block or annotated with the
@intercept true attribute).
defmodule Interception.Config do
use Interceptor.Configurator
intercept "Intercepted.*/*",
before: "MyInterceptor.intercept_before/1",
after: "MyInterceptor.intercept_after/2"
end
More info
You can find the library documentation at https://hexdocs.pm/interceptor.
You can also find the changelog here.
TODO
- Update docs to mention how to understand if we're trying to intercept non-existing functions with the
Interceptor.Configuration.Validatormodule;