Skip to content

Commit 781ebed

Browse files
sambostockkddnewton
authored andcommitted
Dynamically register events to dispatch
Instead of requiring the consumer to provide a list of all events which they wish to handle, we can give them to option of dynamically detecting them, by scanning the listener's public methods. This approach is similar to that used by Minitest (scanning for `test_` methods) and Rails generators (running all public methods in the order they are defined). While this is slower than specifying a hard coded list, the penalty is only during registration. There is no change the the behaviour of dispatching the events.
1 parent 69dbc41 commit 781ebed

File tree

3 files changed

+30
-5
lines changed

3 files changed

+30
-5
lines changed

sig/prism/dispatcher.rbs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ module Prism
44

55
def initialize: () -> void
66
def register: (untyped, *Symbol) -> void
7+
def register_public_methods: (untyped) -> void
78
def dispatch: (Prism::node) -> void
89
def dispatch_once: (Prism::node) -> void
910

11+
private def register_events: (untyped, Array[Symbol]) -> void
12+
1013
class DispatchOnce < Visitor
1114
attr_reader listeners: Hash[Symbol, Array[untyped]]
1215

templates/lib/prism/dispatcher.rb.erb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,19 @@ module Prism
4444
#
4545
# def register: (Listener, *Symbol) -> void
4646
def register(listener, *events)
47+
register_events(listener, events)
48+
end
49+
50+
# Register all public methods of a listener that match the pattern
51+
# `on_<node_name>_(enter|leave)`.
52+
#
53+
# def register_public_methods: (Listener) -> void
54+
def register_public_methods(listener)
55+
register_events(listener, listener.public_methods(false).grep(/\Aon_.+_(?:enter|leave)\z/))
56+
end
57+
58+
# Register a listener for the given events.
59+
private def register_events(listener, events)
4760
events.each { |event| (listeners[event] ||= []) << listener }
4861
end
4962

test/prism/ruby/dispatcher_test.rb

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,12 @@ def on_integer_node_enter(node)
2525
end
2626

2727
def test_dispatching_events
28-
listener = TestListener.new
28+
listener_manual = TestListener.new
29+
listener_public = TestListener.new
30+
2931
dispatcher = Dispatcher.new
30-
dispatcher.register(listener, :on_call_node_enter, :on_call_node_leave, :on_integer_node_enter)
32+
dispatcher.register(listener_manual, :on_call_node_enter, :on_call_node_leave, :on_integer_node_enter)
33+
dispatcher.register_public_methods(listener_public)
3134

3235
root = Prism.parse(<<~RUBY).value
3336
def foo
@@ -36,11 +39,17 @@ def foo
3639
RUBY
3740

3841
dispatcher.dispatch(root)
39-
assert_equal([:on_call_node_enter, :on_integer_node_enter, :on_integer_node_enter, :on_integer_node_enter, :on_call_node_leave], listener.events_received)
4042

41-
listener.events_received.clear
43+
[listener_manual, listener_public].each do |listener|
44+
assert_equal([:on_call_node_enter, :on_integer_node_enter, :on_integer_node_enter, :on_integer_node_enter, :on_call_node_leave], listener.events_received)
45+
listener.events_received.clear
46+
end
47+
4248
dispatcher.dispatch_once(root.statements.body.first.body.body.first)
43-
assert_equal([:on_call_node_enter, :on_call_node_leave], listener.events_received)
49+
50+
[listener_manual, listener_public].each do |listener|
51+
assert_equal([:on_call_node_enter, :on_call_node_leave], listener.events_received)
52+
end
4453
end
4554
end
4655
end

0 commit comments

Comments
 (0)