Current section
Files
Jump to
Current section
Files
lib/mammoth/subscriber.ex
defmodule Mammoth.Subscriber do
@moduledoc """
Keep track of subscriptions to topics or queues.
## Example
{:ok, pid} = Subscriber.start_link()
Subscriber.subscribe(pid, "foo.bar")
Subscriber.subscribe(pid, "foo2.bar")
%{id: id} = Subscriber.get_subscription(pid, "foo.bar")
Subscriber.unsubscribe(pid, "foo2.bar")
"""
use GenServer
require Logger
# Client
def init(args) do
{:ok, Map.put_new(args, :subscriptions, %{})}
end
def start_link(state \\ %{}) do
GenServer.start_link(__MODULE__, state)
end
def subscribe(pid, destination, ack_mode) do
GenServer.call(pid, {:subscribe, destination, ack_mode})
end
def unsubscribe(pid, destination) do
GenServer.call(pid, {:unsubscribe, destination})
end
def has_subscription?(pid, destination) do
GenServer.call(pid, {:has_subscription, destination})
end
def get_subscription(pid, destination) do
GenServer.call(pid, {:get_subscription, destination})
end
def stop(server) do
GenServer.stop(server)
end
# Server
def handle_call(
{:has_subscription, destination},
_from,
state = %{subscriptions: subscriptions}
) do
{
:reply,
Map.has_key?(subscriptions, destination),
state
}
end
def handle_call(
{:get_subscription, destination},
_from,
state = %{subscriptions: subscriptions}
) do
{
:reply,
Map.get(subscriptions, destination),
state
}
end
def handle_call(
{:subscribe, destination, ack_mode},
_from,
state = %{subscriptions: subscriptions}
) do
if Map.has_key?(subscriptions, destination) do
{:reply, {:error, "You have already subscribed to this destination"}, state}
else
do_subscribe(destination, ack_mode, state)
end
end
def handle_call(
{:unsubscribe, destination},
_from,
state = %{subscriptions: subscriptions}
) do
if Map.has_key?(subscriptions, destination) do
entry = Map.get(subscriptions, destination)
{:reply, {:ok, entry}, %{state | subscriptions: Map.delete(subscriptions, destination)}}
else
{:reply, {:error, "You are not subscribed to this destination"}, state}
end
end
defp do_subscribe(destination, ack_mode, state = %{subscriptions: subscriptions}) do
id = next_id(subscriptions)
entry = %{id: id, ack_mode: ack_mode}
{
:reply,
{:ok, entry},
%{
state
| subscriptions: Map.put(subscriptions, destination, entry)
}
}
end
def next_id(subscriptions) do
(subscriptions
|> Map.values()
|> Enum.map(& &1.id)
|> Enum.max(fn -> 0 end)) + 1
end
end