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 22 def initialize(transitions = [], options = {}) 23 super(transitions) 24 25 # Determine the validity of the transitions as a whole 26 @valid = all? 27 reject! {|transition| !transition} 28 29 attributes = map {|transition| transition.attribute}.uniq 30 raise ArgumentError, 'Cannot perform multiple transitions in parallel for the same state machine attribute' if attributes.length != length 31 32 assert_valid_keys(options, :actions, :after, :transaction) 33 options = {:actions => true, :after => true, :transaction => true}.merge(options) 34 @skip_actions = !options[:actions] 35 @skip_after = !options[:after] 36 @use_transaction = options[:transaction] 37 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 50 def perform(&block) 51 reset 52 53 if valid? 54 if use_event_attributes? && !block_given? 55 each do |transition| 56 transition.transient = true 57 transition.machine.write(object, :event_transition, transition) 58 end 59 60 run_actions 61 else 62 within_transaction do 63 catch(:halt) { run_callbacks(&block) } 64 rollback unless success? 65 end 66 end 67 end 68 69 if actions.length == 1 && results.include?(actions.first) 70 results[actions.first] 71 else 72 success? 73 end 74 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 101 def actions 102 empty? ? [nil] : map {|transition| transition.action}.uniq 103 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 167 def catch_exceptions 168 begin 169 yield 170 rescue Exception 171 rollback 172 raise 173 end 174 end
Gets the object being transitioned
# File lib/state_machine/transition_collection.rb 95 def object 96 first.object 97 end
Transitions the current value of the object's states to those specified by each transition
# File lib/state_machine/transition_collection.rb 138 def persist 139 each {|transition| transition.persist} 140 end
Resets any information tracked from previous attempts to perform the collection
# File lib/state_machine/transition_collection.rb 114 def reset 115 @results = {} 116 @success = false 117 end
Rolls back changes made to the object's states via each transition
# File lib/state_machine/transition_collection.rb 160 def rollback 161 each {|transition| transition.rollback} 162 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 146 def run_actions 147 catch_exceptions do 148 @success = if block_given? 149 result = yield 150 actions.each {|action| results[action] = result} 151 !!result 152 else 153 actions.compact.each {|action| !skip_actions && results[action] = object.send(action)} 154 results.values.all? 155 end 156 end 157 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 124 def run_callbacks(index = 0, &block) 125 if transition = self[index] 126 throw :halt unless transition.run_callbacks(:after => !skip_after) do 127 run_callbacks(index + 1, &block) 128 {:result => results[transition.action], :success => success?} 129 end 130 else 131 persist 132 run_actions(&block) 133 end 134 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 90 def success? 91 @success 92 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 108 def use_event_attributes? 109 !skip_actions && !skip_after && actions.all? && actions.length == 1 && first.machine.action_hook? 110 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 82 def valid? 83 @valid 84 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 178 def within_transaction 179 if use_transaction && !empty? 180 first.within_transaction do 181 yield 182 success? 183 end 184 else 185 yield 186 end 187 end