Skip to content

Commit 48b5f51

Browse files
committed
feat: custom dispatcher for Presence handle_diff broadcast
1 parent 1cd6b71 commit 48b5f51

File tree

2 files changed

+75
-10
lines changed

2 files changed

+75
-10
lines changed

lib/phoenix/presence.ex

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,18 @@ defmodule Phoenix.Presence do
7979
8080
See `c:list/1` for more information on the presence data structure.
8181
82+
## Custom dispatcher
83+
84+
It's possible to customize the dispatcher module used to broadcast.
85+
By default, `Phoenix.Channel.Server` is used, which is the same dispatcher
86+
used by channels. To customize the dispatcher, pass the `:dispatcher` option
87+
when using `Phoenix.Presence`:
88+
89+
use Phoenix.Presence,
90+
otp_app: :my_app,
91+
pubsub_server: MyApp.PubSub,
92+
dispatcher: MyApp.CustomDispatcher
93+
8294
## Fetching Presence Information
8395
8496
Presence metadata should be minimized and used to store small,
@@ -417,9 +429,11 @@ defmodule Phoenix.Presence do
417429
pubsub_server =
418430
opts[:pubsub_server] || raise "use Phoenix.Presence expects :pubsub_server to be given"
419431

432+
dispatcher = opts[:dispatcher] || Phoenix.Channel.Server
433+
420434
Phoenix.Tracker.start_link(
421435
__MODULE__,
422-
{module, task_supervisor, pubsub_server},
436+
{module, task_supervisor, pubsub_server, dispatcher},
423437
opts
424438
)
425439
end
@@ -455,15 +469,16 @@ defmodule Phoenix.Presence do
455469
end
456470

457471
@doc false
458-
def init({module, task_supervisor, pubsub_server}) do
472+
def init({module, task_supervisor, pubsub_server, dispatcher}) do
459473
state = %{
460474
module: module,
461475
task_supervisor: task_supervisor,
462476
pubsub_server: pubsub_server,
463477
topics: %{},
464478
tasks: :queue.new(),
465479
current_task: nil,
466-
client_state: nil
480+
client_state: nil,
481+
dispatcher: dispatcher
467482
}
468483

469484
client_state =
@@ -507,12 +522,13 @@ defmodule Phoenix.Presence do
507522
Task.shutdown(task)
508523

509524
Enum.each(computed_diffs, fn {topic, presence_diff} ->
510-
Phoenix.Channel.Server.local_broadcast(
511-
state.pubsub_server,
512-
topic,
513-
"presence_diff",
514-
presence_diff
515-
)
525+
broadcast = %Phoenix.Socket.Broadcast{
526+
topic: topic,
527+
event: "presence_diff",
528+
payload: presence_diff
529+
}
530+
531+
Phoenix.PubSub.local_broadcast(state.pubsub_server, topic, broadcast, state.dispatcher)
516532
end)
517533

518534
new_state =

test/phoenix/presence_test.exs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ defmodule Phoenix.PresenceTest do
3434
Phoenix.Presence.init({
3535
__MODULE__,
3636
__MODULE__.TaskSupervisor,
37-
PresPub
37+
PresPub,
38+
Phoenix.Channel.Server
3839
})
3940
end
4041

@@ -43,13 +44,27 @@ defmodule Phoenix.PresenceTest do
4344
end
4445
end
4546

47+
defmodule CustomDispatcher do
48+
def dispatch(entries, from, message) do
49+
for {pid, _} <- entries, pid != from, do: send(pid, {:custom_dispatcher, message})
50+
51+
:ok
52+
end
53+
end
54+
55+
defmodule CustomDispatcherPresence do
56+
use Phoenix.Presence, otp_app: :phoenix, dispatcher: CustomDispatcher
57+
end
58+
4659
Application.put_env(:phoenix, MyPresence, pubsub_server: PresPub)
4760
Application.put_env(:phoenix, MetasPresence, pubsub_server: PresPub)
61+
Application.put_env(:phoenix, CustomDispatcherPresence, pubsub_server: PresPub)
4862

4963
setup_all do
5064
start_supervised!({Phoenix.PubSub, name: PresPub, pool_size: 1})
5165
start_supervised!(MyPresence)
5266
start_supervised!(MetasPresence)
67+
start_supervised!(CustomDispatcherPresence)
5368
{:ok, pubsub: PresPub}
5469
end
5570

@@ -174,6 +189,40 @@ defmodule Phoenix.PresenceTest do
174189
assert MyPresence.list(topic) == %{}
175190
end
176191

192+
test "handle_diff with custom dispatcher", %{topic: topic} = config do
193+
pid = spawn(fn -> :timer.sleep(:infinity) end)
194+
Phoenix.PubSub.subscribe(config.pubsub, topic)
195+
start_supervised!({DefaultPresence, pubsub_server: config.pubsub})
196+
CustomDispatcherPresence.track(pid, topic, "u1", %{name: "u1"})
197+
198+
assert_receive {:custom_dispatcher,
199+
%Broadcast{
200+
topic: ^topic,
201+
event: "presence_diff",
202+
payload: %{
203+
joins: %{"u1" => %{metas: [%{name: "u1", phx_ref: u1_ref}]}},
204+
leaves: %{}
205+
}
206+
}}
207+
208+
assert %{"u1" => %{metas: [%{name: "u1", phx_ref: ^u1_ref}]}} =
209+
CustomDispatcherPresence.list(topic)
210+
211+
Process.exit(pid, :kill)
212+
213+
assert_receive {:custom_dispatcher,
214+
%Broadcast{
215+
topic: ^topic,
216+
event: "presence_diff",
217+
payload: %{
218+
joins: %{},
219+
leaves: %{"u1" => %{metas: [%{name: "u1", phx_ref: ^u1_ref}]}}
220+
}
221+
}}
222+
223+
assert CustomDispatcherPresence.list(topic) == %{}
224+
end
225+
177226
test "untrack with pid", %{topic: topic} = config do
178227
Phoenix.PubSub.subscribe(config.pubsub, config.topic)
179228
MyPresence.track(self(), config.topic, "u1", %{})

0 commit comments

Comments
 (0)