Class: FiniteMachine::Observer

Inherits:
GenericDSL
  • Object
show all
Includes:
Safety
Defined in:
lib/roseflow/finite_machine.rb

Overview

A class responsible for observing state changes

Defined Under Namespace

Modules: Async, Once

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(machine) ⇒ Observer

Initialize an Observer

Parameters:

  • machine (StateMachine)

    reference to the current machine



37
38
39
40
41
42
43
44
# File 'lib/roseflow/finite_machine.rb', line 37

def initialize(machine)
  @id = ULID.generate
  @machine = machine
  @hooks = Hooks.new

  @machine.subscribe(self)
  ObjectSpace.define_finalizer(@id, method(:cleanup_callback_queue))
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args, &block) ⇒ self (private)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Forward the message to observer

Parameters:

  • method_name (String)
  • args (Array)

Returns:

  • (self)


232
233
234
235
236
237
238
239
# File 'lib/roseflow/finite_machine.rb', line 232

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

Instance Attribute Details

#hooksObject (readonly)

The hooks to trigger around the transition lifecycle.



29
30
31
# File 'lib/roseflow/finite_machine.rb', line 29

def hooks
  @hooks
end

#machineObject (readonly)

The current state machine



26
27
28
# File 'lib/roseflow/finite_machine.rb', line 26

def machine
  @machine
end

Instance Method Details

#call(&block) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Evaluate in current context



53
54
55
# File 'lib/roseflow/finite_machine.rb', line 53

def call(&block)
  instance_eval(&block)
end

#callback_queueObject



46
47
48
# File 'lib/roseflow/finite_machine.rb', line 46

def callback_queue
  @callback_queue ||= MessageQueue.new
end

#cancel_event(msg = nil) ⇒ Object

Cancel the current event

This should be called inside a on_before or on_exit callbacks to prevent event transition.

Parameters:

  • msg (String) (defaults to: nil)

    the message used for failure

Raises:

  • (CallbackError)


165
166
167
# File 'lib/roseflow/finite_machine.rb', line 165

def cancel_event(msg = nil)
  raise CallbackError.new(msg)
end

#cleanup_callback_queue(_id) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Clean up callback queue



18
19
20
21
22
23
# File 'lib/roseflow/finite_machine.rb', line 18

def cleanup_callback_queue(_id)
  if callback_queue.alive?
    callback_queue.shutdown
  end
rescue MessageQueueDeadError
end

#emit(event, *data) ⇒ nil

Execute each of the hooks in order with supplied data

Parameters:

  • event (HookEvent)

    the hook event

  • data (Array[Object])

Returns:

  • (nil)


142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/roseflow/finite_machine.rb', line 142

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

#off(hook_type, name = ANY_STATE, &callback) ⇒ Object

Unregister callback for a given event



82
83
84
85
86
# File 'lib/roseflow/finite_machine.rb', line 82

def off(hook_type, name = ANY_STATE, &callback)
  sync_exclusive do
    hooks.unregister hook_type, name, callback
  end
end

#on(hook_type, state_or_event_name = nil, async = nil, &callback) ⇒ Object

Register callback for a given hook type

Examples:

observer.on HookEvent::Enter, :green

Parameters:

  • hook_type (HookEvent)
  • state_or_event_name (Symbol) (defaults to: nil)
  • callback (Proc)


67
68
69
70
71
72
73
74
75
76
77
# File 'lib/roseflow/finite_machine.rb', line 67

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

#on_after(*args, &callback) ⇒ Object



120
121
122
# File 'lib/roseflow/finite_machine.rb', line 120

def on_after(*args, &callback)
  on HookEvent::After, *args, &callback
end

#on_before(*args, &callback) ⇒ Object



116
117
118
# File 'lib/roseflow/finite_machine.rb', line 116

def on_before(*args, &callback)
  on HookEvent::Before, *args, &callback
end

#on_enter(*args, &callback) ⇒ Object



92
93
94
# File 'lib/roseflow/finite_machine.rb', line 92

def on_enter(*args, &callback)
  on HookEvent::Enter, *args, &callback
end

#on_exit(*args, &callback) ⇒ Object



100
101
102
# File 'lib/roseflow/finite_machine.rb', line 100

def on_exit(*args, &callback)
  on HookEvent::Exit, *args, &callback
end

#on_transition(*args, &callback) ⇒ Object



96
97
98
# File 'lib/roseflow/finite_machine.rb', line 96

def on_transition(*args, &callback)
  on HookEvent::Transition, *args, &callback
end

#once_on_after(*args, &callback) ⇒ Object



128
129
130
# File 'lib/roseflow/finite_machine.rb', line 128

def once_on_after(*args, &callback)
  on HookEvent::After, *args, &callback.extend(Once)
end

#once_on_before(*args, &callback) ⇒ Object



124
125
126
# File 'lib/roseflow/finite_machine.rb', line 124

def once_on_before(*args, &callback)
  on HookEvent::Before, *args, &callback.extend(Once)
end

#once_on_enter(*args, &callback) ⇒ Object



104
105
106
# File 'lib/roseflow/finite_machine.rb', line 104

def once_on_enter(*args, &callback)
  on HookEvent::Enter, *args, &callback.extend(Once)
end

#once_on_exit(*args, &callback) ⇒ Object



112
113
114
# File 'lib/roseflow/finite_machine.rb', line 112

def once_on_exit(*args, &callback)
  on HookEvent::Exit, *args, &callback.extend(Once)
end

#once_on_transition(*args, &callback) ⇒ Object



108
109
110
# File 'lib/roseflow/finite_machine.rb', line 108

def once_on_transition(*args, &callback)
  on HookEvent::Transition, *args, &callback.extend(Once)
end