defmodule PlanB.EventBus do use GenServer @moduledoc """ ## Module EventBus This module is created as a GenServer. To use it just call start_link/1, then create a channel and register subscribers to it. """ # Server side @spec init(any) :: {:ok, any} def init(state) do {:ok, state} end def handle_call({:lookup, name}, _from, state) when is_binary(name) do state |> Map.fetch(name) |> case do :error -> {:reply, :fail, state} {:ok, modules} -> results = modules |> Enum.map(fn (module) -> {module, Process.whereis(module) } end) {:reply, {name, results}, state} end end def handle_call({:register, {name, module}}, _from, state) do state |> Map.fetch(name) |> case do :error -> new_state = state |> Map.put(name, [ module ]) {:reply, :ok, new_state} {:ok, modules} -> modules |> Enum.member?(module) |> case do true -> {:reply, :ok, state} false -> new_state = state |> Map.put(name, modules ++ [module]) {:reply, :ok, new_state} end end end def handle_cast({:publish, name, payload}, state) do state |> Map.fetch(name) |> case do :error -> {:noreply, state} {:ok, modules} -> modules |> Enum.each(fn (module) -> GenServer.cast(module, payload) end) {:noreply, state} end end # Client side @spec start_link(any) :: :ignore | {:error, any} | {:ok, pid} def start_link([state] = args) when is_list(args) and is_map(state) do GenServer.start_link __MODULE__, state, name: __MODULE__ end @spec stop_server :: :ok def stop_server, do: GenServer.stop __MODULE__ @spec health_check :: :fail | :ok def health_check do case GenServer.whereis(__MODULE__) do pid when is_pid(pid) -> :ok _ -> :fail end end @spec register(binary, Module ) :: any def register(name, module), do: GenServer.call __MODULE__, {:register, {name, module}} @spec lookup(binary) :: any def lookup(name) when is_binary(name), do: GenServer.call __MODULE__, {:lookup, name} @spec publish(binary, any) :: any def publish(name, payload) when is_binary(name), do: GenServer.cast __MODULE__, {:publish, name, payload} end