Class StateMachine::Event
In: lib/state_machine/event.rb
Parent: Object

An event defines an action that transitions an attribute from one state to another. The state that an attribute is transitioned to depends on the branches configured for the event.

Methods

Included Modules

Assertions MatcherHelpers

Attributes

branches  [R]  The list of branches that determine what state this event transitions objects to when fired
human_name  [W]  The human-readable name for the event
known_states  [R]  A list of all of the states known to this event using the configured branches/transitions as the source
machine  [RW]  The state machine for which this event is defined
name  [R]  The name of the event
qualified_name  [R]  The fully-qualified name of the event, scoped by the machine‘s namespace

Public Instance methods

Determines whether any transitions can be performed for this event based on the current state of the given object.

If the event can‘t be fired, then this will return false, otherwise true.

[Source]

     # File lib/state_machine/event.rb, line 116
116:     def can_fire?(object, requirements = {})
117:       !transition_for(object, requirements).nil?
118:     end

Draws a representation of this event on the given graph. This will create 1 or more edges on the graph for each branch (i.e. transition) configured.

A collection of the generated edges will be returned.

[Source]

     # File lib/state_machine/event.rb, line 179
179:     def draw(graph)
180:       valid_states = machine.states.by_priority.map {|state| state.name}
181:       branches.collect {|branch| branch.draw(graph, name, valid_states)}.flatten
182:     end

Attempts to perform the next available transition on the given object. If no transitions can be made, then this will return false, otherwise true.

Any additional arguments are passed to the StateMachine::Transition#perform instance method.

[Source]

     # File lib/state_machine/event.rb, line 154
154:     def fire(object, *args)
155:       machine.reset(object)
156:       
157:       if transition = transition_for(object)
158:         transition.perform(*args)
159:       else
160:         on_failure(object)
161:         false
162:       end
163:     end

Transforms the event name into a more human-readable format, such as "turn on" instead of "turn_on"

[Source]

    # File lib/state_machine/event.rb, line 80
80:     def human_name(klass = @machine.owner_class)
81:       @human_name.is_a?(Proc) ? @human_name.call(self, klass) : @human_name
82:     end

Generates a nicely formatted description of this event‘s contents.

For example,

  event = StateMachine::Event.new(machine, :park)
  event.transition all - :idling => :parked, :idling => same
  event   # => #<StateMachine::Event name=:park transitions=[all - :idling => :parked, :idling => same]>

[Source]

     # File lib/state_machine/event.rb, line 191
191:     def inspect
192:       transitions = branches.map do |branch|
193:         branch.state_requirements.map do |state_requirement|
194:           "#{state_requirement[:from].description} => #{state_requirement[:to].description}"
195:         end * ', '
196:       end
197:       
198:       "#<#{self.class} name=#{name.inspect} transitions=[#{transitions * ', '}]>"
199:     end

Marks the object as invalid and runs any failure callbacks associated with this event. This should get called anytime this event fails to transition.

[Source]

     # File lib/state_machine/event.rb, line 167
167:     def on_failure(object)
168:       machine.invalidate(object, :state, :invalid_transition, [[:event, human_name(object.class)]])
169:       
170:       state = machine.states.match!(object).name
171:       Transition.new(object, machine, name, state, state).run_callbacks(:before => false)
172:     end

Creates a new transition that determines what to change the current state to when this event fires.

Since this transition is being defined within an event context, you do not need to specify the :on option for the transition. For example:

 state_machine do
   event :ignite do
     transition :parked => :idling, :idling => same, :if => :seatbelt_on? # Transitions to :idling if seatbelt is on
     transition all => :parked, :unless => :seatbelt_on?                  # Transitions to :parked if seatbelt is off
   end
 end

See StateMachine::Machine#transition for a description of the possible configurations for defining transitions.

[Source]

     # File lib/state_machine/event.rb, line 100
100:     def transition(options)
101:       raise ArgumentError, 'Must specify as least one transition requirement' if options.empty?
102:       
103:       # Only a certain subset of explicit options are allowed for transition
104:       # requirements
105:       assert_valid_keys(options, :from, :to, :except_from, :if, :unless) if (options.keys - [:from, :to, :on, :except_from, :except_to, :except_on, :if, :unless]).empty?
106:       
107:       branches << branch = Branch.new(options.merge(:on => name))
108:       @known_states |= branch.known_states
109:       branch
110:     end

Finds and builds the next transition that can be performed on the given object. If no transitions can be made, then this will return nil.

Valid requirement options:

  • :from - One or more states being transitioned from. If none are specified, then this will be the object‘s current state.
  • :to - One or more states being transitioned to. If none are specified, then this will match any to state.
  • :guard - Whether to guard transitions with the if/unless conditionals defined for each one. Default is true.

[Source]

     # File lib/state_machine/event.rb, line 130
130:     def transition_for(object, requirements = {})
131:       assert_valid_keys(requirements, :from, :to, :guard)
132:       requirements[:from] = machine.states.match!(object).name unless custom_from_state = requirements.include?(:from)
133:       
134:       branches.each do |branch|
135:         if match = branch.match(object, requirements)
136:           # Branch allows for the transition to occur
137:           from = requirements[:from]
138:           to = match[:to].values.empty? ? from : match[:to].values.first
139:           
140:           return Transition.new(object, machine, name, from, to, !custom_from_state)
141:         end
142:       end
143:       
144:       # No transition matched
145:       nil
146:     end

Protected Instance methods

Add the various instance methods that can transition the object using the current event

[Source]

     # File lib/state_machine/event.rb, line 204
204:       def add_actions
205:         # Checks whether the event can be fired on the current object
206:         machine.define_helper(:instance, "can_#{qualified_name}?") do |machine, object, *args|
207:           machine.event(name).can_fire?(object, *args)
208:         end
209:         
210:         # Gets the next transition that would be performed if the event were
211:         # fired now
212:         machine.define_helper(:instance, "#{qualified_name}_transition") do |machine, object, *args|
213:           machine.event(name).transition_for(object, *args)
214:         end
215:         
216:         # Fires the event
217:         machine.define_helper(:instance, qualified_name) do |machine, object, *args|
218:           machine.event(name).fire(object, *args)
219:         end
220:         
221:         # Fires the event, raising an exception if it fails
222:         machine.define_helper(:instance, "#{qualified_name}!") do |machine, object, *args|
223:           object.send(qualified_name, *args) || raise(StateMachine::InvalidTransition.new(object, machine, name))
224:         end
225:       end

[Validate]