class StateMachine::Transition

A transition represents a state change for a specific attribute.

Transitions consist of:

Attributes

args[RW]

The arguments passed in to the event that triggered the transition (does not include the run_action boolean argument if specified)

from[R]

The original state value before the transition

machine[R]

The state machine for which this transition is defined

object[R]

The object being transitioned

result[R]

The result of invoking the action associated with the machine

to[R]

The new state value after the transition

transient[W]

Whether the transition is only existing temporarily for the object

Public Instance Methods

==(other) click to toggle source

Determines equality of transitions by testing whether the object, states, and event involved in the transition are equal

    # File lib/state_machine/transition.rb
313 def ==(other)
314   other.instance_of?(self.class) &&
315   other.object == object &&
316   other.machine == machine &&
317   other.from_name == from_name &&
318   other.to_name == to_name &&
319   other.event == event
320 end
action() click to toggle source

The action that will be run when this transition is performed

    # File lib/state_machine/transition.rb
109 def action
110   machine.action
111 end
attribute() click to toggle source

The attribute which this transition's machine is defined for

    # File lib/state_machine/transition.rb
104 def attribute
105   machine.attribute
106 end
attributes() click to toggle source

A hash of all the core attributes defined for this transition with their names as keys and values of the attributes as values.

Example

machine = StateMachine.new(Vehicle)
transition = StateMachine::Transition.new(Vehicle.new, machine, :ignite, :parked, :idling)
transition.attributes   # => {:object => #<Vehicle:0xb7d60ea4>, :attribute => :state, :event => :ignite, :from => 'parked', :to => 'idling'}
    # File lib/state_machine/transition.rb
185 def attributes
186   @attributes ||= {:object => object, :attribute => attribute, :event => event, :from => from, :to => to}
187 end
event() click to toggle source

The event that triggered the transition

    # File lib/state_machine/transition.rb
114 def event
115   @event.name
116 end
from_name() click to toggle source

The state name before the transition

    # File lib/state_machine/transition.rb
129 def from_name
130   @from_state.name
131 end
human_event() click to toggle source

The human-readable name of the event that triggered the transition

    # File lib/state_machine/transition.rb
124 def human_event
125   @event.human_name(@object.class)
126 end
human_from_name() click to toggle source

The human-readable state name before the transition

    # File lib/state_machine/transition.rb
139 def human_from_name
140   @from_state.human_name(@object.class)
141 end
human_to_name() click to toggle source

The new human-readable state name after the transition

    # File lib/state_machine/transition.rb
154 def human_to_name
155   @to_state.human_name(@object.class)
156 end
inspect() click to toggle source

Generates a nicely formatted description of this transitions's contents.

For example,

transition = StateMachine::Transition.new(object, machine, :ignite, :parked, :idling)
transition   # => #<StateMachine::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>
    # File lib/state_machine/transition.rb
328 def inspect
329   "#<#{self.class} #{%w(attribute event from from_name to to_name).map {|attr| "#{attr}=#{send(attr).inspect}"} * ' '}>"
330 end
loopback?() click to toggle source

Does this transition represent a loopback (i.e. the from and to state are the same)

Example

machine = StateMachine.new(Vehicle)
StateMachine::Transition.new(Vehicle.new, machine, :park, :parked, :parked).loopback?   # => true
StateMachine::Transition.new(Vehicle.new, machine, :park, :idling, :parked).loopback?   # => false
    # File lib/state_machine/transition.rb
166 def loopback?
167   from_name == to_name
168 end
perform(*args) click to toggle source

Runs the actual transition and any before/after callbacks associated with the transition. The action associated with the transition/machine can be skipped by passing in false.

Examples

class Vehicle
  state_machine :action => :save do
    ...
  end
end

vehicle = Vehicle.new
transition = StateMachine::Transition.new(vehicle, machine, :ignite, :parked, :idling)
transition.perform                  # => Runs the +save+ action after setting the state attribute
transition.perform(false)           # => Only sets the state attribute
transition.perform(Time.now)        # => Passes in additional arguments and runs the +save+ action
transition.perform(Time.now, false) # => Passes in additional arguments and only sets the state attribute
    # File lib/state_machine/transition.rb
