class StateMachine::Branch
Represents a set of requirements that must be met in order for a transition or callback to occur. Branches verify that the event, from state, and to state of the transition match, in addition to if/unless conditionals for an object’s state.
Attributes
The requirement for verifying the event being matched
The condition that must be met on an object
A list of all of the states known to this branch. This will pull states from the following options (in the same order):
-
from
/except_from
-
to
/except_to
One or more requirements for verifying the states being matched. All requirements contain a mapping of {:from => matcher, :to => matcher}.
The condition that must not be met on an object
Public Instance Methods
Draws a representation of this branch on the given graph. This will draw an edge between every state this branch matches from to either the configured to state or, if none specified, then a loopback to the from state.
For example, if the following from states are configured:
-
idling
-
first_gear
-
backing_up
…and the to state is parked
, then the following edges will be created:
-
idling
->parked
-
first_gear
->parked
-
backing_up
->parked
Each edge will be labeled with the name of the event that would cause the transition.
# File lib/state_machine/branch.rb 143 def draw(graph, event, valid_states) 144 state_requirements.each do |state_requirement| 145 # From states determined based on the known valid states 146 from_states = state_requirement[:from].filter(valid_states) 147 148 # If a to state is not specified, then it's a loopback and each from 149 # state maps back to itself 150 if state_requirement[:to].values.empty? 151 loopback = true 152 else 153 to_state = state_requirement[:to].values.first 154 to_state = to_state ? to_state.to_s : 'nil' 155 loopback = false 156 end 157 158 # Generate an edge between each from and to state 159 from_states.each do |from_state| 160 from_state = from_state ? from_state.to_s : 'nil' 161 graph.add_edges(from_state, loopback ? from_state : to_state, :label => event.to_s) 162 end 163 end 164 165 true 166 end
Attempts to match the given object / query against the set of requirements configured for this branch. In addition to matching the event, from state, and to state, this will also check whether the configured :if/:unless conditions pass on the given object.
If a match is found, then the event/state requirements that the query passed successfully will be returned. Otherwise, nil is returned if there was no match.
Query options:
-
:from
- One or more states being transitioned from. If none are specified, then this will always match. -
:to
- One or more states being transitioned to. If none are specified, then this will always match. -
:on
- One or more events that fired the transition. If none are specified, then this will always match. -
:guard
- Whether to guard matches with the if/unless conditionals defined for this branch. Default is true.
Examples¶ ↑
branch = StateMachine::Branch.new(:parked => :idling, :on => :ignite) branch.match(object, :on => :ignite) # => {:to => ..., :from => ..., :on => ...} branch.match(object, :on => :park) # => nil
# File lib/state_machine/branch.rb 118 def match(object, query = {}) 119 assert_valid_keys(query, :from, :to, :on, :guard) 120 121 if (match = match_query(query)) && matches_conditions?(object, query) 122 match 123 end 124 end
Determines whether the given object / query matches the requirements configured for this branch. In addition to matching the event, from state, and to state, this will also check whether the configured :if/:unless conditions pass on the given object.
Examples¶ ↑
branch = StateMachine::Branch.new(:parked => :idling, :on => :ignite) # Successful branch.matches?(object, :on => :ignite) # => true branch.matches?(object, :from => nil) # => true branch.matches?(object, :from => :parked) # => true branch.matches?(object, :to => :idling) # => true branch.matches?(object, :from => :parked, :to => :idling) # => true branch.matches?(object, :on => :ignite, :from => :parked, :to => :idling) # => true # Unsuccessful branch.matches?(object, :on => :park) # => false branch.matches?(object, :from => :idling) # => false branch.matches?(object, :to => :first_gear) # => false branch.matches?(object, :from => :parked, :to => :first_gear) # => false branch.matches?(object, :on => :park, :from => :parked, :to => :idling) # => false
# File lib/state_machine/branch.rb 89 def matches?(object, query = {}) 90 !match(object, query).nil? 91 end
Protected Instance Methods
Builds a matcher strategy to use for the given options. If neither a whitelist nor a blacklist option is specified, then an AllMatcher
is built.
# File lib/state_machine/branch.rb 172 def build_matcher(options, whitelist_option, blacklist_option) 173 assert_exclusive_keys(options, whitelist_option, blacklist_option) 174 175 if options.include?(whitelist_option) 176 value = options[whitelist_option] 177 value.is_a?(Matcher) ? value : WhitelistMatcher.new(options[whitelist_option]) 178 elsif options.include?(blacklist_option) 179 value = options[blacklist_option] 180 raise ArgumentError, ":#{blacklist_option} option cannot use matchers; use :#{whitelist_option} instead" if value.is_a?(Matcher) 181 BlacklistMatcher.new(value) 182 else 183 AllMatcher.instance 184 end 185 end
Verifies that the event requirement matches the given query
# File lib/state_machine/branch.rb 199 def match_event(query) 200 matches_requirement?(query, :on, event_requirement) 201 end
Verifies that all configured requirements (event and state) match the given query. If a match is found, then a hash containing the event/state requirements that passed will be returned; otherwise, nil.
# File lib/state_machine/branch.rb 190 def match_query(query) 191 query ||= {} 192 193 if match_event(query) && (state_requirement = match_states(query)) 194 state_requirement.merge(:on => event_requirement) 195 end 196 end
Verifies that the state requirements match the given query. If a matching requirement is found, then it is returned.
# File lib/state_machine/branch.rb 205 def match_states(query) 206 state_requirements.detect do |state_requirement| 207 [:from, :to].all? {|option| matches_requirement?(query, option, state_requirement[option])} 208 end 209 end
Verifies that the conditionals for this branch evaluate to true for the given object
# File lib/state_machine/branch.rb 219 def matches_conditions?(object, query) 220 query[:guard] == false || 221 Array(if_condition).all? {|condition| evaluate_method(object, condition)} && 222 !Array(unless_condition).any? {|condition| evaluate_method(object, condition)} 223 end
Verifies that an option in the given query matches the values required for that option
# File lib/state_machine/branch.rb 213 def matches_requirement?(query, option, requirement) 214 !query.include?(option) || requirement.matches?(query[option], query) 215 end