Class: StateMachines::Branch

Inherits:
Object
  • Object
show all
Includes:
EvalHelpers
Defined in:
lib/state_machines/branch.rb

Overview

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.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from EvalHelpers

#evaluate_method, #evaluate_method_with_event_args

Constructor Details

#initialize(options = {}) ⇒ Branch

Creates a new branch



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/state_machines/branch.rb', line 33

def initialize(options = {}) # :nodoc:
  # Build conditionals
  @if_condition = options.delete(:if)
  @unless_condition = options.delete(:unless)

  # Build event requirement
  @event_requirement = build_matcher(options, :on, :except_on)

  if (options.keys - %i[from to on except_from except_to except_on]).empty?
    # Explicit from/to requirements specified
    @state_requirements = [{ from: build_matcher(options, :from, :except_from), to: build_matcher(options, :to, :except_to) }]
  else
    # Separate out the event requirement
    options.delete(:on)
    options.delete(:except_on)

    # Implicit from/to requirements specified
    @state_requirements = options.collect do |from, to|
      from = WhitelistMatcher.new(from) unless from.is_a?(Matcher)
      to = WhitelistMatcher.new(to) unless to.is_a?(Matcher)
      { from: from, to: to }
    end
  end

  # Track known states.  The order that requirements are iterated is based
  # on the priority in which tracked states should be added.
  @known_states = []
  @state_requirements.each do |state_requirement|
    %i[from to].each { |option| @known_states |= state_requirement[option].values }
  end
end

Instance Attribute Details

#event_requirementObject (readonly)

The requirement for verifying the event being matched



20
21
22
# File 'lib/state_machines/branch.rb', line 20

def event_requirement
  @event_requirement
end

#if_conditionObject (readonly)

The condition that must be met on an object



14
15
16
# File 'lib/state_machines/branch.rb', line 14

def if_condition
  @if_condition
end

#known_statesObject (readonly)

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



30
31
32
# File 'lib/state_machines/branch.rb', line 30

def known_states
  @known_states
end

#state_requirementsObject (readonly)

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



24
25
26
# File 'lib/state_machines/branch.rb', line 24

def state_requirements
  @state_requirements
end

#unless_conditionObject (readonly)

The condition that must not be met on an object



17
18
19
# File 'lib/state_machines/branch.rb', line 17

def unless_condition
  @unless_condition
end

Instance Method Details

#draw(graph, event, valid_states, io = $stdout) ⇒ Object



127
128
129
# File 'lib/state_machines/branch.rb', line 127

def draw(graph, event, valid_states, io = $stdout)
  machine.renderer.draw_branch(self, graph, event, valid_states, io)
end

#match(object, query = {}, event_args = []) ⇒ Object

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.

Event arguments are passed to guard conditions if they accept multiple parameters.

Examples

branch = StateMachines::Branch.new(:parked => :idling, :on => :ignite)

branch.match(object, :on => :ignite)  # => {:to => ..., :from => ..., :on => ...}
branch.match(object, :on => :park)    # => nil


119
120
121
122
123
124
125
# File 'lib/state_machines/branch.rb', line 119

def match(object, query = {}, event_args = [])
  StateMachines::OptionsValidator.assert_valid_keys!(query, :from, :to, :on, :guard)

  return unless (match = match_query(query)) && matches_conditions?(object, query, event_args)

  match
end

#matches?(object, query = {}) ⇒ Boolean

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

Returns:

  • (Boolean)


88
89
90
# File 'lib/state_machines/branch.rb', line 88

def matches?(object, query = {})
  !match(object, query).nil?
end