class StateMachines::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

event_requirement[R]

The requirement for verifying the event being matched

if_condition[R]

The condition that must be met on an object

known_states[R]

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

state_requirements[R]

One or more requirements for verifying the states being matched. All requirements contain a mapping of {:from => matcher, :to => matcher}.

unless_condition[R]

The condition that must not be met on an object

Public Instance Methods

draw(graph, event, valid_states) click to toggle source
# File lib/state_machines/branch.rb, line 122
def draw(graph, event, valid_states)
 fail NotImplementedError
end
match(object, query = {}) click to toggle source

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 = StateMachines::Branch.new(:parked => :idling, :on => :ignite)

branch.match(object, :on => :ignite)  # => {:to => ..., :from => ..., :on => ...}
branch.match(object, :on => :park)    # => nil
# File lib/state_machines/branch.rb, line 114
def match(object, query = {})
  query.assert_valid_keys(:from, :to, :on, :guard)
  
  if (match = match_query(query)) && matches_conditions?(object, query)
    match
  end
end
matches?(object, query = {}) click to toggle source

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 = StateMachines::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_machines/branch.rb, line 85
def matches?(object, query = {})
  !match(object, query).nil?
end

Protected Instance Methods

build_matcher(options, whitelist_option, blacklist_option) click to toggle source

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_machines/branch.rb, line 130
def build_matcher(options, whitelist_option, blacklist_option)
  options.assert_exclusive_keys(whitelist_option, blacklist_option)
  
  if options.include?(whitelist_option)
    value = options[whitelist_option]
    value.is_a?(Matcher) ? value : WhitelistMatcher.new(options[whitelist_option])
  elsif options.include?(blacklist_option)
    value = options[blacklist_option]
    raise ArgumentError, ":#{blacklist_option} option cannot use matchers; use :#{whitelist_option} instead" if value.is_a?(Matcher)
    BlacklistMatcher.new(value)
  else
    AllMatcher.instance
  end
end
match_event(query) click to toggle source

Verifies that the event requirement matches the given query

# File lib/state_machines/branch.rb, line 157
def match_event(query)
  matches_requirement?(query, :on, event_requirement)
end
match_query(query) click to toggle source

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_machines/branch.rb, line 148
def match_query(query)
  query ||= {}
  
  if match_event(query) && (state_requirement = match_states(query))
    state_requirement.merge(:on => event_requirement)
  end
end
match_states(query) click to toggle source

Verifies that the state requirements match the given query. If a matching requirement is found, then it is returned.

# File lib/state_machines/branch.rb, line 163
def match_states(query)
  state_requirements.detect do |state_requirement|
    [:from, :to].all? {|option| matches_requirement?(query, option, state_requirement[option])}
  end
end
matches_conditions?(object, query) click to toggle source

Verifies that the conditionals for this branch evaluate to true for the given object

# File lib/state_machines/branch.rb, line 177
def matches_conditions?(object, query)
  query[:guard] == false ||
  Array(if_condition).all? {|condition| evaluate_method(object, condition)} &&
  !Array(unless_condition).any? {|condition| evaluate_method(object, condition)}
end
matches_requirement?(query, option, requirement) click to toggle source

Verifies that an option in the given query matches the values required for that option

# File lib/state_machines/branch.rb, line 171
def matches_requirement?(query, option, requirement)
  !query.include?(option) || requirement.matches?(query[option], query)
end