lib/modulex.ex

defmodule Modulex do
  @type t() :: module()

  defmacro __using__(_opts \\ []) do
    quote do
      @before_compile unquote(__MODULE__)
      require unquote(__MODULE__).Macros
      import unquote(__MODULE__)
    end
  end

  defmacro defbehaviour(do: block) do
    quote do
      defmodule Behaviour do
        @moduledoc """
        Implements the behaviour for the module that contains this behaviour.
        """
        unquote(block)
      end
    end
  end

  defmacro defimplementation(do: block) do
    quote do
      defmodule Implementation do
        @moduledoc """
        Contains the implementation for the module that contains this module.
        """
        @behaviour Module.split(__MODULE__) |> List.replace_at(-1, :Behaviour) |> Module.concat()

        unquote(block)
      end
    end
  end

  defmacro __before_compile__(env) do
    quote do
      @implementation_module __MODULE__.Implementation
      @behaviour_module __MODULE__.Behaviour
      @mock_module __MODULE__.Mock
      @behaviour @behaviour_module

      unquote(__MODULE__).Macros.functions()
      unquote(__MODULE__).Macros.ensure_module_loaded!(@behaviour_module)
      unquote(__MODULE__).Macros.ensure_module_loaded!(@implementation_module)
      unquote(__MODULE__).Macros.define_behaviour_functions(unquote(env))
    end
  end
end