/
callable.cr
164 lines (141 loc) · 5.28 KB
/
callable.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# Encapsulates everything required to represent an event listener.
# Including what event is being listened on, the callback itself, and its priority.
#
# Each subclass represents a specific "type" of listener.
# See each subclass for more information.
#
# TIP: These types can be manually instantiated and added via the related `AED::EventDispatcherInterface#listener(callable)` overload.
# This can be useful as a point of integration to other libraries, such as lazily instantiating listener instances.
#
# ### Name
#
# Each callable also has an optional *name* that can be useful for debugging to allow identifying a specific callable
# since there would be no way to tell apart two listeners on the same event, with the same priority.
#
# ```
# class MyEvent < AED::Event; end
#
# dispatcher = AED::EventDispatcher.new
#
# dispatcher.listener(MyEvent) { }
# dispatcher.listener(MyEvent, name: "block-listener") { }
#
# class MyListener
# @[AEDA::AsEventListener]
# def on_my_event(event : MyEvent) : Nil
# end
# end
#
# dispatcher.listener MyListener.new
#
# dispatcher.listeners(MyEvent).map &.name # => ["unknown callable", "block-listener", "MyListener#on_my_event"]
# ```
#
# `AED::Callable::EventListenerInstance` instances registered via `AED::EventDispatcherInterface#listener(listener)` will automatically have a name including the
# method and listener class names in the format of `ClassName#method_name`.
abstract struct Athena::EventDispatcher::Callable
include Comparable(self)
# Returns what `AED::Event` class this callable represents.
getter event_class : AED::Event.class
# Returns the name of this callable.
# Useful for debugging to identify a specific callable added from a block, or which method an `AED::Callable::EventListenerInstance` is associated with.
getter name : String
# Returns the [listener priority][Athena::EventDispatcher::EventDispatcherInterface--listener-priority] of this callable.
getter priority : Int32
def initialize(
@event_class : AED::Event.class,
name : String?,
@priority : Int32
)
@name = name || "unknown callable"
end
# :nodoc:
def <=>(other : AED::Callable) : Int32?
other.priority <=> @priority
end
# :nodoc:
def call(event : AED::Event, dispatcher : AED::EventDispatcherInterface) : NoReturn
raise "BUG: Invoked wrong `call` overload"
end
protected abstract def copy_with(priority _priority = @priority)
# Represents a listener that only accepts the `AED::Event` instance.
struct Event(E) < Athena::EventDispatcher::Callable
@callback : E -> Nil
def initialize(
@callback : E -> Nil,
priority : Int32 = 0,
name : String? = nil,
event_class : E.class = E
)
super event_class, name, priority
end
# :nodoc:
def_equals @event_class, @priority, @callback
# :nodoc:
def call(event : E, dispatcher : AED::EventDispatcherInterface) : Nil
@callback.call event
end
protected def copy_with(priority _priority = @priority) : self
Event(E).new(
callback: @callback,
priority: _priority,
)
end
end
# Represents a listener that accepts both the `AED::Event` instance and the `AED::EventDispatcherInterface` instance.
# Such as when using [AED::EventDispatcherInterface#listener(event_class,*,priority,&)][Athena::EventDispatcher::EventDispatcherInterface#listener(callable,*,priority)], or the `AED::Event.callable` method.
struct EventDispatcher(E) < Athena::EventDispatcher::Callable
@callback : E, AED::EventDispatcherInterface -> Nil
def initialize(
@callback : E, AED::EventDispatcherInterface -> Nil,
priority : Int32 = 0,
name : String? = nil,
event_class : E.class = E
)
super event_class, name, priority
end
# :nodoc:
def_equals @event_class, @priority, @callback
# :nodoc:
def call(event : E, dispatcher : AED::EventDispatcherInterface) : Nil
@callback.call event, dispatcher
end
protected def copy_with(priority _priority = @priority) : self
EventDispatcher(E).new(
callback: @callback,
priority: _priority,
)
end
end
# Represents a dedicated type based listener using `AEDA::AsEventListener` annotations.
struct EventListenerInstance(I, E) < Athena::EventDispatcher::Callable
# Returns the listener instance this callable is associated with.
getter instance : I
@callback : Proc(E, Nil) | Proc(E, AED::EventDispatcherInterface, Nil)
def initialize(
@callback : Proc(E, Nil) | Proc(E, AED::EventDispatcherInterface, Nil),
@instance : I,
priority : Int32 = 0,
name : String? = nil,
event_class : E.class = E
)
super event_class, name || "unknown #{@instance.class} method", priority
end
# :nodoc:
def_equals @event_class, @priority, @callback, @instance
# :nodoc:
def call(event : E, dispatcher : AED::EventDispatcherInterface) : Nil
case cb = @callback
in Proc(E, Nil) then cb.call event
in Proc(E, AED::EventDispatcherInterface, Nil) then cb.call event, dispatcher
end
end
protected def copy_with(priority _priority = @priority) : self
EventListenerInstance(I, E).new(
callback: @callback,
instance: @instance,
priority: _priority,
)
end
end
end