Current section

Files

Jump to
event_bus lib event_bus.ex
Raw

lib/event_bus.ex

defmodule EventBus do
@moduledoc """
Traceable, extendable and minimalist event bus implementation for Elixir with
built-in event store and event observation manager based on ETS
"""
alias EventBus.Manager.{
Notification,
Observation,
Store,
Subscription,
Topic
}
alias EventBus.Model.Event
@typedoc "EventBus.Model.Event struct"
@type event :: Event.t()
@typedoc "Event id"
@type event_id :: String.t() | integer()
@typedoc "Tuple of topic name and event id"
@type event_shadow :: {topic(), event_id()}
@typedoc "Event listener"
@type listener :: listener_without_config() | listener_with_config()
@typedoc "Listener configuration"
@type listener_config :: any()
@typedoc "List of event listeners"
@type listener_list :: list(listener())
@typedoc "Event listener with config"
@type listener_with_config :: {module(), listener_config()}
@typedoc "Tuple of listener and event reference"
@type listener_with_event_ref ::
listener_with_event_shadow() | listener_with_topic_and_event_id()
@typedoc "Tuple of listener and event shadow"
@type listener_with_event_shadow :: {listener(), event_shadow()}
@typedoc "Tuple of listener, topic and event id"
@type listener_with_topic_and_event_id :: {listener(), topic(), event_id()}
@typedoc "Tuple of listener and list of topic patterns"
@type listener_with_topic_patterns :: {listener(), topic_pattern_list()}
@typedoc "Event listener without config"
@type listener_without_config :: module()
@typedoc "Topic name"
@type topic :: atom()
@typedoc "List of topic names"
@type topic_list :: list(topic())
@typedoc "Regex pattern to match topic name"
@type topic_pattern :: String.t()
@typedoc "List of topic patterns"
@type topic_pattern_list :: list(topic_pattern())
@doc """
Send an event to all subscribers(listeners)
## Examples
event = %Event{id: 1, topic: :webhook_received,
data: %{"message" => "Hi all!"}}
EventBus.notify(event)
:ok
"""
@spec notify(event()) :: :ok
defdelegate notify(event),
to: Notification,
as: :notify
@doc """
Check if a topic registered
## Examples
EventBus.topic_exist?(:demo_topic)
true
"""
@spec topic_exist?(topic()) :: boolean()
defdelegate topic_exist?(topic),
to: Topic,
as: :exist?
@doc """
List all the registered topics
## Examples
EventBus.topics()
[:metrics_summed]
"""
@spec topics() :: topic_list()
defdelegate topics,
to: Topic,
as: :all
@doc """
Register a topic
## Examples
EventBus.register_topic(:demo_topic)
:ok
"""
@spec register_topic(topic()) :: :ok
defdelegate register_topic(topic),
to: Topic,
as: :register
@doc """
Unregister a topic
## Examples
EventBus.unregister_topic(:demo_topic)
:ok
"""
@spec unregister_topic(topic()) :: :ok
defdelegate unregister_topic(topic),
to: Topic,
as: :unregister
@doc """
Subscribe a listener to the event bus
## Examples
EventBus.subscribe({MyEventListener, [".*"]})
:ok
# For configurable listeners you can pass tuple of listener and config
my_config = %{}
EventBus.subscribe({{OtherListener, my_config}, [".*"]})
:ok
"""
@spec subscribe(listener_with_topic_patterns()) :: :ok
defdelegate subscribe(listener_with_topic_patterns),
to: Subscription,
as: :subscribe
@doc """
Unsubscribe a listener from the event bus
## Examples
EventBus.unsubscribe(MyEventListener)
:ok
# For configurable listeners you must pass tuple of listener and config
my_config = %{}
EventBus.unsubscribe({OtherListener, my_config})
:ok
"""
@spec unsubscribe(listener()) :: :ok
defdelegate unsubscribe(listener),
to: Subscription,
as: :unsubscribe
@doc """
Check if the given listener subscribed to the event bus for the given topic
patterns
## Examples
EventBus.subscribe({MyEventListener, [".*"]})
:ok
EventBus.subscribed?({MyEventListener, [".*"]})
true
EventBus.subscribed?({MyEventListener, ["some_initialized"]})
false
EventBus.subscribed?({AnothEventListener, [".*"]})
false
"""
@spec subscribed?(listener_with_topic_patterns()) :: boolean()
defdelegate subscribed?(listener_with_topic_patterns),
to: Subscription,
as: :subscribed?
@doc """
List the subscribers
## Examples
EventBus.subscribers()
[MyEventListener]
# One usual and one configured listener with its config
EventBus.subscribers()
[MyEventListener, {OtherListener, %{}}]
"""
@spec subscribers() :: listener_list()
defdelegate subscribers,
to: Subscription,
as: :subscribers
@doc """
List the subscribers for the given topic
## Examples
EventBus.subscribers(:metrics_received)
[MyEventListener]
# One usual and one configured listener with its config
EventBus.subscribers(:metrics_received)
[MyEventListener, {OtherListener, %{}}]
"""
@spec subscribers(topic()) :: listener_list()
defdelegate subscribers(topic),
to: Subscription,
as: :subscribers
@doc """
Fetch an event
## Examples
EventBus.fetch_event({:hello_received, "123"})
%EventBus.Model.Model{}
"""
@spec fetch_event(event_shadow()) :: event() | nil
defdelegate fetch_event(event_shadow),
to: Store,
as: :fetch
@doc """
Fetch an event's data
## Examples
EventBus.fetch_event_data({:hello_received, "123"})
"""
@spec fetch_event_data(event_shadow()) :: any()
defdelegate fetch_event_data(event_shadow),
to: Store,
as: :fetch_data
@doc """
Mark the event as completed for the listener
## Examples
topic = :hello_received
event_id = "124"
event_shadow = {topic, event_id}
# For regular listeners
EventBus.mark_as_completed({MyEventListener, event_shadow})
# For configurable listeners you must pass tuple of listener and config
my_config = %{}
listener = {OtherListener, my_config}
EventBus.mark_as_completed({listener, event_shadow})
:ok
"""
@spec mark_as_completed(listener_with_event_ref()) :: :ok
defdelegate mark_as_completed(listener_with_event_ref),
to: Observation,
as: :mark_as_completed
@doc """
Mark the event as skipped for the listener
## Examples
EventBus.mark_as_skipped({MyEventListener, {:unmatched_occurred, "124"}})
# For configurable listeners you must pass tuple of listener and config
my_config = %{}
listener = {OtherListener, my_config}
EventBus.mark_as_skipped({listener, {:unmatched_occurred, "124"}})
:ok
"""
@spec mark_as_skipped(listener_with_event_ref()) :: :ok
defdelegate mark_as_skipped(listener_with_event_ref),
to: Observation,
as: :mark_as_skipped
end