Current section
Files
Jump to
Current section
Files
lib/scaffold.ex
defmodule Agency.Scaffold do
@moduledoc false
# credo:disable-for-this-file Credo.Check.Refactor.LongQuoteBlocks
@agency_impl_ast Agency.Impl.agency_impl_ast()
def this(container), do: container
def this(_, container), do: container
@doc false
defmacro __using__(opts) do
[
quote location: :keep do
@into Keyword.get(unquote(opts), :into, %{})
@moduledoc """
The `Agent` backing up the `#{inspect(@into)}` instance.
"""
@behaviour Agency
@behaviour Access
@name Keyword.get(unquote(opts), :name, __MODULE__)
@raw_data Keyword.get(unquote(opts), :data, name: @name)
@data for {k, v} <- @raw_data, do: {k, v}
defstruct @data
use Agent
@doc """
Returns a thing that might be used in `Kernel.***_in`
function family.
"""
@spec access() :: Access.t()
def access, do: %__MODULE__{}
@doc """
Starts the `Agent` that backs up the container
(defaulted to `Map`.)
The argument passed to the function is ignored.
"""
@spec start_link(opts :: keyword()) :: GenServer.on_start()
def start_link(opts \\ []) do
name = Keyword.get(opts, :name, @name)
Agent.start_link(fn -> @into end, name: name)
end
@doc """
Returns the whole container, backed up by the `Agent`.
"""
@spec this(GenServer.name()) :: Access.t()
def this(name \\ @name) do
name
|> Agent.get(Agency.Scaffold, :this, [])
|> after_this()
end
@impl Access
def fetch(%data{}, key) do
case data.get(key) do
nil -> :error
found -> {:ok, found}
end
end
@doc """
Get the value for the specific `key` from the container,
backed up by the `Agent`.
"""
@spec get(GenServer.name(), Agency.keyz()) :: Agency.value()
def get(name \\ @name, key)
def get(name, key) when not is_list(key),
do: get(name, [key])
def get(name, key) do
key = key |> before_all() |> before_get()
name
|> Agent.get(Kernel, :get_in, [key])
|> after_get()
end
@doc """
Get the value for the specific `key` from the container,
backed up by the `Agent`, and updates it.
"""
@spec get_and_update(
GenServer.name(),
Agency.keyz(),
(Agency.value() -> {get_value, update_value} | :pop)
) :: {get_value, Access.container()}
when get_value: Agency.value(), update_value: Agency.value()
def get_and_update(name \\ @name, key, fun)
@impl Access
def get_and_update(%data{}, key, fun) do
old_value = data.get(key)
case fun.(old_value) do
:pop ->
data.pop(key)
{get_value, update_value} ->
data.put(key, update_value)
{get_value, data}
end
end
def get_and_update(name, key, fun) when not is_list(key),
do: get_and_update(name, [key], fun)
def get_and_update(name, key, fun) do
key = key |> before_all() |> before_get_and_update()
name
|> Agent.get_and_update(Kernel, :get_and_update_in, [key, fun])
|> after_get_and_update()
end
@doc """
Pops the `value` for the specific `key` in the container,
backed up by the `Agent`.
"""
@spec pop(GenServer.name(), Agency.keyz()) ::
{Agency.value(), Access.container()}
def pop(name \\ @name, key)
@impl Access
def pop(%data{}, key) do
data.pop(key)
end
def pop(name, key) when not is_list(key),
do: pop(name, [key])
def pop(name, key) do
key = key |> before_all() |> before_pop()
{value, container} = pop_in(this(), key)
Agent.update(name, Agency.Scaffold, :this, [container])
after_pop({value, container})
end
@doc """
Put the `value` under the specific `key` to the container,
backed up by the `Agent`.
"""
@spec put(GenServer.name(), Agency.keyz(), Agency.value()) :: :ok
def put(name \\ @name, key, value)
def put(name, key, value) when not is_list(key),
do: put(name, [key], value)
def put(name, key, value) do
key = key |> before_all() |> before_put()
name
|> Agent.update(Kernel, :put_in, [key, value])
|> after_put()
end
@doc """
Update the `value` for the specific `key` in the container,
backed up by the `Agent`.
"""
@spec update(GenServer.name(), Agency.keyz(), (Agency.value() -> Agency.value())) :: :ok
def update(name \\ @name, key, fun)
def update(name, key, fun) when not is_list(key),
do: update(name, [key], fun)
def update(name, key, fun) do
key = key |> before_all() |> before_update()
name
|> Agent.update(Kernel, :update_in, [key, fun])
|> after_update()
end
end
| @agency_impl_ast
]
end
end