Class: StateMachine::MachineCollection
- Inherits:
-
Hash
- Object
- Hash
- StateMachine::MachineCollection
- Defined in:
- lib/state_machine/machine_collection.rb
Overview
Represents a collection of state machines for a class
Instance Method Summary collapse
-
#fire_event_attributes(object, action, complete = true) ⇒ Object
Runs one or more event attributes in parallel during the invocation of an action on the given object.
-
#fire_events(object, *events) ⇒ Object
Runs one or more events in parallel on the given object.
-
#initialize_states(object, options = {}) ⇒ Object
Initializes the state of each machine in the given object.
Instance Method Details
#fire_event_attributes(object, action, complete = true) ⇒ Object
Runs one or more event attributes in parallel during the invocation of an action on the given object. after_transition callbacks can be optionally disabled if the events are being only partially fired (for example, when validating records in ORM integrations).
The event attributes that will be fired are based on which machines match the action that is being invoked.
Examples
class Vehicle
include DataMapper::Resource
property :id, Serial
state_machine :initial => :parked do
event :ignite do
transition :parked => :idling
end
end
state_machine :alarm_state, :namespace => 'alarm', :initial => :active do
event :disable do
transition all => :off
end
end
end
With valid events:
vehicle = Vehicle.create # => #<Vehicle id=1 state="parked" alarm_state="active">
vehicle.state_event = 'ignite'
vehicle.alarm_state_event = 'disable'
Vehicle.state_machines.fire_event_attributes(vehicle, :save) { true }
vehicle.state # => "idling"
vehicle.state_event # => nil
vehicle.alarm_state # => "off"
vehicle.alarm_state_event # => nil
With invalid events:
vehicle = Vehicle.create # => #<Vehicle id=1 state="parked" alarm_state="active">
vehicle.state_event = 'park'
vehicle.alarm_state_event = 'disable'
Vehicle.state_machines.fire_event_attributes(vehicle, :save) { true }
vehicle.state # => "parked"
vehicle.state_event # => nil
vehicle.alarm_state # => "active"
vehicle.alarm_state_event # => nil
vehicle.errors # => #<DataMapper::Validate::ValidationErrors:0xb7af9abc @errors={"state_event"=>["is invalid"]}>
With partial firing:
vehicle = Vehicle.create # => #<Vehicle id=1 state="parked" alarm_state="active">
vehicle.state_event = 'ignite'
Vehicle.state_machines.fire_event_attributes(vehicle, :save, false) { true }
vehicle.state # => "idling"
vehicle.state_event # => "ignite"
vehicle.state_event_transition # => #<StateMachine::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/state_machine/machine_collection.rb', line 110 def fire_event_attributes(object, action, complete = true) # Get the transitions to fire for each applicable machine transitions = map {|name, machine| machine.action == action ? machine.events.attribute_transition_for(object, true) : nil}.compact return yield if transitions.empty? # The value generated by the yielded block (the actual action) action_value = nil # Make sure all events were valid if result = transitions.all? {|transition| transition != false} begin result = Transition.perform(transitions, :after => complete) do # Prevent events from being evaluated multiple times if actions are nested transitions.each {|transition| transition.machine.write(object, :event, nil)} action_value = yield end rescue Exception # Revert object modifications transitions.each do |transition| transition.machine.write(object, :event, transition.event) transition.machine.write(object, :event_transition, nil) if complete end raise end transitions.each do |transition| # Revert event unless transition was successful transition.machine.write(object, :event, transition.event) unless complete && result # Track transition if partial transition completed successfully transition.machine.write(object, :event_transition, !complete && result ? transition : nil) end end action_value.nil? ? result : action_value end |
#fire_events(object, *events) ⇒ Object
Runs one or more events in parallel on the given object. See StateMachine::InstanceMethods#fire_events for more information.
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/state_machine/machine_collection.rb', line 18 def fire_events(object, *events) run_action = [true, false].include?(events.last) ? events.pop : true # Generate the transitions to run for each event transitions = events.collect do |event_name| # Find the actual event being run event = nil detect do |name, machine| event = machine.events[event_name, :qualified_name] end raise InvalidEvent, "#{event_name.inspect} is an unknown state machine event" unless event # Get the transition that will be performed for the event unless transition = event.transition_for(object) machine = event.machine machine.invalidate(object, :state, :invalid_transition, [[:event, event_name]]) end transition end.compact # Run the events in parallel only if valid transitions were found for # all of them if events.length == transitions.length Transition.perform_within_transaction(transitions, :action => run_action) else false end end |
#initialize_states(object, options = {}) ⇒ Object
Initializes the state of each machine in the given object. Initial values are only set if the machine’s attribute doesn’t already exist (which must mean the defaults are being skipped)
7 8 9 10 11 12 13 14 |
# File 'lib/state_machine/machine_collection.rb', line 7 def initialize_states(object, = {}) each_value do |machine| if !.include?(:dynamic) || machine.dynamic_initial_state? == [:dynamic] value = machine.read(object, :state) machine.write(object, :state, machine.initial_state(object).value) if value.nil? || value.respond_to?(:empty?) && value.empty? end end end |