207 def perform(*args)
208   run_action = [true, false].include?(args.last) ? args.pop : true
209   self.args = args
210   
211   # Run the transition
212   !!TransitionCollection.new([self], :actions => run_action).perform
213 end
persist() click to toggle source

Transitions the current value of the state to that specified by the transition. Once the state is persisted, it cannot be persisted again until this transition is reset.

Example

class Vehicle
  state_machine do
    event :ignite do
      transition :parked => :idling
    end
  end
end

vehicle = Vehicle.new
transition = StateMachine::Transition.new(vehicle, Vehicle.state_machine, :ignite, :parked, :idling)
transition.persist

vehicle.state   # => 'idling'
    # File lib/state_machine/transition.rb
268 def persist
269   unless @persisted
270     machine.write(object, :state, to)
271     @persisted = true
272   end
273 end
qualified_event() click to toggle source

The fully-qualified name of the event that triggered the transition

    # File lib/state_machine/transition.rb
119 def qualified_event
120   @event.qualified_name
121 end
qualified_from_name() click to toggle source

The fully-qualified state name before the transition

    # File lib/state_machine/transition.rb
134 def qualified_from_name
135   @from_state.qualified_name
136 end
qualified_to_name() click to toggle source

The new fully-qualified state name after the transition

    # File lib/state_machine/transition.rb
149 def qualified_to_name
150   @to_state.qualified_name
151 end
reset() click to toggle source

Resets any tracking of which callbacks have already been run and whether the state has already been persisted

    # File lib/state_machine/transition.rb
306 def reset
307   @before_run = @persisted = @after_run = false
308   @paused_block = nil
309 end
rollback() click to toggle source

Rolls back changes made to the object's state via this transition. This will revert the state back to the from value.

Example

class Vehicle
  state_machine :initial => :parked do
    event :ignite do
      transition :parked => :idling
    end
  end
end

vehicle = Vehicle.new     # => #<Vehicle:0xb7b7f568 @state="parked">
transition = StateMachine::Transition.new(vehicle, Vehicle.state_machine, :ignite, :parked, :idling)

# Persist the new state
vehicle.state             # => "parked"
transition.persist
vehicle.state             # => "idling"

# Roll back to the original state
transition.rollback
vehicle.state             # => "parked"
    # File lib/state_machine/transition.rb
299 def rollback
300   reset
301   machine.write(object, :state, from)
302 end
run_callbacks(options = {}, &block) click to toggle source

Runs the before / after callbacks for this transition. If a block is provided, then it will be executed between the before and after callbacks.

Configuration options:

  • before - Whether to run before callbacks.

  • after - Whether to run after callbacks. If false, then any around callbacks will be paused until called again with after enabled. Default is true.

This will return true if all before callbacks gets executed. After callbacks will not have an effect on the result.

    # File lib/state_machine/transition.rb
235 def run_callbacks(options = {}, &block)
236   options = {:before => true, :after => true}.merge(options)
237   @success = false
238   
239   halted = pausable { before(options[:after], &block) } if options[:before]
240   
241   # After callbacks are only run if:
242   # * An around callback didn't halt after yielding
243   # * They're enabled or the run didn't succeed
244   after if !(@before_run && halted) && (options[:after] || !@success)
245   
246   @before_run
247 end
to_name() click to toggle source

The new state name after the transition

    # File lib/state_machine/transition.rb
144 def to_name
145   @to_state.name
146 end
transient?() click to toggle source

Is this transition existing for a short period only? If this is set, it indicates that the transition (or the event backing it) should not be written to the object if it fails.

    # File lib/state_machine/transition.rb
173 def transient?
174   @transient
175 end
within_transaction() { || ... } click to toggle source

Runs a block within a transaction for the object being transitioned. By default, transactions are a no-op unless otherwise defined by the machine's integration.

    # File lib/state_machine/transition.rb
