defmodule Cache.DETS do @opts_definition [ ram_file: [ type: :boolean, default: false, doc: "Enable RAM File" ], type: [ type: {:in, [:bag, :duplicate_bag, :set]}, default: :set, doc: "Data type of DETS cache" ], file_path: [ type: :string, default: "./", doc: "File path to save DETS file at" ] ] @moduledoc """ DETS adapter so that we can use dets as a cache ## Options #{NimbleOptions.docs(@opts_definition)} """ use Task, restart: :permanent @behaviour Cache @impl Cache def opts_definition, do: @opts_definition @impl Cache def start_link(opts) do Task.start_link(fn -> table_name = opts[:table_name] file_path = opts[:file_path] |> to_string |> create_file_name(table_name) |> tap(&File.mkdir_p!(Path.dirname(&1))) |> String.to_charlist opts = opts |> Keyword.drop([:table_name, :file_path]) |> Kernel.++([access: :read_write, file: file_path]) {:ok, _} = :dets.open_file(table_name, opts) Process.hibernate(Function, :identity, [nil]) end) end defp create_file_name(file_path, table_name) do if File.dir?(file_path) do Path.join(file_path, "#{table_name}.dets") else file_path end end @impl Cache def child_spec({cache_name, opts}) do %{ id: "#{cache_name}_elixir_cache_dets", start: {Cache.DETS, :start_link, [Keyword.put(opts, :table_name, cache_name)]} } end @impl Cache def get(cache_name, key, _opts \\ []) do case :dets.lookup(cache_name, key) do [{^key, value}] -> {:ok, value} [] -> {:ok, nil} end end @impl Cache def put(cache_name, key, _ttl \\ nil, value, _opts \\ []) do :dets.insert(cache_name, {key, value}) end @impl Cache def delete(cache_name, key, _opts \\ []) do :dets.delete(cache_name, key) end end