class EnumStateMachine::Event

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.

Attributes

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

Public Instance Methods

can_fire?(object, requirements = {}) click to toggle source

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.

Note that this will not take the object context into account. Although a transition may be possible based on the state machine definition, object-specific behaviors (like validations) may prevent it from firing.

    # File lib/enum_state_machine/event.rb
125 def can_fire?(object, requirements = {})
126   !transition_for(object, requirements).nil?
127 end
context(&block) click to toggle source

Evaluates the given block within the context of this event. This simply provides a DSL-like syntax for defining transitions.

   # File lib/enum_state_machine/event.rb
85 def context(&block)
86   instance_eval(&block)
87 end
draw(graph, options = {}) click to toggle source

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.

Configuration options:

  • :human_name - Whether to use the event's human name for the node's label that gets drawn on the graph

    # File lib/enum_state_machine/event.rb
205 def draw(graph, options = {})
206   valid_states = machine.states.by_priority.map {|state| state.name}
207   branches.each do |branch|
208     branch.draw(graph, options[:human_name] ? human_name : name, valid_states)
209   end
210   
211   true
212 end
fire(object, *args) click to toggle source

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 EnumStateMachine::Transition#perform instance method.

    # File lib/enum_state_machine/event.rb
169 def fire(object, *args)
170   machine.reset(object)
171   
172   if transition = transition_for(object)
173     transition.perform(*args)
174   else
175     on_failure(object)
176     false
177   end
178 end
human_name(klass = @machine.owner_class) click to toggle source

Transforms the event name into a more human-readable format, such as “turn on” instead of “turn_on”

   # File lib/enum_state_machine/event.rb
79 def human_name(klass = @machine.owner_class)
80   @human_name.is_a?(Proc) ? @human_name.call(self, klass) : @human_name
81 end
inspect() click to toggle source

Generates a nicely formatted description of this event's contents.

For example,

event = EnumStateMachine::Event.new(machine, :park)
event.transition all - :idling => :parked, :idling => same
event   # => #<EnumStateMachine::Event name=:park transitions=[all - :idling => :parked, :idling => same]>
    # File lib/enum_state_machine/event.rb
221 def inspect
222   transitions = branches.map do |branch|
223     branch.state_requirements.map do |state_requirement|
224       "#{state_requirement[:from].description} => #{state_requirement[:to].description}"
225     end * ', '
226   end
227   
228   "#<#{self.class} name=#{name.inspect} transitions=[#{transitions * ', '}]>"
229 end
on_failure(object) click to toggle source

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/enum_state_machine/event.rb
182 def on_failure(object)
183   state = machine.states.match!(object)
184   machine.invalidate(object, :state, :invalid_transition, [[:event, human_name(object.class)], [:state, state.human_name(object.class)]])
185   
186   Transition.new(object, machine, name, state.name, state.name).run_callbacks(:before => false)
187 end
reset() click to toggle source

Resets back to the initial state of the event, with no branches / known states associated. This allows you to redefine an event in situations where you either are re-using an existing state machine implementation or are subclassing machines.

    # File lib/enum_state_machine/event.rb
193 def reset
194   @branches = []
195   @known_states = []
196 end
transition(options) click to toggle source

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 EnumStateMachine::Machine#transition for a description of the possible configurations for defining transitions.

    # File lib/enum_state_machine/event.rb
105 def transition(options)
106   raise ArgumentError, 'Must specify as least one transition requirement' if options.empty?
107   
108   # Only a certain subset of explicit options are allowed for transition
109   # requirements
110   assert_valid_keys(options, :from, :to, :except_from, :except_to, :if, :unless) if (options.keys - [:from, :to, :on, :except_from, :except_to, :except_on, :if, :unless]).empty?
111   
112   branches << branch = Branch.new(options.merge(:on => name))
113   @known_states |= branch.known_states
114   branch
115 end
transition_for(object, requirements = {}) click to toggle source

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:

  • :from - One or more states being transitioned from. If none are specified, then this will be the object's current state.

  • :to - One or more states being transitioned to. If none are specified, then this will match any to state.

  • :guard - Whether to guard transitions with the if/unless conditionals defined for each one. Default is true.

    # File lib/enum_state_machine/event.rb
139 def transition_for(object, requirements = {})
140   assert_valid_keys(requirements, :from, :to, :guard)
141   requirements[:from] = machine.states.match!(object).name unless custom_from_state = requirements.include?(:from)
142   
143   branches.each do |branch|
144     if match = branch.match(object, requirements)
145       # Branch allows for the transition to occur
146       from = requirements[:from]
147       to = if match[:to].is_a?(LoopbackMatcher)
148         from
149       else
150         values = requirements.include?(:to) ? [requirements[:to]].flatten : [from] | machine.states.map {|state| state.name}
151         
152         match[:to].filter(values).first
153       end
154       
155       return Transition.new(object, machine, name, from, to, !custom_from_state)
156     end
157   end
158   
159   # No transition matched
160   nil
161 end

Protected Instance Methods

add_actions() click to toggle source

Add the various instance methods that can transition the object using the current event

    # File lib/enum_state_machine/event.rb
234 def add_actions
235   # Checks whether the event can be fired on the current object
236   machine.define_helper(:instance, "can_#{qualified_name}?") do |machine, object, *args|
237     machine.event(name).can_fire?(object, *args)
238   end
239   
240   # Gets the next transition that would be performed if the event were
241   # fired now
242   machine.define_helper(:instance, "#{qualified_name}_transition") do |machine, object, *args|
243     machine.event(name).transition_for(object, *args)
244   end
245   
246   # Fires the event
247   machine.define_helper(:instance, qualified_name) do |machine, object, *args|
248     machine.event(name).fire(object, *args)
249   end
250   
251   # Fires the event, raising an exception if it fails
252   machine.define_helper(:instance, "#{qualified_name}!") do |machine, object, *args|
253     object.send(qualified_name, *args) || raise(EnumStateMachine::InvalidTransition.new(object, machine, name))
254   end
255 end