Current section

Files

Jump to
cache_me lib cache_me caches ets.ex
Raw

lib/cache_me/caches/ets.ex

defmodule CacheMe.Cache.ETS do
@moduledoc false
import CacheMe.AST, only: [strip_defaults: 1]
def generate(module, kind, name, args, opts) do
cache = :"__cache_#{module}__#{name}_#{Enum.count(args)}"
original = :"__cached_#{name}"
stripped = strip_defaults(args)
key = Macro.var(:key, nil)
ttl =
case Keyword.get(opts, :ttl, :infinity) do
:infinity ->
nil
time when is_integer(time) and time > 0 ->
quote do
Process.send_after(
unquote(__MODULE__),
{:ttl, unquote(cache), unquote(key)},
unquote(time)
)
end
end
cached =
quote do
unquote(key) = {unquote(name), unquote_splicing(stripped)}
try do
case :ets.lookup(unquote(cache), unquote(key)) do
[{_, v}] ->
v
_ ->
result = unquote(original)(unquote_splicing(stripped))
:ets.insert(unquote(cache), {unquote(key), result})
unquote(ttl)
result
end
rescue
_ ->
unquote(__MODULE__).create(unquote(cache))
unquote(name)(unquote_splicing(stripped))
end
end
quote do
Kernel.unquote(kind)(unquote(name)(unquote_splicing(args)), unquote(do: cached))
end
end
### Management ###
use GenServer
def create(table, opts \\ [:named_table, :set, :public, read_concurrency: true])
def create(table, opts), do: GenServer.call(__MODULE__, {:create, table, opts})
def start_link(opts \\ [])
def start_link(_), do: GenServer.start_link(__MODULE__, [], name: __MODULE__)
@impl GenServer
def init(opts)
def init(_), do: {:ok, []}
@impl GenServer
def handle_call({:create, name, opts}, _from, tables) do
if name in tables do
{:reply, :ok, tables}
else
:ets.new(name, opts)
{:reply, :ok, [name | tables]}
end
end
@impl GenServer
def handle_info({:ttl, name, key}, tables) do
:ets.delete(name, key)
{:noreply, tables}
end
end