class FiniteMachine::Observer
A class responsible for observing state changes
Attributes
The hooks to trigger around the transition lifecycle.
The current state machine
Public Class Methods
Clean up callback queue
@api private
# File lib/finite_machine/observer.rb, line 19 def self.cleanup_callback_queue proc do begin if callback_queue.alive? callback_queue.shutdown end rescue MessageQueueDeadError end end end
Initialize an Observer
@param [StateMachine] machine
reference to the current machine
@api public
# File lib/finite_machine/observer.rb, line 42 def initialize(machine) @machine = machine @hooks = Hooks.new @machine.subscribe(self) ObjectSpace.define_finalizer(self, self.class.cleanup_callback_queue) end
Public Instance Methods
Evaluate in current context
@api private
# File lib/finite_machine/observer.rb, line 57 def call(&block) instance_eval(&block) end
# File lib/finite_machine/observer.rb, line 50 def callback_queue @callback_queue ||= MessageQueue.new end
Execute each of the hooks in order with supplied data
@param [HookEvent] event
the hook event
@param [Array] data
@return [nil]
@api public
# File lib/finite_machine/observer.rb, line 146 def emit(event, *data) sync_exclusive do [event.type].each do |hook_type| any_state_or_event = HookEvent.any_state_or_event(hook_type) [any_state_or_event, event.name].each do |event_name| hooks[hook_type][event_name].each do |hook| handle_callback(hook, event, *data) off(hook_type, event_name, &hook) if hook.is_a?(Once) end end end end end
Unregister callback for a given event
@api public
# File lib/finite_machine/observer.rb, line 86 def off(hook_type, name = ANY_STATE, &callback) sync_exclusive do hooks.unregister hook_type, name, callback end end
Register callback for a given hook type
@param [HookEvent] hook_type @param [Symbol] state_or_event_name @param [Proc] callback
@example
observer.on HookEvent::Enter, :green
@api public
# File lib/finite_machine/observer.rb, line 71 def on(hook_type, state_or_event_name = nil, async = nil, &callback) sync_exclusive do if state_or_event_name.nil? state_or_event_name = HookEvent.any_state_or_event(hook_type) end async = false if async.nil? ensure_valid_callback_name!(hook_type, state_or_event_name) callback.extend(Async) if async == :async hooks.register(hook_type, state_or_event_name, callback) end end
# File lib/finite_machine/observer.rb, line 124 def on_after(*args, &callback) on HookEvent::After, *args, &callback end
# File lib/finite_machine/observer.rb, line 120 def on_before(*args, &callback) on HookEvent::Before, *args, &callback end
# File lib/finite_machine/observer.rb, line 96 def on_enter(*args, &callback) on HookEvent::Enter, *args, &callback end
# File lib/finite_machine/observer.rb, line 104 def on_exit(*args, &callback) on HookEvent::Exit, *args, &callback end
# File lib/finite_machine/observer.rb, line 100 def on_transition(*args, &callback) on HookEvent::Transition, *args, &callback end
# File lib/finite_machine/observer.rb, line 132 def once_on_after(*args, &callback) on HookEvent::After, *args, &callback.extend(Once) end
# File lib/finite_machine/observer.rb, line 128 def once_on_before(*args, &callback) on HookEvent::Before, *args, &callback.extend(Once) end
# File lib/finite_machine/observer.rb, line 108 def once_on_enter(*args, &callback) on HookEvent::Enter, *args, &callback.extend(Once) end
# File lib/finite_machine/observer.rb, line 116 def once_on_exit(*args, &callback) on HookEvent::Exit, *args, &callback.extend(Once) end
# File lib/finite_machine/observer.rb, line 112 def once_on_transition(*args, &callback) on HookEvent::Transition, *args, &callback.extend(Once) end
Private Instance Methods
Callback names including all states and events
@return [Array]
valid callback names
@api private
# File lib/finite_machine/observer.rb, line 223 def callback_names machine.states + machine.events + [ANY_EVENT, ANY_STATE] end
Create callable instance
@api private
# File lib/finite_machine/observer.rb, line 210 def create_callable(hook) callback = proc do |trans_event, *data| machine.instance_exec(trans_event, *data, &hook) end Callable.new(callback) end
Defer callback execution
@api private
# File lib/finite_machine/observer.rb, line 201 def defer(callable, trans_event, *data) async_call = AsyncCall.new(machine, callable, trans_event, *data) callback_queue.start unless callback_queue.running? callback_queue << async_call end
Handle callback and decide if run synchronously or asynchronously
@param [Proc] :hook
The hook to evaluate
@param [HookEvent] :event
The event for which the hook is called
@param [Array] :data
@api private
# File lib/finite_machine/observer.rb, line 186 def handle_callback(hook, event, *data) to = machine.events_map.move_to(event.event_name, event.from, *data) trans_event = TransitionEvent.new(event.event_name, event.from, to) callable = create_callable(hook) if hook.is_a?(Async) defer(callable, trans_event, *data) else callable.(trans_event, *data) end end
Forward the message to observer
@param [String] method_name
@param [Array] args
@return [self]
@api private
FiniteMachine::GenericDSL#method_missing
# File lib/finite_machine/observer.rb, line 236 def method_missing(method_name, *args, &block) _, event_name, callback_name = *method_name.to_s.match(/^(\w*?on_\w+?)_(\w+)$/) if callback_name && callback_names.include?(callback_name.to_sym) public_send(event_name, :"#{callback_name}", *args, &block) else super end end
Test if a message can be handled by observer
@param [String] method_name
@param [Boolean] include_private
@return [Boolean]
@api private
# File lib/finite_machine/observer.rb, line 254 def respond_to_missing?(method_name, include_private = false) *_, callback_name = *method_name.to_s.match(/^(\w*?on_\w+?)_(\w+)$/) callback_name && callback_names.include?(:"#{callback_name}") end