Current section
Files
Jump to
Current section
Files
lib/cachex/actions/invoke.ex
defmodule Cachex.Actions.Invoke do
@moduledoc false
# Command module to enable custom command invocation.
#
# This module relies on commands attached to a cache at startup, and
# does not allow for registration afterward.
#
# Invocations which require writes to the table are executed inside a
# transactional context to ensure consistency.
alias Cachex.Actions
alias Cachex.Services.Locksmith
# add our imports
import Cachex.Spec
import Cachex.Error
##############
# Public API #
##############
@doc """
Invokes a custom command on a cache.
Command invocations allow a developer to attach common functions directly to a
cache in order to easily share logic around a codebase. Values are passed through
to a custom command for a given key, and based on the type of command might be
written back into the cache table.
"""
def execute(cache(commands: commands) = cache, cmd, key, _options) do
commands
|> Map.get(cmd)
|> invoke(cache, key)
end
###############
# Private API #
###############
# Executes a read command on the backing cache table.
#
# Values read back will be passed directly to the custom command implementation.
# It should be noted that expirations are taken into account, and nil will be
# passed through in expired/missing cases.
defp invoke(command(type: :read, execute: exec), cache, key) do
{_status_, value} = Cachex.get(cache, key, [])
{:ok, exec.(value)}
end
# Executes a write command on the backing cache table.
#
# This will initialize a transactional context to ensure that modifications are
# kept in sync with other actions happening at the same time. The return format
# is enforced per the documentation and will crash out if something unexpected
# is returned (i.e. a non-Tuple, or a Tuple with invalid size).
defp invoke(command(type: :write, execute: exec), cache() = cache, key) do
Locksmith.transaction(cache, [key], fn ->
{_label, value} = Cachex.get(cache, key, [])
{return, tempv} = exec.(value)
tempv == value ||
apply(
Cachex,
Actions.write_op(value),
[cache, key, tempv, []]
)
{:ok, return}
end)
end
# Returns an error due to a missing command.
defp invoke(_invalid, _cache, _key),
do: error(:invalid_command)
end