Current section
Files
Jump to
Current section
Files
lib/commanded/middleware/uniqueness/adapter.ex
defmodule Commanded.Middleware.Uniqueness.Adapter do
@moduledoc """
Provides uniqueness cache adapter behaviour.
Define options in config/config.exs as:
config :commanded_uniqueness_middleware,
adapter: Commanded.Middleware.Uniqueness.Adapter.Cachex,
# ttl: 60 minutes in seconds
ttl: 60 * 60,
use_command_as_partition: true
where:
- `:adapter` is an Uniqueness adapter implemented `Commanded.Middleware.Uniqueness.Adapter` behavior,
- `:ttl` is claimed value time-to-live,
- `:use_command_as_partition` should be set to use each command module name as partition. If neither this nor
`:partition` option defined then `Commanded.Middleware.Uniqueness` value used as a partition name.
"""
@doc """
Returns an adapter child_spec to inject into a supervisor tree
"""
@callback child_spec() :: Supervisor.child_spec()
@doc """
Claims an `key`, `value`, `owner`, `partition` set
or reports that this combination has already been claimed.
If an `key`, `value`, `owner`, `partition` set has to be claimed
and an old value for the given owner exists it releases first.
"""
@callback claim(key :: term, value :: term, owner :: term, partition :: term) ::
:ok | {:error, :already_exists} | {:error, :unknown_error}
@doc """
Claims an `key`, `value`, `partition` set
or reports that this combination has already been claimed.
"""
@callback claim(key :: term, value :: term, partition :: term) ::
:ok | {:error, :already_exists} | {:error, :unknown_error}
@doc """
Releases a value record via `key`, `value`, `owner`, `partition` set
"""
@callback release(key :: term, value :: term, owner :: term, partition :: term) ::
:ok | {:error, :claimed_by_another_owner} | {:error, :unknown_error}
@doc """
Releases a value record via `key`, `owner`, `partition` set
"""
@callback release_by_owner(key :: term, owner :: term, partition :: term) ::
:ok | {:error, :unknown_error}
@doc """
Releases a value record via `key`, `value`, `partition` set
"""
@callback release_by_value(key :: term, value :: term, partition :: term) ::
:ok | {:error, :unknown_error}
###
###
### Functions
###
###
@doc """
Returns the current adapter or `nil`
"""
@spec get :: :atom | nil
def get do
adapter()
end
@doc false
@spec child_spec :: Supervisor.child_spec() | nil
def child_spec do
case adapter() do
nil -> nil
adapter -> adapter.child_spec()
end
end
@doc false
@spec inject_child_spec(children :: list(), opts :: [at: integer() | atom()]) ::
list(Supervisor.child_spec())
def inject_child_spec(children, opts \\ []) when is_list(children) do
case(child_spec()) do
nil ->
children
child ->
index = get_position(opts)
List.insert_at(children, index, child)
end
end
@positions [
first: 0,
last: -1
]
defp adapter do
case Application.get_env(:commanded_uniqueness_middleware, :adapter) do
nil ->
nil
adapter ->
case Code.ensure_loaded?(adapter) do
true -> adapter
_ -> nil
end
end
end
defp get_position(opts) when is_list(opts), do: opts |> Keyword.get(:at) |> translate_position()
defp translate_position(nil), do: translate_position(:first)
defp translate_position(position) when is_atom(position),
do: translate_position(Keyword.get(@positions, position))
defp translate_position(position) when is_integer(position), do: position
defp translate_position(_),
do: raise("#{__MODULE__}: :at option should be either #{inspect(@positions)} or integer")
end