defmodule NavEx.Adapters.ETS do @moduledoc """ NavEx.Adapters.ETS is adapter for keeping user's navigation history utilizing ETS. It uses NavEx.Adapters.ETS.RecordsStorage as the module to interact with the ETS table. ## Adapter config config NavEx.Adapters.ETS, identity_key: "nav_ex_identity", # name of the key in session where the user's identity is saved table_name: :navigation_history # name of the ETS table """ @behaviour NavEx.Adapter import Plug.Conn alias NavEx.Adapters.ETS.RecordsStorage @key_length 128 @session_key Application.compile_env(:nav_ex, :adapter_config)[:identity_key] || "nav_ex_identity" @impl NavEx.Adapter def children, do: [NavEx.Adapters.ETS.RecordsStorage] @impl NavEx.Adapter def insert(%Plug.Conn{request_path: request_path} = conn) do {conn, user_identity} = maybe_insert_identity(conn) {:ok, _result} = RecordsStorage.insert(user_identity, request_path) {:ok, conn} end @impl NavEx.Adapter def list(%Plug.Conn{} = conn) do with {:ok, user_identity} <- get_identity(conn), {:ok, [{_user_identity, list}]} <- RecordsStorage.list(user_identity) do {:ok, list} end end @impl NavEx.Adapter def last_path(%Plug.Conn{} = conn) do conn |> get_identity() |> case do {:ok, user_identity} -> RecordsStorage.last_path(user_identity) error -> error end end @impl NavEx.Adapter def path_at(%Plug.Conn{} = conn, n) do conn |> get_identity() |> case do {:ok, user_identity} -> RecordsStorage.path_at(user_identity, n) error -> error end end ### defp maybe_insert_identity(%Plug.Conn{} = conn) do case get_session(conn, @session_key) do nil -> user_identity = create_user_identity() conn = put_session(conn, @session_key, user_identity) {conn, user_identity} user_identity -> {conn, user_identity} end end defp get_identity(%Plug.Conn{} = conn) do case get_session(conn, @session_key) do nil -> {:error, :not_found} user_identity -> {:ok, user_identity} end end defp create_user_identity do :crypto.strong_rand_bytes(@key_length) |> Base.url_encode64() |> binary_part(0, @key_length) end end