lib/together.ex

defmodule Together do
  @moduledoc ~S"""
  Group actions that can be handled / responded to later together

  ## What for?

  - group notifications to be sent in *one* email
    - cancel the previously queued email if another event happens within a short period (type: debounce)
  - make heavy operations happen less often, i.e. refresh some global statistics
    - allow only 1 operation per certain period (type: throttle)
  - protect some write api
    - additonally you can choose to use the first value in a period (keep: first)
    - or the last value in the period (keep: last)

  ## How to use

  Start `Together.Supervisor` to use it

  ### Start with application configs

      supervisor(Together.Supervisor, [])

  ### Start with configs passed in

      supervisor(Together.Supervisor, [workers: ..., store: ...])

  See `Together.Supervisor` for full configuration information

  Make calls to the worker process:

      Together.process(binary_name, "something_unique", some_func)
      Together.process(pid, "some_unique_name_or_id", a_function)
      Together.process(Together.Worker, "id", Module, :func, [arg1, arg2, ...])
  """

  @registry_name Together.WorkerRegistry

  @doc ~S"""
  put in a function under the id to be processed (invoked) later
  """
  @spec process(binary | GenServer.server, term, fun) :: :ok | no_return
  def process(name, id, func) when is_binary(name) do
    GenServer.call({:via, Registry, {@registry_name, name}}, {:process, id, func})
  end
  def process(server, id, func) do
    GenServer.call(server, {:process, id, func})
  end

  @doc ~S"""
  put in an `mfa` under the id to be processed (applied) later
  """
  @spec process(binary | GenServer.server, term, module, atom, list) :: :ok | no_return
  def process(name, id, m, f, a) when is_binary(name) do
    GenServer.call({:via, Registry, {@registry_name, name}}, {:process, id, {m, f, a}})
  end
  def process(server, id, m, f, a) do
    GenServer.call(server, {:process, id, {m, f, a}})
  end

  @doc ~S"""
  cancels queued action for the given id
  """
  @spec cancel(binary | GenServer.server, term) :: :ok | :error
  def cancel(name, id) when is_binary(name) do
    GenServer.call({:via, Registry, {@registry_name, name}}, {:cancel, id})
  end
  def cancel(server, id) do
    GenServer.call(server, {:cancel, id})
  end
end