# `VerifiedPubSub.Subscriber`
[🔗](https://github.com/CuriousCurmudgeon/verified_pubsub/blob/v0.1.0/lib/verified_pubsub/subscriber.ex#L1)

Declares that a module subscribes to topics of a manifest, and defines its
handlers.

    defmodule MyAppWeb.PetsLive do
      use MyAppWeb, :live_view
      use VerifiedPubSub.Subscriber, manifest: MyApp.Topics

      def mount(_params, _session, socket) do
        if connected?(socket), do: subscribe(:pets, %{owner_id: socket.assigns.id})
        {:ok, socket}
      end

      handle_message :pets, :adopted, payload, socket do
        {:noreply, stream_insert(socket, :pets, payload)}
      end

      ignore_message :pets, :rehomed
    end

The `handle_message/5` and `ignore_message/2` calls of the module set the topics that it
subscribes to. You keep no separate list of topics. The module must handle or dismiss
each event that those topics declare, or the module does not compile.

`use` also imports `VerifiedPubSub.Api`. The module can then call `subscribe/2`,
`broadcast!/4`, and the other macros without a separate
`use VerifiedPubSub, manifest: ...`.

## Generated code

`use` generates two `handle_info/2` clauses at the `use` site. The first clause matches
a `VerifiedPubSub.Message` from the manifest of *this* module. It passes the message to
private `__verified_pubsub_dispatch__/4` clauses, which the module defines at
`@before_compile`. The second clause matches a message from any other manifest and
raises `VerifiedPubSub.ManifestMismatchError`. See "Messages from another manifest"
below.

`handle_message/5` therefore generates no code of its own. It adds the clause to a
module attribute. The coverage check needs every clause, and the module has every
clause only at `@before_compile`. The attribute does *not* exist to avoid the
clause-grouping warning of Elixir, as an earlier version of this documentation said. A
clause that a macro generates carries `generated: true` metadata, and Elixir gives no
such warning for it.

The `handle_info/2` clauses come from the `use` site, not from `@before_compile`, so
your own `handle_info/2` clauses still work. Elixir tries your clauses *after* the
generated clauses. A catch-all clause of yours thus receives each message that is not
verified, and verified messages still reach the generated clauses.

## Messages from another manifest

An application can declare more than one manifest. Two manifests can produce the same
wire topic when they share a `:pubsub` and declare topics that build the same string.
The disjointness check works only *within* one manifest, because a manifest cannot see
the other manifests while it compiles.

In that case, `Phoenix.PubSub` delivers the message, and its `topic` and `event` atoms
can match a clause here. But its payload follows the field declarations of the
*sender*. The compiler never checked that payload against the handler. Thus the
generated clause raises `VerifiedPubSub.ManifestMismatchError`, and the handler does
not run.

That clause comes before each `handle_info/2` clause that you write, on purpose. A
catch-all clause of yours thus does **not** receive a message from another manifest. A
catch-all clause is the documented way to ignore unrelated messages. If it received
this message, nothing would ever report the collision.

For full isolation, give each manifest its own `Phoenix.PubSub`. Each instance has its
own registry, so a message never goes from one instance to the other:

    children = [
      Supervisor.child_spec({Phoenix.PubSub, name: Pets.PubSub}, id: :pets_pubsub),
      Supervisor.child_spec({Phoenix.PubSub, name: Treats.PubSub}, id: :treats_pubsub)
    ]

Which modules can bind which manifest is a separate question of dependency boundaries.
A manifest is a plain module, so `mix xref` or the `boundary` package can enforce
those boundaries better than this library can.

## Messages this module does not expect

A module that defines a `handle_info/2` clause loses the default clause that
`use GenServer` and `use Phoenix.LiveView` add. An unexpected message then raises
`FunctionClauseError`, and nothing logs it. Each GenServer with its own
`handle_info/2` has this behavior. This module cannot prevent it: Elixir 1.20 made
`super/2` for GenServer callbacks an error, so no code can call the default body.

When your process receives messages that are not verified, add your own catch-all
clause:

    handle_message :pets, :adopted, payload, state do
      {:noreply, state}
    end

    # Matched after the generated clauses, so verified messages still reach them.
    def handle_info(_other, state), do: {:noreply, state}

# `handle_message`
*macro* 

Handles one event on one topic.

`pattern` matches the `payload` of the message. When `pattern` is a
`%VerifiedPubSub.Message{}` pattern in the source, it matches the whole message
instead, so it can match on `params`.

# `handle_message`
*macro* 

Handles one event on one topic, and matches the params of the topic.

The first arguments are the arguments of `broadcast!/4`, in the same order, but here
they **match** and do not build. Unlike a broadcast, the pattern can name a subset of
the params. A param that the topic does not declare is a compile error, because that
pattern can never match.

## Usage

    broadcast!     :pets, %{owner_id: id},    :adopted, payload
    handle_message :pets, %{owner_id: owner}, :adopted, payload, socket

The clause binds `owner` from the params, without a match on the whole message. A
literal limits the clause to that value:

    handle_message :pets, %{owner_id: "7"}, :adopted, payload, socket

# `ignore_message`
*macro* 

Declares that this module does nothing with an event, or with a list of events.

    ignore_message :pets, :rehomed
    ignore_message :pets, [:renamed, :rehomed]

The call satisfies the coverage check without a handler. The generated clause returns
`{:noreply, state}`, which is correct for both a GenServer and a LiveView.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
