View Source PubSub

PubSub (Publish-Subscribe) is a messaging pattern where clients publish messages to channels, and other clients subscribed to those channels receive and react to the messages in real time. Tamnoon provides built-in PubSub functionality, enabling communication between multiple clients.

In Tamnoon's case:

  • Clients publish method calls to channels.

  • Other clients subscribed to those channels will receive and execute the method calls.

  • Clients can subscribe and unsubscribe from channels dynamically.

This allows for real-time interaction between clients - ideal for collaborative apps, chat rooms, live dashboards, and more.

Example

If a client publishes an :add_message method to the channel "room_123", all clients subscribed to "room_123" will receive and execute the :add_message method.

Channels

When publishing method calls, they are sent to a specific channel. Clients can dynamically join or leave channels using the built-in :sub and :unsub methods, and view their current subscribed channels using Tamnoon.Methods.subbed_channels/0.

  • :sub subscribes the client to a channel.

  • :unsub unsubscribes the client from a channel.

  • Both methods expect a channel name passed in as req["channel"].

Channels are automatically created when a client attempts to subscribe to one that doesn't exist.

The clients channel

All clients are automatically subscribed to a special channel named "clients". This channel is non-leavable.

Example

defmethod :switch_room do
  target_room_id = req["value"]

  Tamnoon.Methods.unsub(%{"channel" => "room_\#{state[:current_room_id]}"}, state)
  Tamnoon.Methods.sub(%{"channel" => "room_\#{target_room_id}"}, state)

  {%{current_room_id: target_room_id}}
end

In the example above:

  • The client leaves their current room channel (e.g., "room_1").

  • Then joins the new room channel (e.g., "room_2").

  • The client's state is updated to reflect the new current_room_id.

Manual invocation of PubSub methods

Unlike other methods, PubSub methods (:sub, :unsub and :pub) just trigger a side effect. This means that when using the manual invocation syntax, there is no need to return their return value from the method, and it can be safely ignored.

Publishing

In Tamnoon, clients communicate with each other by publishing method calls. These calls are broadcast to a specified channel, where all clients subscribed to that channel (including the sender) will receive and execute the method as if they triggered it themselves.

To publish a method call, use the built-in :pub method, which accepts:

  • "channel" - the name of the channel to publish to.

  • "action" - a map containing:

    • "method" - the method name to call.

    • Any additional data to pass along via req.

Example

defmethod :send_message do
  %{
    current_message: current_message,
    current_room_id: current_room_id
  } = state

  Tamnoon.Methods.pub(%{
    "channel" => "room_#{current_room_id}",
    "action" => %{
      "method" => "add_message",
      "message" => current_message
    }
  }, state)

  {%{current_message: ""}}
end

What this does:

  • Publishes a call to the :add_message method on the channel for the current room.

    • All clients in that room (including the sender) will receive and run :add_message.

    • The message content is sent as req["message"].

  • Clears the :current_message state field on the sender's side only (clean-up).

This is a common pattern in Tamnoon:

  • One method (e.g., :send_message) handles logic only the publisher needs (validation, cleanup, etc.).

  • It then publishes another method (e.g., :add_message) that contains logic for all clients in the channel.