Class: Supabase::Realtime::Presence
- Inherits:
-
Object
- Object
- Supabase::Realtime::Presence
- Defined in:
- lib/supabase/realtime/presence.rb
Overview
Tracks presence state for one channel and implements the Phoenix Presence sync algorithm: presence_state replaces the local snapshot, presence_diff applies joins/leaves on top of it.
The algorithm mirrors phoenix.js’s Presence.syncState / Presence.syncDiff so callers porting from JS/Python see identical behavior.
Instance Attribute Summary collapse
-
#state ⇒ Object
readonly
Returns the value of attribute state.
Instance Method Summary collapse
- #any_callbacks? ⇒ Boolean
-
#initialize ⇒ Presence
constructor
A new instance of Presence.
-
#list ⇒ Object
List every meta currently tracked, flat.
- #on_join(&block) ⇒ Object
- #on_leave(&block) ⇒ Object
- #on_sync(&block) ⇒ Object
-
#sync_diff(diff) ⇒ Object
Subsequent presence_diff messages carry only joins/leaves to apply.
-
#sync_state(new_state) ⇒ Object
The first presence_state message after joining sends the full state.
Constructor Details
#initialize ⇒ Presence
Returns a new instance of Presence.
14 15 16 17 18 19 |
# File 'lib/supabase/realtime/presence.rb', line 14 def initialize @state = {} @on_sync_callbacks = [] @on_join_callbacks = [] @on_leave_callbacks = [] end |
Instance Attribute Details
#state ⇒ Object (readonly)
Returns the value of attribute state.
12 13 14 |
# File 'lib/supabase/realtime/presence.rb', line 12 def state @state end |
Instance Method Details
#any_callbacks? ⇒ Boolean
109 110 111 |
# File 'lib/supabase/realtime/presence.rb', line 109 def any_callbacks? [@on_sync_callbacks, @on_join_callbacks, @on_leave_callbacks].any? { |list| !list.empty? } end |
#list ⇒ Object
List every meta currently tracked, flat. Useful when callers don’t care about the per-key grouping.
90 91 92 |
# File 'lib/supabase/realtime/presence.rb', line 90 def list @state.values.flat_map { |presence| (presence) } end |
#on_join(&block) ⇒ Object
99 100 101 102 |
# File 'lib/supabase/realtime/presence.rb', line 99 def on_join(&block) @on_join_callbacks << block self end |
#on_leave(&block) ⇒ Object
104 105 106 107 |
# File 'lib/supabase/realtime/presence.rb', line 104 def on_leave(&block) @on_leave_callbacks << block self end |
#on_sync(&block) ⇒ Object
94 95 96 97 |
# File 'lib/supabase/realtime/presence.rb', line 94 def on_sync(&block) @on_sync_callbacks << block self end |
#sync_diff(diff) ⇒ Object
Subsequent presence_diff messages carry only joins/leaves to apply.
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/supabase/realtime/presence.rb', line 56 def sync_diff(diff) joins = diff["joins"] || {} leaves = diff["leaves"] || {} joins.each do |key, presence| if @state[key] existing_refs = (@state[key]).map { |m| m["phx_ref"] } = (presence).reject { |m| existing_refs.include?(m["phx_ref"]) } @state[key] = { "metas" => (@state[key]) + } else @state[key] = presence end end leaves.each do |key, presence| next unless @state[key] leaving_refs = (presence).map { |m| m["phx_ref"] } remaining = (@state[key]).reject { |m| leaving_refs.include?(m["phx_ref"]) } if remaining.empty? @state.delete(key) else @state[key] = { "metas" => remaining } end end emit_joins(joins) emit_leaves(leaves) @on_sync_callbacks.each(&:call) @state end |
#sync_state(new_state) ⇒ Object
The first presence_state message after joining sends the full state. Any local metas we already have for a key but the server doesn’t are emitted as leaves; anything new is emitted as a join.
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/supabase/realtime/presence.rb', line 24 def sync_state(new_state) joins = {} leaves = {} @state.each do |key, presence| leaves[key] = presence unless new_state.key?(key) end new_state.each do |key, new_presence| current = @state[key] if current joined = [] left = [] current_refs = (current).map { |m| m["phx_ref"] } new_refs = (new_presence).map { |m| m["phx_ref"] } joined = (new_presence).reject { |m| current_refs.include?(m["phx_ref"]) } left = (current).reject { |m| new_refs.include?(m["phx_ref"]) } joins[key] = { "metas" => joined } unless joined.empty? leaves[key] = { "metas" => left } unless left.empty? else joins[key] = new_presence end end @state = deep_copy(new_state) emit_joins(joins) emit_leaves(leaves) @on_sync_callbacks.each(&:call) @state end |