Class: StateMachine::Guard
- Inherits:
-
Object
- Object
- StateMachine::Guard
- Includes:
- Assertions, EvalHelpers
- Defined in:
- lib/state_machine/guard.rb
Overview
Represents a set of requirements that must be met in order for a transition or callback to occur. Guards 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
-
#event_requirement ⇒ Object
readonly
The requirement for verifying the event being guarded.
-
#if_condition ⇒ Object
readonly
The condition that must be met on an object.
-
#known_states ⇒ Object
readonly
A list of all of the states known to this guard.
-
#state_requirements ⇒ Object
readonly
One or more requirements for verifying the states being guarded.
-
#success_requirement ⇒ Object
readonly
The requirement for verifying the success of the event.
-
#unless_condition ⇒ Object
readonly
The condition that must not be met on an object.
Instance Method Summary collapse
-
#draw(graph, event, valid_states) ⇒ Object
Draws a representation of this guard on the given graph.
-
#initialize(options = {}) ⇒ Guard
constructor
Creates a new guard.
-
#match(object, query = {}) ⇒ Object
Attempts to match the given object / query against the set of requirements configured for this guard.
-
#matches?(object, query = {}) ⇒ Boolean
Determines whether the given object / query matches the requirements configured for this guard.
Methods included from EvalHelpers
Methods included from Assertions
#assert_exclusive_keys, #assert_valid_keys
Constructor Details
#initialize(options = {}) ⇒ Guard
Creates a new guard
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 64 65 66 67 68 69 70 |
# File 'lib/state_machine/guard.rb', line 37 def initialize( = {}) #:nodoc: # Build conditionals @if_condition = .delete(:if) @unless_condition = .delete(:unless) # Build event requirement @event_requirement = build_matcher(, :on, :except_on) # Build success requirement @success_requirement = .delete(:include_failures) ? AllMatcher.instance : WhitelistMatcher.new([true]) if (.keys - [:from, :to, :on, :except_from, :except_to, :except_on]).empty? # Explicit from/to requirements specified @state_requirements = [{:from => build_matcher(, :from, :except_from), :to => build_matcher(, :to, :except_to)}] else # Separate out the event requirement .delete(:on) .delete(:except_on) # Implicit from/to requirements specified @state_requirements = .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| [:from, :to].each {|option| @known_states |= state_requirement[option].values} end end |
Instance Attribute Details
#event_requirement ⇒ Object (readonly)
The requirement for verifying the event being guarded
21 22 23 |
# File 'lib/state_machine/guard.rb', line 21 def event_requirement @event_requirement end |
#if_condition ⇒ Object (readonly)
The condition that must be met on an object
15 16 17 |
# File 'lib/state_machine/guard.rb', line 15 def if_condition @if_condition end |
#known_states ⇒ Object (readonly)
A list of all of the states known to this guard. This will pull states from the following options (in the same order):
-
from
/except_from
-
to
/except_to
34 35 36 |
# File 'lib/state_machine/guard.rb', line 34 def known_states @known_states end |
#state_requirements ⇒ Object (readonly)
One or more requirements for verifying the states being guarded. All requirements contain a mapping of => matcher, :to => matcher.
25 26 27 |
# File 'lib/state_machine/guard.rb', line 25 def state_requirements @state_requirements end |
#success_requirement ⇒ Object (readonly)
The requirement for verifying the success of the event
28 29 30 |
# File 'lib/state_machine/guard.rb', line 28 def success_requirement @success_requirement end |
#unless_condition ⇒ Object (readonly)
The condition that must not be met on an object
18 19 20 |
# File 'lib/state_machine/guard.rb', line 18 def unless_condition @unless_condition end |
Instance Method Details
#draw(graph, event, valid_states) ⇒ Object
Draws a representation of this guard on the given graph. This will draw an edge between every state this guard 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.
The collection of edges generated on the graph will be returned.
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/state_machine/guard.rb', line 147 def draw(graph, event, valid_states) state_requirements.inject([]) do |edges, state_requirement| # From states determined based on the known valid states from_states = state_requirement[:from].filter(valid_states) # If a to state is not specified, then it's a loopback and each from # state maps back to itself if state_requirement[:to].values.empty? loopback = true else to_state = state_requirement[:to].values.first to_state = to_state ? to_state.to_s : 'nil' loopback = false end # Generate an edge between each from and to state from_states.each do |from_state| from_state = from_state ? from_state.to_s : 'nil' edges << graph.add_edge(from_state, loopback ? from_state : to_state, :label => event.to_s) end edges end end |
#match(object, query = {}) ⇒ Object
Attempts to match the given object / query against the set of requirements configured for this guard. 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.
Examples
guard = StateMachine::Guard.new(:parked => :idling, :on => :ignite)
guard.match(object, :on => :ignite) # => {:to => ..., :from => ..., :on => ...}
guard.match(object, :on => :park) # => nil
122 123 124 125 126 |
# File 'lib/state_machine/guard.rb', line 122 def match(object, query = {}) if (match = match_query(query)) && matches_conditions?(object) match end end |
#matches?(object, query = {}) ⇒ Boolean
Determines whether the given object / query matches the requirements configured for this guard. 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
guard = StateMachine::Guard.new(:parked => :idling, :on => :ignite)
# Successful
guard.matches?(object, :on => :ignite) # => true
guard.matches?(object, :from => nil) # => true
guard.matches?(object, :from => :parked) # => true
guard.matches?(object, :to => :idling) # => true
guard.matches?(object, :from => :parked, :to => :idling) # => true
guard.matches?(object, :on => :ignite, :from => :parked, :to => :idling) # => true
# Unsuccessful
guard.matches?(object, :on => :park) # => false
guard.matches?(object, :from => :idling) # => false
guard.matches?(object, :to => :first_gear) # => false
guard.matches?(object, :from => :parked, :to => :first_gear) # => false
guard.matches?(object, :on => :park, :from => :parked, :to => :idling) # => false
95 96 97 |
# File 'lib/state_machine/guard.rb', line 95 def matches?(object, query = {}) !match(object, query).nil? end |