class StateMachine::TransitionCollection
Represents a collection of transitions in a state machine
Attributes
Whether to skip running the action for each transition's machine
Whether to skip running the after callbacks
Whether transitions should wrapped around a transaction block
Public Class Methods
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
# 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
Runs each of the collection's transitions in parallel.
All transitions will run through the following steps:
-
Before callbacks
-
Persist state
-
Invoke action
-
After callbacks (if configured)
-
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
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
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
Gets the object being transitioned
# File lib/state_machine/transition_collection.rb, line 96 def object first.object end
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
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
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
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
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
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
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
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
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