Global Event Bus

For event-driven programming purposes, Jivago provides a simple EventBus interface which can be used from anywhere to trigger events and dispatch messages. This approach has the benefit of completely decoupling the caller from the callee(s), which can be beneficial in some large-scale applications. Take a look at the following code snippet :

from jivago.event.config.annotations import EventHandler
from jivago.event.event_bus import EventBus
from jivago.lang.annotations import Override, Inject
from jivago.lang.runnable import Runnable
from jivago.wsgi.annotations import Resource
from jivago.wsgi.methods import POST


@EventHandler("Player:Death")
class PlayerDeathEventHandler(Runnable):

    @Override
    def run(self):
        print("Oh dear! You are dead!")


@Resource("/kill-player")
class PlayerResource(object):

    @Inject
    def __init__(self, event_bus: EventBus):
        self.event_bus = event_bus

    @POST
    def kill_player(self) -> str:
        self.event_bus.emit("Player:Death")
        return "OK"

In this example, a call to /kill-player triggers an event named Player:Death, thereby invoking all known handlers.

Event payload parameter and handler responses

The emit and handler methods accepts zero or one argument. In the event (pun intended) that said payload parameter is supplied while emitting the event, it shall be passed to all handlers which require it. Note that only a single payload parameter is allowed.

from jivago.event.config.annotations import EventHandler
from jivago.event.event_bus import EventBus


@EventHandler("event")
def handle_event(payload) -> str:
    return "ok"

...
event_bus: EventBus
event_bus.emit("event", {"key": "value"})
...

Should event handlers return something, responses are returned to the caller in the form of a tuple containing all non-nil responses. Note that this behaviour is not applicable when using the asynchronous (async) event bus.

Event handler types

Event handlers can be implemented using any of the usual ways. Functions, methods and runnable classes are allowed. Since EventHandlerClass and Runnable objects are instantiated by the service locator, constructor injection is supported as usual.

from jivago.event.config.annotations import EventHandler, EventHandlerClass
from jivago.lang.annotations import Override
from jivago.lang.runnable import Runnable


@EventHandler("event")
def handler_function():
    pass


@EventHandlerClass
class HandlerClass(object):

    @EventHandler("event")
    def handler_method(self):
        pass


@EventHandler("event")
class HandlerRunnable(Runnable):

    @Override
    def run(self):
        pass

Synchronous vs Asynchronous event dispatching

By default, all events are handled synchronously, i.e. on the caller thread. Therefore, calling EventBus.emit() will only return once all handlers have been called. When asynchronicity is desired, events should be emitted using the AsyncEventBus.

from jivago.event.async_event_bus import AsyncEventBus
from jivago.lang.annotations import Inject
from jivago.wsgi.annotations import Resource
from jivago.wsgi.methods import POST


@Resource("/hello")
class MyResource(object):

    @Inject
    def __init__(self, event_bus: AsyncEventBus):
        self.event_bus = event_bus

    @POST
    def send_hello(self) -> str:
        self.event_bus.emit("hello")
        return "Hello has been sent to all listeners!"

Unlike the usual EventBus.emit(), AsyncEventBus.emit() returns immediately and returns nothing. Events are then dispatched by a thread pool executor, which can be configured in your application context.