Class: Servus::Events::Bus

Inherits:
Object
  • Object
show all
Defined in:
lib/servus/events/bus.rb

Overview

Thread-safe event bus for registering and dispatching event handlers.

The Bus acts as a central registry that maps event names to their corresponding handler classes. It uses ActiveSupport::Notifications internally to provide instrumentation and thread-safe event dispatch.

Events are automatically instrumented and will appear in Rails logs with timing information, making it easy to monitor event performance.

Examples:

Registering a handler

class UserCreatedHandler < Servus::EventHandler
  handles :user_created
end

Servus::Events::Bus.register_handler(:user_created, UserCreatedHandler)

Retrieving handlers for an event

handlers = Servus::Events::Bus.handlers_for(:user_created)
handlers.each { |handler| handler.handle(payload) }

Instrumentation in logs

Bus.emit(:user_created, user_id: 123)
# Rails log: servus.events.user_created (1.2ms) {:user_id=>123}

See Also:

Class Method Summary collapse

Class Method Details

.clearvoid

This method returns an undefined value.

Clears all registered handlers and unsubscribes from notifications.

Useful for testing and development mode reloading.

Examples:

Bus.clear


131
132
133
134
135
136
137
138
# File 'lib/servus/events/bus.rb', line 131

def clear
  subscriptions.values.flatten.each do |subscription|
    ActiveSupport::Notifications.unsubscribe(subscription)
  end

  @handlers = nil
  @subscriptions = nil
end

.emit(event_name, payload) ⇒ void

This method returns an undefined value.

Emits an event to all registered handlers with instrumentation.

Uses ActiveSupport::Notifications to instrument the event, providing automatic timing and logging. The event will appear in Rails logs with duration and payload information.

Examples:

Bus.emit(:user_created, { user_id: 123, email: 'user@example.com' })
# Rails log: servus.events.user_created (1.2ms) {:user_id=>123, :email=>"user@example.com"}

Parameters:

  • event_name (Symbol)

    the name of the event to emit

  • payload (Hash)

    the event payload to pass to handlers



90
91
92
# File 'lib/servus/events/bus.rb', line 90

def emit(event_name, payload)
  ActiveSupport::Notifications.instrument(notification_name(event_name), payload)
end

.handlers_for(event_name) ⇒ Array<Class>

Retrieves all registered handlers for a specific event.

Returns a duplicate array to prevent external modification of the internal handler registry.

Examples:

handlers = Bus.handlers_for(:user_created)
handlers.each { |handler| handler.handle(payload) }

Parameters:

  • event_name (Symbol)

    the name of the event

Returns:

  • (Array<Class>)

    array of handler classes registered for this event



73
74
75
# File 'lib/servus/events/bus.rb', line 73

def handlers_for(event_name)
  (handlers[event_name] || []).dup
end

.register_handler(event_name, handler_class) ⇒ Array

Registers a handler class for a specific event.

Multiple handlers can be registered for the same event, and they will all be invoked when the event is emitted. The handler is automatically subscribed to ActiveSupport::Notifications.

Handlers are typically registered automatically when EventHandler classes are loaded at boot time via the ‘handles` DSL method.

Examples:

Bus.register_handler(:user_created, UserCreatedHandler)

Parameters:

  • event_name (Symbol)

    the name of the event

  • handler_class (Class)

    the handler class to register

Returns:

  • (Array)

    the updated array of handlers for this event



47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/servus/events/bus.rb', line 47

def register_handler(event_name, handler_class)
  handlers[event_name] ||= []
  handlers[event_name] << handler_class

  # Subscribe to ActiveSupport::Notifications
  subscription = ActiveSupport::Notifications.subscribe(notification_name(event_name)) do |*args|
    event = ActiveSupport::Notifications::Event.new(*args)
    handler_class.handle(event.payload)
  end

  # Store subscription for cleanup
  subscriptions[event_name] ||= []
  subscriptions[event_name] << subscription
end

.subscribe_all {|event_name, payload, started_at:, finished_at:, id:| ... } ⇒ Object

Subscribes to all Servus event emissions.

Yields the clean event name and payload as positional args, plus started_at, finished_at, and id as keyword args. Returns the subscription for manual unsubscribe.

Examples:

Forward all events to an external system

Servus::Events::Bus.subscribe_all do |event_name, payload, started_at:, **|
  EventusForwardJob.perform_later(
    event: event_name.to_s,
    payload: payload.as_json,
    occurred_at: started_at.utc.iso8601(6)
  )
end

Yields:

  • (event_name, payload, started_at:, finished_at:, id:)

Yield Parameters:

  • event_name (Symbol)

    the event name

  • payload (Hash)

    the event payload

  • started_at (Time)

    when the event was emitted

  • finished_at (Time)

    when the instrumented block completed

  • id (String)

    unique notification ID

Returns:

  • (Object)

    the ActiveSupport::Notifications subscription



116
117
118
119
120
121
# File 'lib/servus/events/bus.rb', line 116

def subscribe_all(&block)
  ActiveSupport::Notifications.subscribe(/^servus\.events\./) do |name, started, finished, id, payload|
    event_name = name.delete_prefix('servus.events.').to_sym
    block.call(event_name, payload, started_at: started, finished_at: finished, id: id)
  end
end