Skip to content

amalbuquerque/interceptor

Repository files navigation

Interceptor

Actions Status Coverage Status hex.pm version hex.pm downloads

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.Validator module;

About

Library to easily intercept Elixir function calls

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages