Plushie.State (Plushie v0.6.0)

Copy Markdown View Source

Path-based state management with revision tracking and transactions.

A lightweight wrapper around a plain map that tracks a monotonically increasing revision number on every mutation. Useful for detecting changes and implementing optimistic concurrency.

Transactions

begin_transaction/1 captures a snapshot of the current data and revision. Subsequent mutations increment the revision as usual. commit_transaction/1 finalises the transaction (bumping the revision once from the pre-transaction value). rollback_transaction/1 restores the snapshot exactly.

Example

state = Plushie.State.new(%{count: 0})
state = Plushie.State.put(state, [:count], 5)
Plushie.State.get(state, [:count])
#=> 5
Plushie.State.revision(state)
#=> 1

Summary

Functions

Begins a transaction by capturing the current data and revision.

Commits the active transaction, setting the revision to one past the pre-transaction value.

Removes the key at the end of path, incrementing the revision.

Reads the value at path in the state data.

Creates a new state container wrapping data.

Sets the value at path to value, incrementing the revision.

Returns the current revision number.

Rolls back the active transaction, restoring the data and revision to their pre-transaction values.

Applies fun to the value at path, incrementing the revision.

Types

t()

@type t() :: %Plushie.State{
  data: map(),
  revision: non_neg_integer(),
  transaction: map() | nil
}

Functions

begin_transaction(state)

@spec begin_transaction(state :: t()) :: t() | {:error, :transaction_already_active}

Begins a transaction by capturing the current data and revision.

Returns {:error, :transaction_already_active} if a transaction is already in progress.

commit_transaction(state)

@spec commit_transaction(state :: t()) :: t()

Commits the active transaction, setting the revision to one past the pre-transaction value.

delete(state, path)

@spec delete(state :: t(), path :: list()) :: t()

Removes the key at the end of path, incrementing the revision.

Uses Kernel.pop_in/2 to remove the key. If the path doesn't exist, the revision is still incremented for consistency.

get(state, path)

@spec get(state :: t(), path :: list()) :: term()

Reads the value at path in the state data.

An empty path returns the entire data map. Path elements are keys passed to Kernel.get_in/2.

new(data)

@spec new(data :: map()) :: t()

Creates a new state container wrapping data.

The initial revision is 0.

put(state, path, value)

@spec put(state :: t(), path :: list(), value :: term()) :: t()

Sets the value at path to value, incrementing the revision.

revision(state)

@spec revision(state :: t()) :: non_neg_integer()

Returns the current revision number.

rollback_transaction(state)

@spec rollback_transaction(state :: t()) :: t()

Rolls back the active transaction, restoring the data and revision to their pre-transaction values.

update(state, path, fun)

@spec update(state :: t(), path :: list(), fun :: (term() -> term())) :: t()

Applies fun to the value at path, incrementing the revision.

fun receives the current value and must return the new value.