A very fast low-level state machine that uses no events for simplicity and speed.
Features:
- Allowed from transitions
- Before/after callbacks
- Before state change guard clause
- Error raising when can't change state (can be disabled)
In my testing LowState is 10 to 12x faster than State Machines at changing state.
Supports 3 levels of complexity depending on how simple you want life to be:
🤩 Simplicity
add_state :hole
add_state :seedling
add_state :adult✨ Functionality
add_state :hole, from: [:hole, :seedling]
add_state :seedling, from: :hole
add_state :adult, from: :seedling🚀 Complete control
add_state State.new(state: :hole, from: [:hole, :seedling], before: 'custom_callback_name')
add_state State.new(state: :seedling, from: :hole, error: false)
add_state CustomState.new(state: :adult, from: :seedling)alias: low_state=()
Change state with:
state = :seedlingalias: low_state
Get the current state with:
stateThe first add_state defined is the default state.
Configuration options for each state:
from:- The allowed from transitionsbefore:- The method name of the before callbackafter:- The method name of the after callbackerror:- Whether or not to error when state does not transition
Callback methods with the following naming pattern and are automatically available to use:
[state_name]_state_before[state_name]_state_after
def seed_state_before
# Do stuff before the state has changed.
# Return `false` if you would like to abort the state change.
enddef seed_state_after
# Do stuff after the state has changed.
# The return value here becomes the return value of change_state().
endIf extra arguments are passed to change_state() then they will be available in the before/after callbacks:
# Called with extra argument.
change_state(:seedling, my_arg)
# Argument passed to callback.
def seed_state_before(my_arg)
puts my_arg
endNOTE: Once extra arguments are passed to change_state() then every before/after callback must now support receiving arguments or you'll get an "Expected Argument" error.
# Define the class with state:
class Cell
include LowState
add_state :hole, from: [:hole, :seedling]
add_state :seedling, from: :hole, error: false
add_state :adult, from: :seedling
# Fire methods before and after state change:
def seed_state_before(my_arg)
puts my_arg # => My argument
end
def seed_state_after(my_arg)
"We did it!"
end
end
# Create the instance:
cell = Cell.new
# Change state on the instance:
result = cell.state = :seedling, "My argument" # => "We did it!"
# Access state on the instance:
cell.state # => :seedling📣 Events. Why have events? Seriously. I was using events with state machines and all it ended up making me do was think of duplicate names for things. Besides, the events are usually what your methods that enact the state change are called anyway (method: verb, state: noun). Or you use an event-driven architecture and now you have a double up of what an event means. LowState gets out of your way.
🚘 Speed. Less is more, there's so much code going on in other state machine libraries and there really is no need. It also makes them hard to debug. All we're doing is changing a state/symbol on an object.
🌹 Name. The name LowState represents my ever-lasting depression and I hope this tool will guide you out of yours too.