Antenna (antenna v0.1.0)

View Source

Antenna is a mixture of Phoenix.PubSub and :gen_event functionality with some batteries included.

It implements back-pressure on top of GenStage, is fully conformant with OTP Design Principles. and is distributed out of the box.

One can have as many isolated Antennas as necessary, distinguished by t:id().

The workflow looks like shown below.

Sequence Diagram

sequenceDiagram
  Consumer->>+Broadcaster: event(channel, event)
  Broadcaster-->>+Consumer@Node1: event
  Broadcaster-->>+Consumer@Node2: event
  Broadcaster-->>+Consumer@NodeN: event
  Consumer@Node1-->>-NoOp: mine?
  Consumer@NodeN-->>-NoOp: mine?
  Consumer@Node2->>Matchers: event
  Matchers->>Handlers: handle_match/2

ASCII representation.

Usage Example

The consumer of this library is supposed to declare one or more matchers, subscribing to one or more channels, and then call Antenna.event/2 to propagate the event.

assert {:ok, _pid, "{:tag_1, a, _} when is_nil(a)"} =
  Antenna.match(Antenna, {:tag_1, a, _} when is_nil(a), self(), channels: [:chan_1])

assert :ok = Antenna.event(Antenna, [:chan_1], {:tag_1, nil, 42})
assert_receive {:antenna_event, :chan_1, {:tag_1, nil, 42}}

Summary

Types

The identifier of the channel, messages can be sent to, preferrably atom()

The event being sent to the listeners

The actual handler to be associated with an event(s). It might be either a function or a process id, in which case the message of a following shape will be sent to it.

The identifier of the isolated Antenna

Functions

Sends an event to all the associated matchers through channels.

Declares a matcher for tagged events.

Returns a map of matches to matchers

Subscribes a matcher process specified by pid to a channel(s)

Undeclares a matcher for tagged events previously declared with Antenna.match/4.

Unsubscribes a previously subscribed matcher process specified by pid from the channel(s)

Types

channel()

@type channel() :: atom() | term()

The identifier of the channel, messages can be sent to, preferrably atom()

event()

@type event() :: term()

The event being sent to the listeners

handler()

@type handler() ::
  (event() -> :ok)
  | (channel(), event() -> :ok)
  | Antenna.Matcher.t()
  | pid()
  | GenServer.name()

The actual handler to be associated with an event(s). It might be either a function or a process id, in which case the message of a following shape will be sent to it.

{:antenna_event, channel, event}

id()

@type id() :: module()

The identifier of the isolated Antenna

Functions

event(id \\ Antenna, channels, event)

@spec event(id :: id(), channels :: channel() | [channel()], event :: event()) :: :ok

Sends an event to all the associated matchers through channels.

The special :* might be specified as channels, then the event will be sent to all the registered channels.

match(id \\ Antenna, match, handlers, opts \\ [])

(macro)

Declares a matcher for tagged events.

Example

Antenna.match(Antenna, %{tag: _, success: false}, fn channel, message ->
  Logger.warning("The processing failed for [" <> 
    inspect(channel) <> "], result: " <> inspect(message))
end, channels: [:rabbit])

registered_matchers(id)

@spec registered_matchers(id :: id()) :: %{
  required(term()) => {pid(), Supervisor.child_spec()}
}

Returns a map of matches to matchers

subscribe(id \\ Antenna, channels, pid)

@spec subscribe(id :: id(), channels :: channel() | [channel()], pid()) :: :ok

Subscribes a matcher process specified by pid to a channel(s)

unmatch(id \\ Antenna, match)

(macro)

Undeclares a matcher for tagged events previously declared with Antenna.match/4.

Accepts both an original match or a name returned by Antenna.match/4, which is effectively Macro.to_string(match).

Example

Antenna.unmatch(Antenna, %{tag: _, success: false})

unsubscribe(id \\ Antenna, channels, pid)

@spec unsubscribe(id :: id(), channels :: channel() | [channel()], pid()) :: :ok

Unsubscribes a previously subscribed matcher process specified by pid from the channel(s)