lib/odd_job/pool.ex

defmodule OddJob.Pool do
  @external_resource readme = Path.join([__DIR__, "../../README.md"])

  @usingdoc readme
            |> File.read!()
            |> String.split("<!-- USINGDOC -->")
            |> Enum.fetch!(1)

  @moduledoc """
  ## The `OddJob.Pool` process

  The `OddJob.Pool` is the supervisor at the top of a pool supervision tree, and is
  responsible for starting and supervising the `OddJob.Pool.Supervisor`, `OddJob.Queue`,
  `OddJob.Async.ProxySupervisor`, and `OddJob.Scheduler.Supervisor`. When you add a
  `OddJob.child_spec/1` to your list of supervised children, or call `OddJob.start_link/1`,
  you are adding an `OddJob.Pool` to your application.

  ## Public functions

  This module defines the public functions `start_link/1` and `child_spec/1`. It is recommended
  that you call these functions from the `OddJob` module namespace instead. See `OddJob` for
  documentation and usage.

  #{@usingdoc}
  """
  @moduledoc since: "0.4.0"

  use Supervisor

  @type name :: atom
  @type options :: [{:name, name} | start_option]
  @type child_spec :: Supervisor.child_spec()
  @type start_option ::
          {:pool_size, non_neg_integer}
          | {:max_restarts, non_neg_integer}
          | {:max_seconds, non_neg_integer}

  @doc false
  @spec child_spec(options) :: child_spec
  def child_spec(opts) when is_list(opts) do
    opts = Keyword.merge([name: __MODULE__], opts)

    opts
    |> super()
    |> Supervisor.child_spec(id: opts[:name])
  end

  @doc false
  @spec start_link(options) :: Supervisor.on_start()
  def start_link(opts) when is_list(opts) do
    {name, init_opts} = Keyword.pop!(opts, :name)
    Supervisor.start_link(__MODULE__, [name, init_opts], name: name)
  end

  @impl Supervisor
  def init([name, _opts] = args) do
    children = [
      {OddJob.Async.ProxySupervisor, name},
      {OddJob.Scheduler.Supervisor, name},
      {OddJob.Queue, name},
      {OddJob.Pool.Supervisor, args}
    ]

    Supervisor.init(children, strategy: :one_for_one)
  end

  @doc false
  @doc since: "0.4.0"
  defmacro __using__(opts) do
    quote location: :keep, bind_quoted: [opts: opts] do
      unless Module.has_attribute?(__MODULE__, :doc) do
        @doc """
        Returns a specification to start this module under a supervisor.
        See `Supervisor`.
        """
      end

      def child_spec(arg) do
        default = %{
          id: __MODULE__,
          start: {__MODULE__, :start_link, [arg]},
          type: :supervisor
        }

        Supervisor.child_spec(default, unquote(Macro.escape(opts)))
      end

      unless Module.has_attribute?(__MODULE__, :doc) do
        @doc """
        Starts an OddJob supervision tree linked to the current process.
        See `OddJob.start_link/1`.
        """
      end

      def start_link([]), do: OddJob.start_link(name: __MODULE__)

      def start_link(arg) do
        IO.warn("""
        Your initial argument was ignored because you have not defined a custom
        `start_link/1` in #{__MODULE__}.
        """)

        start_link([])
      end

      defoverridable child_spec: 1, start_link: 1
    end
  end
end