class StateMachine::Path
A path represents a sequence of transitions that can be run for a particular object. Paths can walk to new transitions, revealing all of the possible branches that can be encountered in the object's state machine.
Attributes
The state machine this path is walking
The object whose state machine is being walked
Public Class Methods
Creates a new transition path for the given object. Initially this is an empty path. In order to start walking the path, it must be populated with an initial transition.
Configuration options:
-
:target
- The target state to end the path on -
:guard
- Whether to guard transitions with the if/unless conditionals defined for each one
# File lib/state_machine/path.rb, line 22 def initialize(object, machine, options = {}) assert_valid_keys(options, :target, :guard) @object = object @machine = machine @target = options[:target] @guard = options[:guard] end
Public Instance Methods
Determines whether or not this path has completed. A path is considered complete when one of the following conditions is met:
-
The last transition in the path ends on the target state
-
There are no more transitions remaining to walk and there is no target state
# File lib/state_machine/path.rb, line 85 def complete? !empty? && (@target ? to_name == @target : transitions.empty?) end
Lists all of the events that can be fired through this path.
For example,
path.events # => [:park, :ignite, :shift_up, ...]
# File lib/state_machine/path.rb, line 70 def events map {|transition| transition.event}.uniq end
The initial state name for this path
# File lib/state_machine/path.rb, line 37 def from_name first && first.from_name end
Lists all of the from states that can be reached through this path.
For example,
path.to_states # => [:parked, :idling, :first_gear, ...]
# File lib/state_machine/path.rb, line 46 def from_states map {|transition| transition.from_name}.uniq end
The end state name for this path. If a target state was specified for the path, then that will be returned if the path is complete.
# File lib/state_machine/path.rb, line 52 def to_name last && last.to_name end
Lists all of the to states that can be reached through this path.
For example,
path.to_states # => [:parked, :idling, :first_gear, ...]
# File lib/state_machine/path.rb, line 61 def to_states map {|transition| transition.to_name}.uniq end
Walks down the next transitions at the end of this path. This will only walk down paths that are considered valid.
# File lib/state_machine/path.rb, line 76 def walk transitions.each {|transition| yield dup.push(transition)} end
Private Instance Methods
Determines whether it's possible to walk to the given transition from the current path. A transition can be walked to if:
-
It has not been recently walked and
-
If a target is specified, it has not been walked to twice yet
# File lib/state_machine/path.rb, line 110 def can_walk_to?(transition) !recently_walked?(transition) && (!@target || times_walked_to(@target) < 2) end
Determines whether the given transition has been recently walked down in this path. If a target is configured for this path, then this will only look at transitions walked down since the target was last reached.
# File lib/state_machine/path.rb, line 98 def recently_walked?(transition) transitions = self if @target && @target != to_name && target_transition = detect {|t| t.to_name == @target} transitions = transitions[index(target_transition) + 1..-1] end transitions.include?(transition) end
Calculates the number of times the given state has been walked to
# File lib/state_machine/path.rb, line 91 def times_walked_to(state) select {|transition| transition.to_name == state}.length end
Get the next set of transitions that can be walked to starting from the end of this path
# File lib/state_machine/path.rb, line 116 def transitions @transitions ||= empty? ? [] : machine.events.transitions_for(object, :from => to_name, :guard => @guard).select {|transition| can_walk_to?(transition)} end