Class: Tina4::Events

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

Class Method Summary collapse

Class Method Details

.clearObject

Remove all listeners for all events.



129
130
131
# File 'lib/tina4/events.rb', line 129

def clear
  @listeners.clear
end

.emit(event, *args, strict: false) ⇒ Object

Fire an event synchronously. Returns array of listener results.

results = Tina4::Events.emit("user.created", user_data)

Listener isolation (visible-but-resilient): each listener is called inside a rescue so ONE throwing listener never aborts the rest of the emit. A failed listener is LOGGED (never silent) and contributes a nil slot, so N listeners always yield N results in priority order. Pass strict: true to RE-RAISE on the first listener error instead of isolating (later listeners then do NOT run).



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/tina4/events.rb', line 69

def emit(event, *args, strict: false)
  entries = @listeners[event].dup
  results = []
  entries.each do |entry|
    # Remove one-time listeners before calling so re-entrant emits are
    # safe AND so the one-shot is gone before any throw — cleanup stays
    # correct under isolation.
    @listeners[event].delete(entry) if entry[:once]
    begin
      results << entry[:callback].call(*args)
    rescue StandardError, ScriptError => error
      raise if strict

      log_listener_error(event, error)
      results << nil
    end
  end
  results
end

.emit_async(event, *args, strict: false) ⇒ Object

Fire an event asynchronously. Each listener runs in its own thread.

Tina4::Events.emit_async("user.created", user_data)

Listener isolation: each threaded listener is wrapped so one rejection never aborts the others. A failed listener is LOGGED (never silent). Pass strict: true to RE-RAISE the first listener error on the main thread (the thread that raised aborts on join) instead of isolating.



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/tina4/events.rb', line 107

def emit_async(event, *args, strict: false)
  return unless @listeners&.key?(event)

  @listeners[event].sort_by { |l| -(l[:priority] || 0) }.map do |listener|
    Thread.new do
      # In strict mode the error is re-raised to surface on #join; mute
      # the per-thread auto-report so the deliberate raise doesn't spam
      # stderr (the caller still sees it via join).
      Thread.current.report_on_exception = false if strict
      begin
        listener[:callback].call(*args)
      rescue StandardError, ScriptError => error
        raise if strict

        log_listener_error(event, error)
        nil
      end
    end
  end
end

.eventsObject

List all registered event names.



95
96
97
# File 'lib/tina4/events.rb', line 95

def events
  @listeners.keys
end

.listeners(event) ⇒ Object

Get all listener callbacks for an event (in priority order).



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

def listeners(event)
  @listeners[event].map { |entry| entry[:callback] }
end

.off(event, callback = nil) ⇒ Object

Remove a specific listener, or all listeners for an event.

Tina4::Events.off("user.created", handler)  # remove specific
Tina4::Events.off("user.created")            # remove all for event


51
52
53
54
55
56
57
# File 'lib/tina4/events.rb', line 51

def off(event, callback = nil)
  if callback.nil?
    @listeners.delete(event)
  else
    @listeners[event].reject! { |entry| entry[:callback] == callback }
  end
end

.on(event, priority: 0, &block) ⇒ Object

Register a listener for an event.

Tina4::Events.on("user.created") { |user| ... }
Tina4::Events.on("user.created", priority: 10) { |user| ... }

Higher priority runs first.

Raises:

  • (ArgumentError)


26
27
28
29
30
31
32
# File 'lib/tina4/events.rb', line 26

def on(event, priority: 0, &block)
  raise ArgumentError, "block required" unless block_given?

  @listeners[event] << { priority: priority, callback: block, once: false }
  @listeners[event].sort_by! { |entry| -entry[:priority] }
  block
end

.once(event, priority: 0, &block) ⇒ Object

Register a listener that fires only once then auto-removes.

Tina4::Events.once("app.ready") { puts "App started!" }

Raises:

  • (ArgumentError)


38
39
40
41
42
43
44
# File 'lib/tina4/events.rb', line 38

def once(event, priority: 0, &block)
  raise ArgumentError, "block required" unless block_given?

  @listeners[event] << { priority: priority, callback: block, once: true }
  @listeners[event].sort_by! { |entry| -entry[:priority] }
  block
end