A state machine gem designed for RubyMotion.
Hey, this is motion-state-machine
— a state machine gem designed for
RubyMotion for iOS.
It features:
Defining a state machine looks like this:
fsm = StateMachine::Base.new start_state: :awake
fsm.when :awake do |state|
state.on_entry { puts "I'm awake, started and alive!" }
state.transition_to :sleeping, on: :finished_hard_work,
state.die on: :too_hard_work
end
See below for more examples and usage instructions or consult the RubyDoc page on RubyGems.
Undefined states and visual glitches in applications with complex UIs can
be a hassle, especially when the UI is animated and the app has to handle
asynchronous data retrieved in the background.
Well-defined UI state machines avoid these problems while ensuring that
asynchronous event handling does not lead to undefined results (a.k.a. bugs).
MacRuby and Cocoa don’t provide a simple library to address this —
motion-state-machine should fill the gap for RubyMotion developers.
If not done yet, add bundler
gem management to your RubyMotion app.
See http://thunderboltlabs.com/posts/using-bundler-with-rubymotion for
an explanation how.
Add this line to your application’s Gemfile:
gem 'motion-state-machine'
Execute:
$ bundle
The following example shows how to initialize and define a state machine:
fsm = StateMachine::Base.new start_state: :working, verbose: true
This initializes a state machine. Calling fsm.start!
would start the
machine in the defined start state :working
. Using :verbose
activates
debug output on the console.
After initialization, you can define states and transitions:
fsm.when :working do |state|
state.on_entry { puts "I'm awake, started and alive!" }
state.on_exit { puts "Phew. That was enough work." }
state.transition_to :sleeping,
on: :finished_hard_work,
if: proc { really_worked_enough_for_now? },
action: proc { puts "Will go to sleep now." }
state.die on: :too_hard_work
end
This defines…
An entry and an exit action block, called when entering/exiting the state
:working.
a transition from state :working
to :sleeping
, happening when calling
fsm.event(:finished_hard_work)
.
Before the transition is executed, the state machine asks the :if
guard
block if the transition is allowed. Returning false
in this block would
prevent the transition from happening.
If the transition is executed, the machine calls the given :action
block.
another transition that terminates the state machine when calling
fsm.event(:too_hard_work)
. When terminated, the state machine stops
responding to events.
Note that a transition from a state to itself can be internal: Entry/exit
actions are not called on execution in this case.
Transitions can be triggered…
by calling the state machine’s #event
method (see above).
automatically after a given timeout:
fsm.when :sleeping do |state|
state.transition_to :working, after: 20
end
(goes back to :working
after 20 seconds in state :sleeping
)
when a NSNotification
is posted:
fsm.when :awake do |state|
state.transition_to :in_background,
on_notification: UIApplicationDidEnterBackgroundNotification
end
The implementation is designed for general non-performance-intensive purposes
like managing UI state behavior. It might be too slow for parsing XML, realtime
signal processing with high sample rates and similar tasks.
Anyway, it should be able to handle several thousand events per second on
an iOS device.
Feel free to fork the project and send me a pull request if you would
like me to integrate your bugfix, enhancement, or feature.
You can easily add new triggering mechanisms — they can be
implemented in few lines by subclassing the Transition
class (see
the implementation of NotificationTransition
for an example).
I’m also open for suggestions regarding the interface design.
To contribute,
git checkout -b my-new-feature
)git commit -am 'Added some feature'
)git push origin my-new-feature
)If the feature has specs, I will probably merge it 😃