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 guards configured for the event.

Methods
Included Modules
Attributes
[R] guards The list of guards that determine what state this event transitions objects to when fired
[R] known_states A list of all of the states known to this event using the configured guards/transitions as the source
[RW] machine The state machine for which this event is defined
[R] name The name of the event
[R] qualified_name The fully-qualified name of the event, scoped by the machine‘s namespace
Public Instance methods
can_fire?(object)

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.

     # File lib/state_machine/event.rb, line 159
159:     def can_fire?(object)
160:       !transition_for(object).nil?
161:     end
draw(graph)

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

A collection of the generated edges will be returned.

     # File lib/state_machine/event.rb, line 204
204:     def draw(graph)
205:       valid_states = machine.states.by_priority.map {|state| state.name}
206:       guards.collect {|guard| guard.draw(graph, name, valid_states)}.flatten
207:     end
fire(object, *args)

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.

     # File lib/state_machine/event.rb, line 188
188:     def fire(object, *args)
189:       machine.reset(object)
190:       
191:       if transition = transition_for(object)
192:         transition.perform(*args)
193:       else
194:         machine.invalidate(object, :state, :invalid_transition, [[:event, name]])
195:         false
196:       end
197:     end
inspect()

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]>
     # File lib/state_machine/event.rb, line 216
216:     def inspect
217:       transitions = guards.map do |guard|
218:         guard.state_requirements.map do |state_requirement|
219:           "#{state_requirement[:from].description} => #{state_requirement[:to].description}"
220:         end * ', '
221:       end
222:       
223:       "#<#{self.class} name=#{name.inspect} transitions=[#{transitions * ', '}]>"
224:     end
transition(options)

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

Defining transitions

The options for a new transition uses the Hash syntax to map beginning states to ending states. For example,

  transition :parked => :idling, :idling => :first_gear

In this case, when the event is fired, this transition will cause the state to be idling if it‘s current state is parked or first_gear if it‘s current state is idling.

To help define these implicit transitions, a set of helpers are available for slightly more complex matching:

  • all - Matches every state in the machine
  • all - [:parked, :idling, …] - Matches every state except those specified
  • any - An alias for all (matches every state in the machine)
  • same - Matches the same state being transitioned from

See StateMachine::MatcherHelpers for more information.

Examples:

  transition all => nil                               # Transitions to nil regardless of the current state
  transition all => :idling                           # Transitions to :idling regardless of the current state
  transition all - [:idling, :first_gear] => :idling  # Transitions every state but :idling and :first_gear to :idling
  transition nil => :idling                           # Transitions to :idling from the nil state
  transition :parked => :idling                       # Transitions to :idling if :parked
  transition [:parked, :stalled] => :idling           # Transitions to :idling if :parked or :stalled

  transition :parked => same                          # Loops :parked back to :parked
  transition [:parked, :stalled] => same              # Loops either :parked or :stalled back to the same state
  transition all - :parked => same                    # Loops every state but :parked back to the same state

Verbose transitions

Transitions can also be defined use an explicit set of deprecated configuration options:

  • :from - A state or array of states that can be transitioned from. If not specified, then the transition can occur for any state.
  • :to - The state that‘s being transitioned to. If not specified, then the transition will simply loop back (i.e. the state will not change).
  • :except_from - A state or array of states that cannot be transitioned from.

Examples:

  transition :to => nil
  transition :to => :idling
  transition :except_from => [:idling, :first_gear], :to => :idling
  transition :from => nil, :to => :idling
  transition :from => [:parked, :stalled], :to => :idling

  transition :from => :parked
  transition :from => [:parked, :stalled]
  transition :except_from => :parked

Notice that the above examples are the verbose equivalent of the examples described initially.

Conditions

In addition to the state requirements for each transition, a condition can also be defined to help determine whether that transition is available. These options will work on both the normal and verbose syntax.

Configuration options:

  • :if - A method, proc or string to call to determine if the transition should occur (e.g. :if => :moving?, or :if => lambda {|vehicle| vehicle.speed > 60}). The condition should return or evaluate to true or false.
  • :unless - A method, proc or string to call to determine if the transition should not occur (e.g. :unless => :stopped?, or :unless => lambda {|vehicle| vehicle.speed <= 60}). The condition should return or evaluate to true or false.

Examples:

  transition :parked => :idling, :if => :moving?
  transition :parked => :idling, :unless => :stopped?

  transition :from => :parked, :to => :idling, :if => :moving?
  transition :from => :parked, :to => :idling, :unless => :stopped?

Order of operations

Transitions are evaluated in the order in which they‘re defined. As a result, if more than one transition applies to a given object, then the first transition that matches will be performed.

     # File lib/state_machine/event.rb, line 143
143:     def transition(options)
144:       raise ArgumentError, 'Must specify as least one transition requirement' if options.empty?
145:       
146:       # Only a certain subset of explicit options are allowed for transition
147:       # requirements
148:       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?
149:       
150:       guards << guard = Guard.new(options.merge(:on => name))
151:       @known_states |= guard.known_states
152:       guard
153:     end
transition_for(object, requirements = {})

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.

     # File lib/state_machine/event.rb, line 165
165:     def transition_for(object, requirements = {})
166:       requirements[:from] = machine.states.match!(object).name unless custom_from_state = requirements.include?(:from)
167:       
168:       guards.each do |guard|
169:         if match = guard.match(object, requirements)
170:           # Guard allows for the transition to occur
171:           from = requirements[:from]
172:           to = match[:to].values.empty? ? from : match[:to].values.first
173:           
174:           return Transition.new(object, machine, name, from, to, !custom_from_state)
175:         end
176:       end
177:       
178:       # No transition matched
179:       nil
180:     end
Protected Instance methods
add_actions()

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

     # File lib/state_machine/event.rb, line 229
229:       def add_actions
230:         # Checks whether the event can be fired on the current object
231:         machine.define_instance_method("can_#{qualified_name}?") do |machine, object|
232:           machine.event(name).can_fire?(object)
233:         end
234:         
235:         # Gets the next transition that would be performed if the event were
236:         # fired now
237:         machine.define_instance_method("#{qualified_name}_transition") do |machine, object|
238:           machine.event(name).transition_for(object)
239:         end
240:         
241:         # Fires the event
242:         machine.define_instance_method(qualified_name) do |machine, object, *args|
243:           machine.event(name).fire(object, *args)
244:         end
245:         
246:         # Fires the event, raising an exception if it fails
247:         machine.define_instance_method("#{qualified_name}!") do |machine, object, *args|
248:           object.send(qualified_name, *args) || raise(StateMachine::InvalidTransition, "Cannot transition #{machine.name} via :#{name} from #{machine.states.match!(object).name.inspect}")
249:         end
250:       end