Class: Ably::Realtime::Presence::MembersMap Private

Inherits:
Object
  • Object
show all
Extended by:
Modules::Enum
Includes:
Modules::EventEmitter, Modules::SafeYield, Modules::StateEmitter, Enumerable
Defined in:
lib/ably/realtime/presence/members_map.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

A class encapsulating a map of the members of this presence channel, indexed by the unique Models::PresenceMessage#member_key

This map synchronises the membership of the presence set by handling SYNC messages from the service. Since sync messages can be out-of-order - e.g. a PRESENT sync event being received after that member has in fact left - this map keeps “witness” entries, with ABSENT Action, to remember the fact that a LEAVE event has been seen for a member. These entries are cleared once the last set of updates of a sync sequence have been received.

Constant Summary collapse

STATE =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

ruby_enum('STATE',
  :initialized,
  :sync_starting, # Indicates the client is waiting for SYNC ProtocolMessages from Ably
  :sync_none, # Indicates the ATTACHED ProtocolMessage had no presence flag and thus no members on the channel
  :finalizing_sync,
  :sync_complete, # Indicates completion of server initiated sync
  :failed
)

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Modules::StateEmitter

#once_or_if, #once_state_changed, #state, #state=, #state?, #unsafe_once_or_if, #unsafe_once_state_changed

Methods included from Modules::EventEmitter

#emit, #off, #on, #once, #unsafe_off, #unsafe_on, #unsafe_once

Constructor Details

#initialize(presence) ⇒ MembersMap

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of MembersMap.



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/ably/realtime/presence/members_map.rb', line 31

def initialize(presence)
  @presence = presence

  @state = STATE(:initialized)

  # Two sets of members maintained
  # @members contains all members present on the channel
  # @local_members contains only this connection's members for the purpose of re-entering the member if channel continuity is lost
  reset_members
  reset_local_members

  @absent_member_cleanup_queue = []

  # Each SYNC session has a unique ID so that following SYNC
  # any members present in the map without this session ID are
  # not present according to Ably, see #RTP19
  @sync_session_id = -1

  setup_event_handlers
end

Instance Attribute Details

#lengthInteger (readonly) Also known as: count, size

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns number of present members known at this point in time, will not wait for sync operation to complete.

Returns:

  • (Integer)

    number of present members known at this point in time, will not wait for sync operation to complete



132
133
134
# File 'lib/ably/realtime/presence/members_map.rb', line 132

def length
  present_members.length
end

Instance Method Details

#each(&block) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Note:

this method will not wait for the sync operation to complete so may return an incomplete set of members. Use #get instead.

Method to allow Ably::Realtime::Presence::MembersMap to be Enumerable



140
141
142
143
# File 'lib/ably/realtime/presence/members_map.rb', line 140

def each(&block)
  return to_enum(:each) unless block_given?
  present_members.each(&block)
end

#enter_local_membersObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/ably/realtime/presence/members_map.rb', line 155

def enter_local_members
  local_members.values.each do |member|
    logger.debug { "#{self.class.name}: Manually re-entering local presence member, client ID: #{member.client_id} with data: #{member.data}" }
    presence.enter_client_with_id(member.id, member.client_id, member.data).tap do |deferrable|
      deferrable.errback do |error|
        re_enter_error = Ably::Models::ErrorInfo.new(
          message: "unable to automatically re-enter presence channel for client_id '#{member.client_id}'. Source error code #{error.code} and message '#{error.message}'",
          code: Ably::Exceptions::Codes::UNABLE_TO_AUTOMATICALLY_REENTER_PRESENCE_CHANNEL
        )
        channel.emit :update, Ably::Models::ChannelStateChange.new(
          current: channel.state,
          previous: channel.state,
          event: Ably::Realtime::Channel::EVENT(:update),
          reason: re_enter_error,
          resumed: true
        )
      end
    end
  end
end

#get(options = {}) {|Array<Ably::Models::PresenceMessage>| ... } ⇒ Ably::Util::SafeDeferrable

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Get the list of presence members

Parameters:

  • options (Hash, String) (defaults to: {})

    an options Hash to filter members

Options Hash (options):

  • :client_id (String)

    optional client_id filter for the member

  • :connection_id (String)

    optional connection_id filter for the member

  • :wait_for_sync (String)

    defaults to true, if true the get method waits for the initial presence sync following channel attachment to complete before returning the members present, else it immediately returns the members present currently

Yields:

Returns:



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/ably/realtime/presence/members_map.rb', line 83

def get(options = {}, &block)
  wait_for_sync = options.fetch(:wait_for_sync, true)
  deferrable    = Ably::Util::SafeDeferrable.new(logger)

  result_block = lambda do
    present_members.tap do |members|
      members.keep_if { |member| member.connection_id == options[:connection_id] } if options[:connection_id]
      members.keep_if { |member| member.client_id == options[:client_id] } if options[:client_id]
    end.tap do |members|
      safe_yield block, members if block_given?
      deferrable.succeed members
    end
  end

  if !wait_for_sync || sync_complete?
    result_block.call
  else
    # Must be defined before subsequent procs reference this callback
    reset_callbacks = nil

    sync_complete_callback = lambda do
      reset_callbacks.call if reset_callbacks
      result_block.call
    end

    sync_failed_callback = lambda do |error|
      reset_callbacks.call if reset_callbacks
      deferrable.fail error
    end

    reset_callbacks = lambda do
      off(&sync_complete_callback)
      off(&sync_failed_callback)
      channel.off(&sync_failed_callback)
    end

    unsafe_once(:sync_complete, &sync_complete_callback)
    unsafe_once(:failed, &sync_failed_callback)

    channel.unsafe_once(:detaching, :detached, :failed) do |error_reason|
      sync_failed_callback.call error_reason
    end
  end

  deferrable
end

#local_membersHash<String, PresenceMessage>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

A copy of the local members present i.e. members entered from this connection and thus the responsibility of this library to re-enter on the channel automatically if the channel loses continuity

Returns:

  • (Hash<String, PresenceMessage>)


151
152
153
# File 'lib/ably/realtime/presence/members_map.rb', line 151

def local_members
  @local_members
end

#sync_serial_cursor_at_end?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

When channel serial in ProtocolMessage SYNC is nil or an empty cursor appears after the ‘:’ such as ‘cf30e75054887:psl_7g:client:189’. That is an indication that there are no more SYNC messages.

Returns:

  • (Boolean)


68
69
70
# File 'lib/ably/realtime/presence/members_map.rb', line 68

def sync_serial_cursor_at_end?
  sync_serial.nil? || sync_serial.to_s.match(/^[\w-]+:?$/)
end

#update_sync_serial(serial) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Update the SYNC serial from the ProtocolMessage so that SYNC can be resumed. If the serial is nil, or the part after the first : is empty, then the SYNC is complete



58
59
60
# File 'lib/ably/realtime/presence/members_map.rb', line 58

def update_sync_serial(serial)
  @sync_serial = serial
end