Represents a collection of transitions in a state machine
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
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
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
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 101 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 167 def catch_exceptions begin yield rescue Exception rollback raise end end
Gets the object being transitioned
# File lib/state_machine/transition_collection.rb, line 95 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 138 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 114 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 160 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 146 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 124 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 90 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 108 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 82 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 178 def within_transaction if use_transaction && !empty? first.within_transaction do yield success? end else yield end end