module Athena::EventDispatcher
Overview
A Mediator and Observer pattern event library.
Athena::EventDispatcher
or, AED
for short, allows defining instance methods on EventListenerInterface
types (observers) that will be executed
when an Event
is dispatched via the EventDispatcher
(mediator).
All events are registered with an EventDispatcher
at compile time. While the recommended usage involves using
listener structs, it is also possible to add/remove event handlers dynamically at runtime. The EventDispatcher
has two constructors;
one that supports manual or DI initialization, while the other auto registers listeners at compile time via macros.
An event is nothing more than a class
that, optionally, contains stateful information about the event. For example a HttpOnRequest
event would
contain a reference to the HTTP::Request
object so that the listeners have access to request data. Similarly, a HttpOnResponse
event
would contain a reference to the HTTP::Server::Response
object so that the response body/headers/status can be mutated by the listeners.
Since events and listeners are registered at compile time (via macros or DI), listeners can be added to a project seamlessly without updating any configuration, or having
to instantiate a HTTP::Handler
object and add it to an array for example. The main benefit of this is that an external shard that defines a listener could
be installed and would inherently be picked up and used by Athena::EventDispatcher
; thus making an application easily extendable.
Example
# Create a custom event.
class ExceptionEvent < AED::Event
property? handled : Bool = false
getter exception : Exception
# Events can contain stateful information related to the event.
def initialize(@exception : Exception); end
end
# Create a listener.
struct ExceptionListener
include AED::EventListenerInterface
# Define what events `self` is listening on as well as their priorities.
#
# The higher the priority the sooner that specific listener is executed.
def self.subscribed_events : AED::SubscribedEvents
AED::SubscribedEvents{
ExceptionEvent => 0,
}
end
# Listener handler's are `#call` instance methods restricted to the type of event it should handle.
#
# Multiple methods can be defined to handle multiple events within the same listener.
#
# Event handler's also have access to the dispatcher instance itself.
def call(event : ExceptionEvent, dispatcher : AED::EventDispatcherInterface) : Nil
# Do something with the `ExceptionEvent` and/or dispatcher
event.handled = true
end
end
# New up an `AED::EventDispatcher`, using `AED::EventDispatcher#new`.
# This overload automatically registers listeners using macros.
#
# See also `AED::EventDispatcher#new(listeners : Array(EventListenerInterface))` for a more manual/DI friendly initializer.
dispatcher = AED::EventDispatcher.new
# Instantiate our custom event.
event = ExceptionEvent.new ArgumentError.new("Test exception")
# All events are dispatched via an `AED::EventDispatcher` instance.
#
# Similarly, all listeners are registered with it.
dispatcher.dispatch event
event.handled # => true
# Additional methods also exist on the dispatcher, such as:
# * Adding/removing listeners at runtime
# * Checking the priority of a listener
# * Getting an array of listeners for a given event
# * Checking if there is a listener(s) listening on a given `AED::Event`
dispatcher.has_listeners? ExceptionEvent # => true
Defined in:
athena-event_dispatcher.crevent_dispatcher_interface.cr
Constant Summary
-
VERSION =
"0.1.0"
Macro Summary
-
create_listener(event, &)
Creates a listener for the provided event.
Macro Detail
Creates a listener for the provided event. The macro's block is used as the listener.
The macro block implicitly yields event
and dispatcher
.
listener = AED.create_listener(SampleEvent) do
# Do something with the event.
event.some_method
# A reference to the `AED::EventDispatcherInterface` is also provided.
dispatcher.dispatch FakeEvent.new
end