lib/blunt/testing/ex_machina.ex

if Code.ensure_loaded?(ExMachina) and Code.ensure_loaded?(Faker) do
  defmodule Blunt.Testing.ExMachina do
    alias Blunt.Testing.ExMachina.Factory

    defmacro __using__(opts) do
      repo = Keyword.get(opts, :repo)

      quote do
        use Blunt.Testing.ExMachina.DispatchStrategy

        if unquote(repo) do
          use ExMachina.Ecto, repo: unquote(repo)
        else
          use ExMachina
        end

        import Blunt.Testing.ExMachina, only: :macros
        import Blunt.Testing.ExMachina.Values, only: :macros
      end
    end

    defmacro factory(message) do
      factory_name = factory_name(message, [])
      create_factory(factory_name, message: message, values: [])
    end

    defmacro factory(message, do: body) do
      values = extract_values(body)
      factory_name = factory_name(message, [])
      create_factory(factory_name, message: message, values: values)
    end

    defmacro factory(message, opts) do
      factory_name = factory_name(message, opts)
      create_factory(factory_name, message: message, values: [])
    end

    defmacro factory(message, opts, do: body) do
      values = extract_values(body)
      factory_name = factory_name(message, opts)
      create_factory(factory_name, message: message, values: values)
    end

    defp extract_values({:__block__, _meta, elements}), do: elements
    defp extract_values(nil), do: []
    defp extract_values(element), do: [element]

    def create_factory(name, opts) do
      message = Keyword.fetch!(opts, :message)
      values = Keyword.fetch!(opts, :values)

      quote do
        def unquote(name)(attrs) do
          Factory.build(%Factory{message: unquote(message), values: unquote(values)}, attrs, unquote(opts))
        end
      end
    end

    defp factory_name({:__aliases__, _meta, message}, opts) do
      case Keyword.get(opts, :as, nil) do
        name when is_atom(name) and not is_nil(name) ->
          String.to_atom(to_string(name) <> "_factory")

        _ ->
          message
          |> List.last()
          |> to_string()
          |> Macro.underscore()
          |> Kernel.<>("_factory")
          |> String.to_atom()
      end
    end
  end
end