# `Slither.Store`
[🔗](https://github.com/nshkrdotcom/slither/blob/v0.1.0/lib/slither/store.ex#L1)

Behaviour for ETS-backed shared state with Python-accessible views.

A Store declares tables, their schemas, views for Python access,
and a loader for initial data population. Tables use `:protected`
access so reads are lock-free from any BEAM process while writes
are serialized through the owning GenServer.

## Example

    defmodule MyApp.FeatureStore do
      @behaviour Slither.Store

      @impl true
      def tables do
        [
          %{name: :features, type: :set, read_concurrency: true},
          %{name: :user_index, type: :ordered_set, read_concurrency: true}
        ]
      end

      @impl true
      def views do
        [
          %{
            name: :lookup_feature,
            mode: :scalar,
            scope: :session,
            handler: fn %{"key" => key}, _ctx ->
              case Slither.Store.Server.get(MyApp.FeatureStore, :features, key) do
                nil -> %{"error" => "not_found"}
                value -> %{"value" => value}
              end
            end
          },
          %{
            name: :lookup_features_batch,
            mode: :batch,
            scope: :session,
            handler: fn %{"keys" => keys}, _ctx ->
              results = Enum.map(keys, &Slither.Store.Server.get(MyApp.FeatureStore, :features, &1))
              %{"results" => results}
            end
          }
        ]
      end

      @impl true
      def load(tables) do
        # Populate tables from your data source
        data = load_from_database()
        Enum.each(data, fn {key, value} ->
          :ets.insert(tables[:features], {key, value})
        end)
        :ok
      end
    end

# `table_name`

```elixir
@type table_name() :: atom()
```

# `table_spec`

```elixir
@type table_spec() :: %{
  name: table_name(),
  type: :set | :bag | :ordered_set,
  keypos: pos_integer(),
  read_concurrency: boolean(),
  write_concurrency: boolean(),
  compressed: boolean()
}
```

# `view_mode`

```elixir
@type view_mode() :: :scalar | :batch
```

# `view_scope`

```elixir
@type view_scope() :: :session | :global
```

# `view_spec`

```elixir
@type view_spec() :: %{
  name: atom(),
  mode: view_mode(),
  scope: view_scope(),
  handler: (map(), Slither.Context.t() -&gt; map()),
  timeout_ms: non_neg_integer() | :infinity
}
```

# `load`

```elixir
@callback load(tabs :: %{required(table_name()) =&gt; :ets.tid()}) :: :ok
```

Load initial data into the tables. Called once at startup.

# `tables`

```elixir
@callback tables() :: [table_spec()]
```

Declare tables and their schemas.

# `views`

```elixir
@callback views() :: [view_spec()]
```

Declare Python-accessible view functions.

---

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