Current section
Files
Jump to
Current section
Files
lib/cachex/macros/boilerplate.ex
defmodule Cachex.Macros.Boilerplate do
@moduledoc false
# Provides shorthand wrappers to a Cachex action, by verifying that the provided
# cache exists before executing an action. If the cache does not exist, an error
# tuple will be returned. This is just convenience instead of having to check in
# every interface function. In addition, we also generate an "unsafe" version of
# each function. Unsafe functions will throw any errors, and return raw results.
# alias the parent module
alias Cachex.ExecutionError
alias Cachex.Macros
alias Cachex.Util
@doc """
This is gross, but very convenient. It will basically define a function for the
main Cachex module, and it short-circuits if the specified GenServer can not be
found. In addition, it builds up a `!` version of the function to return values
or throw errors explicity.
"""
defmacro defcheck(head, do: body) do
explicit_head = gen_unsafe(head)
{ func_name, arguments } = Macros.name_and_args(head)
args_list =
arguments
|> Macro.prewalk([], fn(x, acc) ->
case x do
{ key, _, _ } when key != :\\ ->
{ x, [key|acc] }
_other ->
{ x, acc }
end
end)
|> Util.last_of_tuple
|> Enum.reverse
quote do
def unquote(head) do
if not is_atom(var!(cache)) or GenServer.whereis(var!(cache)) == nil do
{ :error, "Invalid cache name provided, got: #{inspect var!(cache)}" }
else
unquote(body)
end
end
@doc false
def unquote(explicit_head) do
args = binding()
fun_args = Enum.map(unquote(args_list), &(args[&1]))
raise_result(apply(Cachex, unquote(func_name), fun_args))
end
end
end
# allow the "use" syntax
defmacro __using__(_opts) do
quote do
import unquote(__MODULE__)
end
end
@doc """
Neat little guy you can use to wrap the result of a function call returning
an :ok/:error tuple to return just the result or throw the error. This is used
when autogenerating `!` functions.
## Examples
iex> Cachex.Macros.raise_result({ :ok, "value" })
"value"
iex> Cachex.Macros.raise_result({ :error, "value" })
** (RuntimeError) value
"""
def raise_result({ status, value }) do
cond do
status == :error and is_binary(value) ->
raise ExecutionError, message: value
true -> value
end
end
def raise_result(value), do: value
# Converts various function input to an unsafe version by adding a trailing
# "!" to the function name.
defp gen_unsafe({ :when, ctx, [head | tail] }) do
scary_head = gen_unsafe(head)
{ :when, ctx, [scary_head|tail]}
end
defp gen_unsafe(head) do
{ name, _, _ } = head
scary_name = to_string(name) <> "!" |> String.to_atom
put_elem(head, 0, scary_name)
end
end