Class | StateMachine::Event |
In: |
lib/state_machine/event.rb
|
Parent: | Object |
An event defines an action that transitions an attribute from one state to another. The state that an attribute is transitioned to depends on the branches configured for the event.
branches | [R] | The list of branches that determine what state this event transitions objects to when fired |
human_name | [W] | The human-readable name for the event |
known_states | [R] | A list of all of the states known to this event using the configured branches/transitions as the source |
machine | [RW] | The state machine for which this event is defined |
name | [R] | The name of the event |
qualified_name | [R] | The fully-qualified name of the event, scoped by the machine‘s namespace |
Determines whether any transitions can be performed for this event based on the current state of the given object.
If the event can‘t be fired, then this will return false, otherwise true.
# File lib/state_machine/event.rb, line 116 116: def can_fire?(object, requirements = {}) 117: !transition_for(object, requirements).nil? 118: end
Draws a representation of this event on the given graph. This will create 1 or more edges on the graph for each branch (i.e. transition) configured.
A collection of the generated edges will be returned.
# File lib/state_machine/event.rb, line 179 179: def draw(graph) 180: valid_states = machine.states.by_priority.map {|state| state.name} 181: branches.collect {|branch| branch.draw(graph, name, valid_states)}.flatten 182: end
Attempts to perform the next available transition on the given object. If no transitions can be made, then this will return false, otherwise true.
Any additional arguments are passed to the StateMachine::Transition#perform instance method.
# File lib/state_machine/event.rb, line 154 154: def fire(object, *args) 155: machine.reset(object) 156: 157: if transition = transition_for(object) 158: transition.perform(*args) 159: else 160: on_failure(object) 161: false 162: end 163: end
Transforms the event name into a more human-readable format, such as "turn on" instead of "turn_on"
# File lib/state_machine/event.rb, line 80 80: def human_name(klass = @machine.owner_class) 81: @human_name.is_a?(Proc) ? @human_name.call(self, klass) : @human_name 82: end
Generates a nicely formatted description of this event‘s contents.
For example,
event = StateMachine::Event.new(machine, :park) event.transition all - :idling => :parked, :idling => same event # => #<StateMachine::Event name=:park transitions=[all - :idling => :parked, :idling => same]>
# File lib/state_machine/event.rb, line 191 191: def inspect 192: transitions = branches.map do |branch| 193: branch.state_requirements.map do |state_requirement| 194: "#{state_requirement[:from].description} => #{state_requirement[:to].description}" 195: end * ', ' 196: end 197: 198: "#<#{self.class} name=#{name.inspect} transitions=[#{transitions * ', '}]>" 199: end
Marks the object as invalid and runs any failure callbacks associated with this event. This should get called anytime this event fails to transition.
# File lib/state_machine/event.rb, line 167 167: def on_failure(object) 168: machine.invalidate(object, :state, :invalid_transition, [[:event, human_name(object.class)]]) 169: 170: state = machine.states.match!(object).name 171: Transition.new(object, machine, name, state, state).run_callbacks(:before => false) 172: end
Creates a new transition that determines what to change the current state to when this event fires.
Since this transition is being defined within an event context, you do not need to specify the :on option for the transition. For example:
state_machine do event :ignite do transition :parked => :idling, :idling => same, :if => :seatbelt_on? # Transitions to :idling if seatbelt is on transition all => :parked, :unless => :seatbelt_on? # Transitions to :parked if seatbelt is off end end
See StateMachine::Machine#transition for a description of the possible configurations for defining transitions.
# File lib/state_machine/event.rb, line 100 100: def transition(options) 101: raise ArgumentError, 'Must specify as least one transition requirement' if options.empty? 102: 103: # Only a certain subset of explicit options are allowed for transition 104: # requirements 105: assert_valid_keys(options, :from, :to, :except_from, :if, :unless) if (options.keys - [:from, :to, :on, :except_from, :except_to, :except_on, :if, :unless]).empty? 106: 107: branches << branch = Branch.new(options.merge(:on => name)) 108: @known_states |= branch.known_states 109: branch 110: end
Finds and builds the next transition that can be performed on the given object. If no transitions can be made, then this will return nil.
Valid requirement options:
# File lib/state_machine/event.rb, line 130 130: def transition_for(object, requirements = {}) 131: assert_valid_keys(requirements, :from, :to, :guard) 132: requirements[:from] = machine.states.match!(object).name unless custom_from_state = requirements.include?(:from) 133: 134: branches.each do |branch| 135: if match = branch.match(object, requirements) 136: # Branch allows for the transition to occur 137: from = requirements[:from] 138: to = match[:to].values.empty? ? from : match[:to].values.first 139: 140: return Transition.new(object, machine, name, from, to, !custom_from_state) 141: end 142: end 143: 144: # No transition matched 145: nil 146: end
Add the various instance methods that can transition the object using the current event
# File lib/state_machine/event.rb, line 204 204: def add_actions 205: # Checks whether the event can be fired on the current object 206: machine.define_helper(:instance, "can_#{qualified_name}?") do |machine, object, *args| 207: machine.event(name).can_fire?(object, *args) 208: end 209: 210: # Gets the next transition that would be performed if the event were 211: # fired now 212: machine.define_helper(:instance, "#{qualified_name}_transition") do |machine, object, *args| 213: machine.event(name).transition_for(object, *args) 214: end 215: 216: # Fires the event 217: machine.define_helper(:instance, qualified_name) do |machine, object, *args| 218: machine.event(name).fire(object, *args) 219: end 220: 221: # Fires the event, raising an exception if it fails 222: machine.define_helper(:instance, "#{qualified_name}!") do |machine, object, *args| 223: object.send(qualified_name, *args) || raise(StateMachine::InvalidTransition.new(object, machine, name)) 224: end 225: end