/behaves

Behaves allows you to check if an Elixir module implements a given Behaviour.

Primary LanguageElixirMIT LicenseMIT

Behaves

Hex Version Hex Downloads License

Behaves can help you if you need to check Elixir modules behaviours at runtime.

Elixir warns at compile-time when you haven't implemented the required functions in a behaviour's implementation. However, there is no elegant built-in way to know whether or not a given module implements another module's behaviour at runtime. Behaves tries to solve this problem.

How to use

Given the following modules definition:

defmodule Parser do
  @callback parse(String.t()) :: {:ok, map()} | {:error, String.t()}
end

defmodule JSONParser do
  @behaviour Parser

  @impl Parser
  def parse(json) do
    # ...
  end
end

Behaves can help you to check if JSONParser implements the Parser behaviour at runtime in different ways (with like_a?/2, like_a/2, like_a!/2 or like_a/1):

JSONParser
|> Behaves.like_a?(Parser)
# => true

JSONParser
|> Behaves.like_a(Parser)
# => :ok

JSONParser
|> Behaves.like_a!(Parser)
# => :ok

JSONParser
|> Behaves.like_a()
# => [Parser]

The returned value informs you of the situation in case of a non-positive check:

Supervisor
|> Behaves.like_a?(Parser)
# => false

Superviour
|> Behaves.like_a(NotAModule)
# => {:not_a_module, NotAModule}

NotAModule
|> Behaves.like_a(Parser)
# => {:not_a_module, NotAModule}

Supervisor
|> Behaves.like_a(File)
# => {:not_a_behaviour, File}

Supervisor
|> Behaves.like_a(Parser)
# => {:not_implemented, Parser}

NotAModule
|> Behaves.like_a!(File)
# ** (ArgumentError) given implementation NotAModule is not a module
#     (behaves 0.1.0) lib/behaves.ex:89: Behaves.like_a!/2
#     iex:23: (file)

Supervisor
|> Behaves.like_a!(NotAModule)
# ** (ArgumentError) given behaviour NotAModule is not a module
#     (behaves 0.1.0) lib/behaves.ex:86: Behaves.like_a!/2
#     iex:23: (file)

Supervisor
|> Behaves.like_a!(File)
# ** (ArgumentError) given behaviour File is not a behaviour
#     (behaves 0.1.0) lib/behaves.ex:83: Behaves.like_a!/2
#     iex:23: (file)

Supervisor
|> Behaves.like_a!(Parser)
# ** (Behaves.NotImplementedError) Supervisor does not implement Parser
#     (behaves 0.1.0) lib/behaves.ex:80: Behaves.like_a!/2
#     iex:24: (file)

Supervisor
|> Behaves.like_a()
# => []

NotAModule
|> Behaves.like_a()
# => {:not_a_module, NotAModule}

Installation

Behaves can be installed by adding the line below in your Mix dependencies:

def deps do
  [
    {:behaves, "~> 0.1.0"}
  ]
end

Documentation

Documentation can be found at https://hexdocs.pm/behaves.

Tests

Tested with Elixir 1.15.7 (should be fine from 1.9) and Erlang/OTP 26.1.2.

Inspiration

Inspired by behave, but Behaves has more features. Moreover, it has a better API considering A |> Behaves.like_a(B) better than Behave.behaviour_implemented?(A, B).