# `DCATR.Manifest.Cache`
[🔗](https://github.com/dcat-r/dcatr-ex/blob/v0.1.0/lib/dcatr/manifest/cache.ex#L1)

In-memory caching for loaded `DCATR.Manifest`s using ETS.

The cache stores loaded manifests to avoid re-parsing and reloading from files
on repeated access.

## Architecture

- **GenServer-based**: Automatically started by the DCAT-R application supervisor
- **ETS storage**: Public ETS table with read concurrency enabled for fast parallel reads
- **Cache key**: Tuples of `{manifest_type, load_path}` ensuring distinct caches per configuration
- **Manual invalidation**: No automatic file watching - use `invalidate/2` or `clear/0` to refresh

## Usage

The cache is transparent when using `DCATR.Manifest.Cache.manifest/2`:

    # First call: loads from files and caches
    {:ok, manifest} = Cache.manifest(DCATR.Manifest)

    # Subsequent calls: returns cached manifest instantly
    {:ok, ^manifest} = Cache.manifest(DCATR.Manifest)

    # Force reload: bypass cache and refresh
    {:ok, new_manifest} = Cache.manifest(DCATR.Manifest, reload: true)

## Cache Invalidation

Manual invalidation strategies:

- **Selective invalidation**: `invalidate(manifest_type, opts)` - Invalidates specific manifest
- **Full clear**: `clear/0` - Removes all cached manifests
- **Reload option**: Pass `reload: true` to `manifest/2` - Bypasses cache and updates it
- **Application restart**: ETS table is recreated, implicitly clearing cache

Note: There is no automatic file watching or invalidation on file changes.

# `cache_key`

```elixir
@type cache_key() :: {DCATR.Manifest.Type.t(), String.t()}
```

# `state`

```elixir
@type state() :: %{table: :ets.table()}
```

GenServer state containing the ETS table reference

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `clear`

```elixir
@spec clear() :: :ok
```

Clears the entire cache.

Useful for testing purposes.

# `invalidate`

```elixir
@spec invalidate(
  DCATR.Manifest.Type.t(),
  keyword()
) :: :ok
```

Removes a specific manifest from the cache.

If the manifest is not cached, this is a no-op.

## Options

- `:load_path` - Custom load path (affects cache key)

# `manifest`

```elixir
@spec manifest(
  DCATR.Manifest.Type.t(),
  keyword()
) :: {:ok, DCATR.Manifest.Type.schema()} | {:error, DCATR.ManifestError.t()}
```

Gets a manifest from the cache or loads it if not present or if reload is requested.

The cache key is `{manifest_type, load_path}`, so manifests with different
load paths are cached separately.

## Options

- `:reload` - When `true`, forces reloading the manifest and updates the cache
- `:load_path` - Custom load path (affects cache key)
- Any other options are passed to `DCATR.Manifest.Loader.load/2`

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

Starts the cache GenServer.

This function is called automatically by the DCAT-R application supervisor.
The `opts` are passed to `GenServer.start_link/3` but are not used for
cache initialization.

---

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