218 def within_transaction
219   machine.within_transaction(object) do
220     yield
221   end
222 end

Private Instance Methods

after() click to toggle source

Runs the machine's after callbacks for this transition. Only callbacks that are configured to match the event, from state, and to state will be invoked.

Once the callbacks are run, they cannot be run again until this transition is reset.

Halting

If any callback throws a :halt exception, it will be caught and the callback chain will be automatically stopped. However, this exception will not bubble up to the caller since after callbacks should never halt the execution of a perform.

    # File lib/state_machine/transition.rb
437 def after
438   unless @after_run
439     # First resume previously paused callbacks
440     if resume
441       catch(:halt) do
442         type = @success ? :after : :failure
443         machine.callbacks[type].each {|callback| callback.call(object, context, self)}
444       end
445     end
446     
447     @after_run = true
448   end
449 end
before(complete = true, index = 0) { |: {}| ... } click to toggle source

Runs the machine's before callbacks for this transition. Only callbacks that are configured to match the event, from state, and to state will be invoked.

Once the callbacks are run, they cannot be run again until this transition is reset.

    # File lib/state_machine/transition.rb
392 def before(complete = true, index = 0, &block)
393   unless @before_run
394     while callback = machine.callbacks[:before][index]
395       index += 1
396       
397       if callback.type == :around
398         # Around callback: need to handle recursively.  Execution only gets
399         # paused if:
400         # * The block fails and the callback doesn't run on failures OR
401         # * The block succeeds, but after callbacks are disabled (in which
402         #   case a continuation is stored for later execution)
403         return if catch(:cancel) do
404           callback.call(object, context, self) do
405             before(complete, index, &block)
406             
407             pause if @success && !complete
408             throw :cancel, true unless @success
409           end
410         end
411       else
412         # Normal before callback
413         callback.call(object, context, self)
414       end
415     end
416     
417     @before_run = true
418   end
419   
420   action = {:success => true}.merge(block_given? ? yield : {})
421   @result, @success = action[:result], action[:success]
422 end
context() click to toggle source

Gets a hash of the context defining this unique transition (including event, from state, and to state).

Example

machine = StateMachine.new(Vehicle)
transition = StateMachine::Transition.new(Vehicle.new, machine, :ignite, :parked, :idling)
transition.context    # => {:on => :ignite, :from => :parked, :to => :idling}
    # File lib/state_machine/transition.rb
459 def context
460   @context ||= {:on => event, :from => from_name, :to => to_name}
461 end
pausable() { || ... } click to toggle source

Runs a block that may get paused. If the block doesn't pause, then execution will continue as normal. If the block gets paused, then it will take care of switching the execution context when it's resumed.

This will return true if the given block halts for a reason other than getting paused.

    # File lib/state_machine/transition.rb
339 def pausable
340   begin
341     halted = !catch(:halt) { yield; true }
342   rescue Exception => error
343     raise unless @resume_block
344   end
345   
346   if @resume_block
347     @resume_block.call(halted, error)
348   else
349     halted
350   end
351 end
pause() click to toggle source

Pauses the current callback execution. This should only occur within around callbacks when the remainder of the callback will be executed at a later point in time.

    # File lib/state_machine/transition.rb
356 def pause
357   raise ArgumentError, 'around_transition callbacks cannot be called in multiple execution contexts in java implementations of Ruby. Use before/after_transitions instead.' if RUBY_PLATFORM == 'java'
358   
359   unless @resume_block
360     require 'continuation' unless defined?(callcc)
361     callcc do |block|
362       @paused_block = block
363       throw :halt, true
364     end
365   end
366 end
resume() click to toggle source

Resumes the execution of a previously paused callback execution. Once the paused callbacks complete, the current execution will continue.

    # File lib/state_machine/transition.rb
370 def resume
371   if @paused_block
372     halted, error = callcc do |block|
373       @resume_block = block
374       @paused_block.call
375     end
376     
377     @resume_block = @paused_block = nil
378     
379     raise error if error
380     !halted
381   else
382     true
383   end
384 end