lib/live_query/core/store.ex

defmodule LiveQuery.Core.Store do
  # CONTRACT

  @callback start_link(term()) :: {:ok, pid(), term()}
  @callback read(system :: atom(), key :: term()) :: {:ok, term()} | {:error, :not_found}
  @callback set(system :: atom(), key :: term(), value) :: {:ok, value} when value: term()
  @callback unset(system :: atom(), key :: term()) :: :ok

  defmacro __using__(opts) do
    quote location: :keep, bind_quoted: [opts: opts] do
      @behaviour LiveQuery.Core.Store

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

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

      defoverridable child_spec: 1
    end
  end

  # API

  alias LiveQuery.Core.System
  alias LiveQuery.Core.Config

  def child_spec(system) do
    %{
      id: __MODULE__,
      start: {__MODULE__, :start_link, [system]}
    }
  end

  def start_link(system) do
    store_module = Config.read(system, :store)
    {:ok, pid, name} = store_module.start_link([])
    {:ok, _} = System.register(system, store_module, name)
    {:ok, pid}
  end

  def read(system, key) do
    store_module = Config.read(system, :store)
    {:ok, name} = System.lookup(system, store_module)
    store_module.read(name, key)
  end

  def set(system, key, value) do
    store_module = Config.read(system, :store)
    {:ok, name} = System.lookup(system, store_module)
    store_module.set(name, key, value)
    :ok = System.broadcast(system, "store:#{inspect(key)}", :set)
  end

  def unset(system, key) do
    store_module = Config.read(system, :store)
    {:ok, name} = System.lookup(system, store_module)
    store_module.unset(name, key)
    :ok = System.broadcast(system, "store:#{inspect(key)}", :unset)
  end
end