Module: Protege::Subscribers::HookDispatcher

Extended by:
HookDispatcher
Included in:
HookDispatcher
Defined in:
lib/protege/subscribers/hook_dispatcher.rb

Overview

The hooks-only event dispatcher — the runtime that activates hooks against the engine's event stream. Installed once at boot by Protege::Engine (a peer of Subscribers::Tracing and Subscribers::IntrospectionBroadcaster); it subscribes one dispatcher per Protege::Event.all that, when an event fires, runs every registered hook's matching handlers.

Kept separate from the hook contract (+Protege::HookMixin+, the +on+/+stateful+ DSL a hook declares with): this module only reads that contract off each hook class (+hook_class.subscriptions+, hook_class.stateful?) — a one-way dependency, dispatcher → contract.

Hooks are pulled live from Hook.descendants (via #registered) when an event fires, so a Zeitwerk reload just changes the descendant set; the dispatchers are stable and never need re-subscribing. Handler blocks run via instance_exec on a hook instance — fresh per event for stateless hooks, or a shared per-run instance (keyed by correlation_id) for stateful ones — inside a rescue that logs and swallows, so one hook can never break inference or a sibling hook.

Constant Summary collapse

TERMINAL_EVENTS =

Event classes that end a run; a stateful hook's per-run instance is evicted after these fire. Includes InferenceFailedEvent so a run that raises before completing never leaves a stale instance behind.

[
  InferenceCompletedEvent,
  InferenceFailedEvent,
  InferenceMaxTurnsReachedEvent
].freeze

Instance Method Summary collapse

Instance Method Details

#install!void

This method returns an undefined value.

Wire the event bus to hook dispatch — the subscriber's install! contract. Called once at boot by the engine; idempotent, so a repeat call is a no-op until #reset! runs. Installs one dispatcher per Protege::Event.all that fans the event out to every registered hook's handlers.



48
49
50
51
52
53
54
55
56
# File 'lib/protege/subscribers/hook_dispatcher.rb', line 48

def install!
  @install_mutex.synchronize do
    return unless @handles.empty?

    @handles = Protege::Event.all.map do |event_class|
      event_class.subscribe { |event| dispatch(event_class, event) }
    end
  end
end

#reset!void

This method returns an undefined value.

Detach every dispatcher and drop all per-run state — the subscriber's reset! contract, for boot re-wiring and tests.



62
63
64
65
66
67
68
# File 'lib/protege/subscribers/hook_dispatcher.rb', line 62

def reset!
  @install_mutex.synchronize do
    @handles.each { |handle| ActiveSupport::Notifications.unsubscribe(handle) }
    @handles = []
    @instances_mutex.synchronize { @instances.clear }
  end
end