Skip to content

Commit 4a8b804

Browse files
committed
Update docs and naming
1 parent 1de0a98 commit 4a8b804

8 files changed

Lines changed: 313 additions & 264 deletions

File tree

lib/rage/events/events.rb

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,41 @@
11
# frozen_string_literal: true
22

3+
##
4+
# `Rage::Events` provides a simple event-driven system for publishing and subscribing to events.
5+
# Define events as data structures, register subscriber classes, and publish events to notify all relevant subscribers.
6+
# Subscribers can handle events and optionally receive additional context with each event.
7+
#
8+
# ```ruby
9+
# # define an event
10+
# UserRegistered = Data.define(:user_id)
11+
#
12+
# # define a subscriber
13+
# class SendWelcomeEmail
14+
# include Rage::Events::Subscriber
15+
# subscribe_to UserRegistered
16+
#
17+
# def call(event)
18+
# puts "Sending welcome email to user #{event.user_id}"
19+
# end
20+
# end
21+
#
22+
# # publish the event
23+
# Rage::Events.publish(UserRegistered.new(user_id: 1))
24+
# ```
25+
#
326
module Rage::Events
4-
# Publish an event.
5-
# @param event [Object] the event to publish
6-
# @param metadata [Object] the metadata to publish along with the event
7-
# @example
8-
# # define an event
9-
# UserRegistered = Data.define(:user_id)
10-
#
11-
# # publish the event
12-
# Rage::Events.publish(UserRegistered.new(user_id: 1))
27+
# Publish an event to all subscribers registered for the event's class or its ancestors.
28+
# Optionally, additional context data can be provided and passed to each subscriber.
1329
#
14-
# # publish with metadata
15-
# Rage::Events.publish(UserRegistered.new(user_id: 1), metadata: { published_at: Time.now })
16-
def self.publish(event, metadata: nil)
30+
# @param event [Object] the event to publish
31+
# @param context [Object] additional data to publish along with the event
32+
# @example Publish an event
33+
# Rage::Events.publish(MyEvent.new)
34+
# @example Publish an event with context
35+
# Rage::Events.publish(MyEvent.new, context: { published_at: Time.now })
36+
def self.publish(event, context: nil)
1737
handler = __event_handlers[event.class] || __build_event_handler(event.class)
18-
handler.call(event, metadata)
38+
handler.call(event, context)
1939

2040
nil
2141
end
@@ -51,30 +71,30 @@ def self.__build_event_handler(event_class)
5171

5272
arguments = "event"
5373

54-
metadata_type, _ = subscriber_class.instance_method(:handle).parameters.find do |param_type, param_name|
55-
param_name == :metadata || param_type == :keyrest
74+
context_type, _ = subscriber_class.instance_method(:call).parameters.find do |param_type, param_name|
75+
param_name == :context || param_type == :keyrest
5676
end
5777

58-
if metadata_type
59-
if metadata_type == :keyreq
60-
arguments += ", metadata: metadata || {}"
78+
if context_type
79+
if context_type == :keyreq
80+
arguments += ", context: context || {}"
6181
else
62-
arguments += ", metadata:"
82+
arguments += ", context:"
6383
end
6484
end
6585

6686
if subscriber_class.__is_deferred
6787
"#{subscriber_class}.enqueue(#{arguments})"
6888
else
69-
"#{subscriber_class}.new.__handle(#{arguments})"
89+
"#{subscriber_class}.new.__call(#{arguments})"
7090
end
7191
end
7292

