# Notifier GenServer-based pub/sub notification system with automatic subscriber cleanup via process monitoring. ## Overview `Notifier` is a lightweight publish/subscribe system built on GenServer. Processes subscribe with an action atom, and when a notification is broadcast, every subscriber receives a `GenServer.cast` with `{action, notification}`. Subscribers are monitored with `Process.monitor/1` and automatically removed when they terminate. Multiple independent notifier instances can coexist. String-named notifiers are registered via `:gproc` (local, non-unique), while atom-named notifiers use standard Erlang process registration. ## Features | Feature | Description | |---------|-------------| | Action-based dispatch | Subscribers choose the cast action atom they receive, enabling pattern-matched callbacks | | Automatic cleanup | Monitored via `Process.monitor/1`; dead subscribers are removed on `:DOWN` | | Dual registration | String names use `:gproc` via tuples; atom names use standard registration | | Supervision-ready | `child_spec/1` allows direct inclusion in a supervision tree | | Broadcast delivery | `notify/2` fans out to all subscribers synchronously via `GenServer.cast` | ## GenServer State The state is a simple list of `{pid, action}` tuples: ```elixir [ {#PID<0.123.0>, :handle_event}, {#PID<0.456.0>, :log}, {#PID<0.789.0>, :handle_event} ] ``` ## Types ```elixir @type name :: binary() | atom() | GenServer.server() @type action :: atom() @type subscriber :: {pid(), action()} @type state :: [subscriber()] ``` ## Public API ### start_link/1 Start a notifier process with the given name. ```elixir # String name — registered via gproc {:ok, pid} = Notifier.start_link("my_notifier") # Atom name — standard process registration {:ok, pid} = Notifier.start_link(:global_notifier) ``` ### child_spec/1 Returns a child specification for supervision trees. ```elixir children = [ {Notifier, name: "events"}, {Notifier, name: :my_notifier} ] Supervisor.start_link(children, strategy: :one_for_one) ``` ### subscribe/2 Subscribe the calling process to receive notifications. The `action` atom becomes the first element of the cast tuple. ```elixir :ok = Notifier.subscribe("events", :handle_event) :ok = Notifier.subscribe(:my_notifier, :log) # The subscriber will receive casts like: # GenServer.cast(self(), {:handle_event, notification}) ``` ### notify/2 Broadcast a notification to all subscribers. Each receives `{action, notification}` as a cast. ```elixir :ok = Notifier.notify("events", %{type: :user_created, user_id: 123}) :ok = Notifier.notify(:my_notifier, "Hello subscribers!") ``` ### unsubscribe/1 Remove the calling process from the subscriber list. ```elixir :ok = Notifier.unsubscribe("events") ``` ### get_subscribers/1 Return the list of current `{pid, action}` subscriber tuples. ```elixir Notifier.get_subscribers("events") # => [{#PID<0.123.0>, :handle_event}, {#PID<0.456.0>, :log}] ``` ### via/1 Build a `:gproc` via tuple for string-named notifiers. ```elixir Notifier.via("events") # => {:via, :gproc, {:n, :l, {Notifier, "events"}}} ``` ## Internal Behaviour ### Subscriber Monitoring When a process calls `subscribe/2`, the notifier calls `Process.monitor/1` on the subscriber's pid. If the subscriber terminates for any reason, the notifier receives a `:DOWN` message and removes all entries for that pid from the subscriber list. ### Notification Delivery `notify/2` is a synchronous `GenServer.call` that iterates through all subscribers and sends each a `GenServer.cast` with `{action, notification}`. The call returns `:ok` after all casts have been dispatched. ## Usage in the Codebase | Consumer | Notifier Name | Action | Purpose | |----------|---------------|--------|---------| | `LoggerNotifier` | `:logger_notifier` | N/A (produces) | Broadcasts log events to subscribers | | `LogSubscriber` | `:logger_notifier` | `:handle_log` | Receives log events for ANSI console output | | `AutoCompile` | `:web_notifier` | N/A (produces) | Broadcasts websocket reload signals | ## Dependencies | Module | Purpose | |--------|---------| | `:gproc` | Process registration for string-named notifiers | | `Logger` | Logging subscribe/unsubscribe/init events |