Class: Plushie::State

Inherits:
Object
  • Object
show all
Defined in:
lib/plushie/state.rb

Overview

Path-based state management with revision tracking and transactions.

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

== Transactions

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

Examples:

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

Defined Under Namespace

Classes: Container

Class Method Summary collapse

Class Method Details

.begin_transaction(state) ⇒ Container, Array(:error, Symbol)

Begins a transaction by capturing the current data and revision.

Parameters:

Returns:



91
92
93
94
95
96
97
# File 'lib/plushie/state.rb', line 91

def self.begin_transaction(state)
  if state.transaction
    [:error, :transaction_already_active]
  else
    state.with(transaction: {data: state.data, revision: state.revision})
  end
end

.commit_transaction(state) ⇒ Container

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

Parameters:

Returns:



104
105
106
107
# File 'lib/plushie/state.rb', line 104

def self.commit_transaction(state)
  old_rev = state.transaction[:revision]
  state.with(transaction: nil, revision: old_rev + 1)
end

.from_hash(data) ⇒ Container

Creates a new state from an existing hash.

Parameters:

  • data (Hash)

Returns:



42
43
44
# File 'lib/plushie/state.rb', line 42

def self.from_hash(data)
  Container.new(data: data, revision: 0, transaction: nil)
end

.get(state, path) ⇒ Object

Reads the value at +path+ in the state data. An empty path returns the entire data hash.

Parameters:

  • state (Container)
  • path (Array)

    key path

Returns:

  • (Object)


52
53
54
55
# File 'lib/plushie/state.rb', line 52

def self.get(state, path)
  return state.data if path.empty?
  state.data.dig(*path)
end

.new(**data) ⇒ Container

Creates a new state container wrapping +data+. The initial revision is 0.

Parameters:

  • data (Hash)

    initial state data

Returns:



34
35
36
# File 'lib/plushie/state.rb', line 34

def self.new(**data)
  Container.new(data: data, revision: 0, transaction: nil)
end

.put(state, path, value) ⇒ Container

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

Parameters:

  • state (Container)
  • path (Array)

    key path (must have at least one element)

  • value (Object)

Returns:



63
64
65
66
# File 'lib/plushie/state.rb', line 63

def self.put(state, path, value)
  new_data = deep_put(state.data, path, value)
  state.with(data: new_data, revision: state.revision + 1)
end

.revision(state) ⇒ Integer

Returns the current revision number.

Parameters:

Returns:

  • (Integer)


85
# File 'lib/plushie/state.rb', line 85

def self.revision(state) = state.revision

.rollback_transaction(state) ⇒ Container

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

Parameters:

Returns:



114
115
116
117
# File 'lib/plushie/state.rb', line 114

def self.rollback_transaction(state)
  snapshot = state.transaction
  state.with(data: snapshot[:data], revision: snapshot[:revision], transaction: nil)
end

.update(state, path) {|current_value| ... } ⇒ Container

Applies +block+ to the value at +path+, incrementing the revision. The block receives the current value and must return the new value.

Parameters:

  • state (Container)
  • path (Array)

    key path

Yields:

  • (current_value)

    the current value at path

Yield Returns:

  • (Object)

    the new value

Returns:



76
77
78
79
# File 'lib/plushie/state.rb', line 76

def self.update(state, path, &block)
  current = get(state, path)
  put(state, path, block.call(current))
end