7393
if subscriber_calls.empty?
7494
->(_, _) {}
7595
else
7696
__event_handlers[event_class] = eval <<-RUBY
77-
->(event, metadata) { #{subscriber_calls.join("; ")} }
97+
->(event, context) { #{subscriber_calls.join("; ")} }
7898
RUBY
7999
end
80100
end

lib/rage/events/subscriber.rb

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@
1414
# include Rage::Events::Subscriber
1515
# subscribe_to MyEvent
1616
#
17-
# def handle(event)
18-
# puts "Handled event: #{event.inspect}"
17+
# def call(event)
18+
# puts "Received event: #{event.inspect}"
1919
# end
2020
# end
2121
#
2222
# # Publish an event
2323
# Rage::Events.publish(MyEvent.new)
2424
# ```
2525
#
26-
# When an event matching the specified class is published, the `handle` method will be invoked with the event instance.
26+
# When an event matching the specified class is published, the `call` method will be invoked with the event instance.
2727
#
2828
# You can also subscribe to multiple event classes:
2929
#
@@ -32,7 +32,7 @@
3232
# include Rage::Events::Subscriber
3333
# subscribe_to EventA, EventB
3434
#
35-
# def handle(event)
35+
# def call(event)
3636
# puts "Received event: #{event.inspect}"
3737
# end
3838
# end
@@ -45,25 +45,42 @@
4545
# include Rage::Events::Subscriber
4646
# subscribe_to MyEvent, deferred: true
4747
#
48-
# def handle(event)
48+
# def call(event)
4949
# puts "Received event in background: #{event.inspect}"
5050
# end
5151
# end
5252
# ```
5353
#
5454
# Such subscriber will be executed in the background using Rage's deferred task system.
5555
#
56+
# You can also define custom error handling for exceptions raised during event processing using `rescue_from`:
57+
#
58+
# ```ruby
59+
# class MySubscriber
60+
# include Rage::Events::Subscriber
61+
# subscribe_to MyEvent
62+
#
63+
# rescue_from StandardError do |exception|
64+
# puts "An error occurred: #{exception.message}"
65+
# end
66+
# end
67+
# ```
68+
#
69+
# @see ClassMethods
70+
#
5671
module Rage::Events::Subscriber
5772
def self.included(handler_class)
5873
handler_class.extend ClassMethods
5974
end
6075

61-
def handle(_)
76+
# @private
77+
def call(_)
6278
end
6379

64-
def __handle(event, metadata: nil)
80+
# @private
81+
def __call(event, context: nil)
6582
Rage.logger.with_context(self.class.__log_context) do
66-
metadata.nil? ? handle(event) : handle(event, metadata: metadata.freeze)
83+
context.nil? ? call(event) : call(event, context: context.freeze)
6784
rescue Exception => _e
6885
e = self.class.__rescue_handlers ? __run_rescue_handlers(_e) : _e
6986

@@ -75,8 +92,13 @@ def __handle(event, metadata: nil)
7592
end
7693

7794
module ClassMethods
95+
# @private
7896
attr_accessor :__event_classes, :__is_deferred, :__log_context, :__rescue_handlers
7997

98+
# Subscribe the class to one or more events.
99+
#
100+
# @param event_classes [Class, Array<Class>] one or more event classes to subscribe to
101+
# @param deferred [Boolean] whether to process events asynchronously
80102
def subscribe_to(*event_classes, deferred: false)
81103
@__event_classes = (@__event_classes || []) | event_classes
82104
@__is_deferred = !!deferred
@@ -88,10 +110,16 @@ def subscribe_to(*event_classes, deferred: false)
88110

89111
if @__is_deferred
90112
include Rage::Deferred::Task
91-
alias_method :perform, :__handle
113+
alias_method :perform, :__call
92114
end
93115
end
94116

117+
# Define exception handlers for the subscriber.
118+
#
119+
# @param klasses [Class, Array<Class>] one or more exception classes to handle
120+
# @param with [Symbol, String] the method name to call when an exception is raised
121+
# @yield [exception] optional block to handle the exception
122+
# @note If you do not re-raise exceptions in deferred subscribers, the subscriber will be marked as successful and Rage will not attempt to retry it.
95123
def rescue_from(*klasses, with: nil, &block)
96124
unless with
97125
if block_given?
@@ -116,6 +144,7 @@ def inherited(klass)
116144
klass.subscribe_to(*@__event_classes, deferred: @__is_deferred) if @__event_classes
117145
end
118146

147+
# @private
119148
def __register_rescue_handlers
120149
return if method_defined?(:__run_rescue_handlers, false) || @__rescue_handlers.nil?
121150

0 commit comments

Comments
 (0)