# `PhiaUi.ClassMerger.Cache`
[🔗](https://github.com/charlenopires/PhiaUI/blob/v0.1.17/lib/phia_ui/class_merger/cache.ex#L1)

ETS-backed cache for memoising resolved Tailwind class strings.

Stores `{input_key, resolved_string}` pairs so that identical lists of class
tokens are merged only once per application lifetime. This is the performance
backbone of `PhiaUi.ClassMerger.cn/1`: the first call for a given input list
runs the full resolution pipeline; every subsequent call with the same input
is a direct ETS read.

## Why GenServer as Table Owner?

ETS tables are owned by the Erlang process that created them. If the owning
process exits, the table is garbage-collected and all cached data is lost.
Without a long-lived owner, every application restart (or crash) would
destroy the cache.

`PhiaUi.ClassMerger.Cache` is a `GenServer` whose sole initialisation
side-effect is creating the ETS table. It holds no other meaningful state
(the GenServer state is an empty list `[]`). The GenServer is supervised by
`PhiaUi.Supervisor`, so it is restarted automatically on failure, and the
ETS table is recreated on restart.

## Why `:public` + Direct ETS Calls for Reads and Writes?

Routing all reads and writes through the GenServer's `handle_call/3` would
create a sequential bottleneck: every LiveView process would have to queue
behind one another waiting for the GenServer to process their request.

Instead the table is created as `:public`, which allows any process to call
`:ets.lookup/2` and `:ets.insert/2` directly — bypassing the GenServer
message queue entirely. The GenServer only needs to be involved at
initialisation time to create the table. After that it exists purely to keep
the table alive.

## Why `read_concurrency: true`?

`cn/1` is called far more often than it writes (the cache is warm after the
first few renders). The `:read_concurrency` option switches ETS to an
internal implementation that uses a lock-free read path optimised for this
workload. Multiple LiveView processes can read the same key simultaneously
without any serialisation.

## Table Configuration

| Option              | Value       | Reason                                        |
|---------------------|-------------|-----------------------------------------------|
| `:named_table`      | (flag)      | Allows lookup by module name without a table  |
|                     |             | reference — any process can call the module   |
|                     |             | name as if it were an ETS table identifier.   |
| `:public`           | (flag)      | Any process may read and write without going  |
|                     |             | through the owning GenServer.                 |
| `read_concurrency`  | `true`      | Lock-free read path for high read / low write |
|                     |             | workloads.                                    |

The default ETS table type (`:set`) is used, giving O(1) key lookup via
hashing. Each `{key, value}` pair is unique by key.

## Examples

    iex> PhiaUi.ClassMerger.Cache.get(["px-4", "px-2"])
    nil

    iex> PhiaUi.ClassMerger.Cache.put(["px-4", "px-2"], "px-2")
    "px-2"

    iex> PhiaUi.ClassMerger.Cache.get(["px-4", "px-2"])
    "px-2"

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `get`

```elixir
@spec get(term()) :: String.t() | nil
```

Returns the cached merged class string for `key`, or `nil` if absent.

Reads directly from ETS — no GenServer message is sent. Safe to call from
any process concurrently.

## Examples

    iex> PhiaUi.ClassMerger.Cache.get(["bg-red-500", "bg-blue-500"])
    nil  # cache miss on first call

# `put`

```elixir
@spec put(term(), String.t()) :: String.t()
```

Stores `value` under `key` and returns `value`.

Writes directly to ETS via `:ets.insert/2` — no GenServer message is sent.
Because the table is `:public`, any process can write without serialising
through the GenServer. In the rare case of two processes computing the same
key concurrently, the last writer wins — both will produce the same string,
so this is safe.

## Examples

    iex> PhiaUi.ClassMerger.Cache.put(["text-sm", "text-lg"], "text-lg")
    "text-lg"

# `start_link`

Starts the cache GenServer and registers it under its module name.

The GenServer's `init/1` callback creates the ETS table immediately, so the
table is available as soon as `start_link/1` returns. Called by
`PhiaUi.Supervisor` during application startup.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
