class StateMachine::TransitionCollection

Represents a collection of transitions in a state machine

Attributes

skip_actions[R]

Whether to skip running the action for each transition's machine

skip_after[R]

Whether to skip running the after callbacks

use_transaction[R]

Whether transitions should wrapped around a transaction block

Public Class Methods

new(transitions = [], options = {}) click to toggle source

Creates a new collection of transitions that can be run in parallel. Each transition must be for a different attribute.

Configuration options:

  • :actions - Whether to run the action configured for each transition

  • :after - Whether to run after callbacks

  • :transaction - Whether to wrap transitions within a transaction

Calls superclass method
# File lib/state_machine/transition_collection.rb, line 22
def initialize(transitions = [], options = {})
  super(transitions)
  
  # Determine the validity of the transitions as a whole
  @valid = all?
  reject! {|transition| !transition}
  
  attributes = map {|transition| transition.attribute}.uniq
  raise ArgumentError, 'Cannot perform multiple transitions in parallel for the same state machine attribute' if attributes.length != length
  
  assert_valid_keys(options, :actions, :after, :transaction)
  options = {:actions => true, :after => true, :transaction => true}.merge(options)
  @skip_actions = !options[:actions]
  @skip_after = !options[:after]
  @use_transaction = options[:transaction]
end

Public Instance Methods

perform(&block) click to toggle source

Runs each of the collection's transitions in parallel.

All transitions will run through the following steps:

  1. Before callbacks

  2. Persist state

  3. Invoke action

  4. After callbacks (if configured)

  5. Rollback (if action is unsuccessful)

If a block is passed to this method, that block will be called instead of invoking each transition's action.

# File lib/state_machine/transition_collection.rb, line 50
def perform(&block)
  reset
  
  if valid?
    if use_event_attributes? && !block_given?
      each do |transition|
        transition.transient = true
        transition.machine.write(object, :event_transition, transition)
      end
      
      run_actions
    else
      within_transaction do
        catch(:halt) { run_callbacks(&block) }
        rollback unless success?
      end
    end
  end
  
  if actions.length == 1 && results.include?(actions.first)
    results[actions.first]
  else
    success?
  end
end

Private Instance Methods

actions() click to toggle source

Gets the list of actions to run. If configured to skip actions, then this will return an empty collection.

# File lib/state_machine/transition_collection.rb, line 102
def actions
  empty? ? [nil] : map {|transition| transition.action}.uniq
end
catch_exceptions() { || ... } click to toggle source

Wraps the given block with a rescue handler so that any exceptions that occur will automatically result in the transition rolling back any changes that were made to the object involved.

# File lib/state_machine/transition_collection.rb, line 168
def catch_exceptions
  begin
    yield
  rescue Exception
    rollback
    raise
  end
end
object() click to toggle source

Gets the object being transitioned

# File lib/state_machine/transition_collection.rb, line 96
def object
  first.object
end
persist() click to toggle source

Transitions the current value of the object's states to those specified by each transition

# File lib/state_machine/transition_collection.rb, line 139
def persist
  each {|transition| transition.persist}
end
reset() click to toggle source

Resets any information tracked from previous attempts to perform the collection

# File lib/state_machine/transition_collection.rb, line 115
def reset
  @results = {}
  @success = false
end
rollback() click to toggle source

Rolls back changes made to the object's states via each transition

# File lib/state_machine/transition_collection.rb, line 161
def rollback
  each {|transition| transition.rollback}
end
run_actions() { || ... } click to toggle source

Runs the actions for each transition. If a block is given method, then it will be called instead of invoking each transition's action.

The results of the actions will be used to determine success?.

# File lib/state_machine/transition_collection.rb, line 147
def run_actions
  catch_exceptions do
    @success = if block_given?
      result = yield
      actions.each {|action| results[action] = result}
      !!result
    else
      actions.compact.each {|action| !skip_actions && results[action] = object.send(action)}
      results.values.all?
    end
  end
end
run_callbacks(index = 0, &block) click to toggle source

Runs each transition's callbacks recursively. Once all before callbacks have been executed, the transitions will then be persisted and the configured actions will be run.

If any transition fails to run its callbacks, :halt will be thrown.

# File lib/state_machine/transition_collection.rb, line 125
def run_callbacks(index = 0, &block)
  if transition = self[index]
    throw :halt unless transition.run_callbacks(:after => !skip_after) do
      run_callbacks(index + 1, &block)
      {:result => results[transition.action], :success => success?}
    end
  else
    persist
    run_actions(&block)
  end
end
success?() click to toggle source

Did each transition perform successfully? This will only be true if the following requirements are met:

  • No before callbacks halt

  • All actions run successfully (always true if skipping actions)

# File lib/state_machine/transition_collection.rb, line 91
def success?
  @success
end
use_event_attributes?() click to toggle source

Determines whether an event attribute be used to trigger the transitions in this collection or whether the transitions be run directly outside of the action.

# File lib/state_machine/transition_collection.rb, line 109
def use_event_attributes?
  !skip_actions && !skip_after && actions.all? && actions.length == 1 && first.machine.action_hook?
end
valid?() click to toggle source

Is this a valid set of transitions? If the collection was creating with any false values for transitions, then the the collection will be marked as invalid.

# File lib/state_machine/transition_collection.rb, line 83
def valid?
  @valid
end
within_transaction() { || ... } click to toggle source

Runs a block within a transaction for the object being transitioned. If transactions are disabled, then this is a no-op.

# File lib/state_machine/transition_collection.rb, line 179
def within_transaction
  if use_transaction && !empty?
    first.within_transaction do
      yield
      success?
    end
  else
    yield
  end
end