# `Hume`
[🔗](https://github.com/zen-en-tonal/hume/blob/main/lib/hume.ex#L1)

Hume is a library for building event-sourced state machines in Elixir.

It provides a framework for defining state machines that can handle events,
maintain state, and take snapshots of their state for efficient recovery.

The library leverages Elixir's `GenServer` for process management and can use
ETS for event storage. It also includes a mechanism for transferring ETS table
ownership using a "heir" process.

## Features

  - Define state machines using a simple behaviour.
  - Handle events and update state accordingly.
  - Persist events to an event store (in-memory or ETS).
  - Take snapshots of the current state for efficient recovery.
  - Transfer ETS table ownership using a dedicated heir process.

## Getting Started
To get started with Hume, you can define a state machine by creating a module
that uses `Hume.Projection` and implements the required callbacks. You can then
start the state machine process and send events to it.

## Example

    defmodule MyProjection do
      use Hume.Projection, use_ets: true, store: Hume.EventStore.ETS

      @impl true
      def init_state(_), do: %{}

      @impl true
      def handle_event({:add, key, value}, state),
        do: {:ok, Map.put(state || %{}, key, value)}

      @impl true
      def handle_event({:remove, key}, state),
        do: {:ok, Map.delete(state || %{}, key)}
    end

    Hume.EventStore.ETS.start_link([])
    {:ok, pid} = Hume.start_link(MyProjection, stream: MyStream, projection: MyProjection)

    {:ok, _} = Hume.publish(Hume.EventStore.ETS, MyStream, {:add, :foo, 42})
    %{foo: 42} = Hume.state(pid)
    {:ok, _} = Hume.publish(Hume.EventStore.ETS, MyStream, {:remove, :foo})
    %{} = Hume.state(pid)

# `snapshot_opts`

```elixir
@type snapshot_opts() :: {:timeout, timeout()} | :dirty
```

# `publish`

```elixir
@spec publish(
  event_store :: module(),
  Hume.EventStore.stream(),
  Hume.EventStore.payload(),
  [Hume.Publisher.options()]
) :: {:ok, non_neg_integer()} | {:error, term()}
```

Publishes an event to the specified event store and stream.

## Parameters
  - event_store: The module implementing the event store (must use `Hume.EventStore`).
  - stream: The stream identifier where the event will be published.
  - payload: The event payload to be published.
  - opts: Optional keyword list of options. Supported options:
    - `:expect_seq` - The expected sequence number for optimistic concurrency control.

## Returns
  - `{:ok, seq}` if the event is published successfully.
  - `{:error, reason}` if there is an error during publishing.

# `snapshot`

```elixir
@spec snapshot(GenServer.server(), [snapshot_opts()]) :: Hume.Projection.snapshot()
```

See `Hume.Projection.snapshot/2`.

# `start`

```elixir
@spec start(module(), [Hume.Projection.option()]) :: GenServer.on_start()
```

Starts a state machine process without linking it to the current process.

See `Hume.start_link/2` for options and return values.

# `start_link`

```elixir
@spec start_link(module(), [Hume.Projection.option()]) :: GenServer.on_start()
```

Starts a state machine process.

## Options
  - `:stream` - The stream identifier (required).
  - `:projection` - The unique name for the projection (optional). 

## Returns
  - `{:ok, pid}` if the process starts successfully.
  - `{:error, reason}` if the process fails to start.

# `state`

```elixir
@spec state(GenServer.server(), [snapshot_opts()]) :: Hume.Projection.state()
```

See `Hume.Projection.state/2`.

---

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