Class: Ably::Realtime::Presence

Inherits:
Object
  • Object
show all
Extended by:
Modules::Enum
Includes:
Modules::AsyncWrapper, Modules::Conversions, Modules::EventEmitter, Modules::MessageEmitter, Modules::SafeYield, Modules::StateEmitter, Modules::UsesStateMachine
Defined in:
lib/ably/realtime/presence.rb,
lib/ably/realtime/presence/members_map.rb,
lib/ably/realtime/presence/presence_manager.rb,
lib/ably/realtime/presence/presence_state_machine.rb

Overview

Presence provides access to presence operations and state for the associated Channel

Defined Under Namespace

Classes: MembersMap, PresenceManager, PresenceStateMachine

Constant Summary collapse

STATE =
ruby_enum('STATE',
  :initialized,
  :entering,
  :entered,
  :leaving,
  :left
)

Instance Attribute Summary collapse

Attributes included from Modules::UsesStateMachine

#previous_state, #state_history

Instance Method Summary collapse

Methods included from Modules::UsesStateMachine

#synchronize_state_with_statemachine, #transition_state_machine, #transition_state_machine!

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::MessageEmitter

#emit_message

Methods included from Modules::EventEmitter

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

Constructor Details

#initialize(channel) ⇒ Presence

Returns a new instance of Presence


43
44
45
46
47
48
49
50
51
# File 'lib/ably/realtime/presence.rb', line 43

def initialize(channel)
  @channel       = channel
  @client_id     = client.client_id

  @state_machine = PresenceStateMachine.new(self)
  @state         = STATE(state_machine.current_state)
  @members       = MembersMap.new(self)
  @manager       = PresenceManager.new(self)
end

Instance Attribute Details

#__incoming_msgbus__Ably::Util::PubSub (readonly)

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 Client library internal channel incoming protocol message bus

Returns:


311
312
313
314
315
# File 'lib/ably/realtime/presence.rb', line 311

def __incoming_msgbus__
  @__incoming_msgbus__ ||= Ably::Util::PubSub.new(
    coerce_into: lambda { |event| Ably::Models::ProtocolMessage::ACTION(event) }
  )
end

#channelAbly::Realtime::Channel (readonly)

Channel this Presence object is associated with


23
24
25
# File 'lib/ably/realtime/presence.rb', line 23

def channel
  @channel
end

#client_idString (readonly)

The client_id for the member present on this channel

Returns:

  • (String)

27
28
29
# File 'lib/ably/realtime/presence.rb', line 27

def client_id
  @client_id
end

#dataString (readonly)

The data for the member present on this channel

Returns:

  • (String)

31
32
33
# File 'lib/ably/realtime/presence.rb', line 31

def data
  @data
end

#managerAbly::Realtime::Presence::PresenceManager (readonly)

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.

The Presence manager responsible for actions relating to state changes such as entering a channel


41
42
43
# File 'lib/ably/realtime/presence.rb', line 41

def manager
  @manager
end

#membersMembersMap (readonly)

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.

MembersMap containing an up to date list of members on this channel

Returns:


36
37
38
# File 'lib/ably/realtime/presence.rb', line 36

def members
  @members
end

Instance Method Details

#enter(data = nil) {|Ably::Realtime::Presence| ... } ⇒ Ably::Util::SafeDeferrable

Enter this client into this channel. This client will be added to the presence set and presence subscribers will see an enter message for this client.

Parameters:

  • data (String, Hash, nil) (defaults to: nil)

    optional data (eg a status message) for this member

Yields:

Returns:


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
87
88
89
90
91
# File 'lib/ably/realtime/presence.rb', line 61

def enter(data = nil, &success_block)
  deferrable = create_deferrable

  ensure_supported_payload data
  @data = data

  return deferrable_succeed(deferrable, &success_block) if state == STATE.Entered

  requirements_failed_deferrable = ensure_presence_publishable_on_connection_deferrable
  return requirements_failed_deferrable if requirements_failed_deferrable

  ensure_channel_attached(deferrable) do
    if entering?
      once_or_if(STATE.Entered, else: lambda { |args| deferrable_fail deferrable, *args }) do
        deferrable_succeed deferrable, &success_block
      end
    else
      current_state = state
      change_state STATE.Entering
      send_protocol_message_and_transition_state_to(
        Ably::Models::PresenceMessage::ACTION.Enter,
        deferrable:   deferrable,
        target_state: STATE.Entered,
        data:         data,
        client_id:    client_id,
        failed_state: current_state, # return to current state if enter fails
        &success_block
      )
    end
  end
end

#enter_client(client_id, data = nil) {|Ably::Realtime::Presence| ... } ⇒ Ably::Util::SafeDeferrable

Enter the specified client_id into this channel. The given client will be added to the presence set and presence subscribers will see a corresponding presence message. This method is provided to support connections (e.g. connections from application server instances) that act on behalf of multiple client_ids. In order to be able to enter the channel with this method, the client library must have been instanced either with a key, or with a token bound to the wildcard client_id

Parameters:

  • client_id (String)

    id of the client

  • data (String, Hash, nil) (defaults to: nil)

    optional data (eg a status message) for this member

Yields:

Returns:


106
107
108
109
110
111
# File 'lib/ably/realtime/presence.rb', line 106

def enter_client(client_id, data = nil, &success_block)
  ensure_supported_client_id client_id
  ensure_supported_payload data

  send_presence_action_for_client(Ably::Models::PresenceMessage::ACTION.Enter, client_id, data, &success_block)
end

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

Get the presence members for this Channel.

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:


225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/ably/realtime/presence.rb', line 225

def get(options = {}, &block)
  deferrable = create_deferrable

  # #RTP11d Don't return PresenceMap when wait for sync is true
  #   if the map is stale
  wait_for_sync = options.fetch(:wait_for_sync, true)
  if wait_for_sync && channel.suspended?
    EventMachine.next_tick do
      deferrable.fail Ably::Exceptions::InvalidState.new(
        'Presence state is out of sync as channel is SUSPENDED. Presence#get on a SUSPENDED channel is only supported with option wait_for_sync: false',
        nil,
        Ably::Exceptions::Codes::PRESENCE_STATE_IS_OUT_OF_SYNC
      )
    end
    return deferrable
  end

  ensure_channel_attached(deferrable, allow_suspended: true) do
    members.get(options).tap do |members_map_deferrable|
      members_map_deferrable.callback do |members|
        safe_yield(block, members) if block_given?
        deferrable.succeed(members)
      end
      members_map_deferrable.errback do |*args|
        deferrable.fail(*args)
      end
    end
  end
end

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

Return the presence messages history for the channel

Once attached to a channel, you can retrieve presence message history on the channel before the channel was attached with the option until_attach: true. This is very useful for developers who wish to capture new presence events as well as retrieve historical presence state with the guarantee that no presence history has been missed.

Parameters:

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

    the options for the message history request

Options Hash (options):

  • :until_attach (Boolean)

    When true, request for history will be limited only to messages published before the associated channel was attached. The associated channel must be attached.

  • :start (Integer, Time)

    Ensure earliest time or millisecond since epoch for any presence messages retrieved is :start

  • :end (Integer, Time)

    Ensure latest time or millisecond since epoch for any presence messages retrieved is :end

  • :direction (Symbol)

    :forwards or :backwards, defaults to :backwards

  • :limit (Integer)

    Maximum number of messages to retrieve up to 1,000, defaults to 100

Yields:

Returns:


294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/ably/realtime/presence.rb', line 294

def history(options = {}, &callback)
  if options.delete(:until_attach)
    unless channel.attached?
      error = Ably::Exceptions::InvalidRequest.new('option :until_attach is invalid as the channel is not attached')
      return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
    end
    options[:from_serial] = channel.attached_serial
  end

  async_wrap(callback) do
    rest_presence.history(options.merge(async_blocking_operations: true))
  end
end

#leave(data = nil) {|Ably::Realtime::Presence| ... } ⇒ Ably::Util::SafeDeferrable

Leave this client from this channel. This client will be removed from the presence set and presence subscribers will see a leave message for this client.

Parameters:

  • data (String, Hash, nil) (defaults to: nil)

    optional data (eg a status message) for this member

Yields:

Returns:


121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/ably/realtime/presence.rb', line 121

def leave(data = nil, &success_block)
  deferrable = create_deferrable

  ensure_supported_payload data

  @data = data

  return deferrable_succeed(deferrable, &success_block) if state == STATE.Left

  requirements_failed_deferrable = ensure_presence_publishable_on_connection_deferrable
  return requirements_failed_deferrable if requirements_failed_deferrable

  ensure_channel_attached(deferrable) do
    if leaving?
      once_or_if(STATE.Left, else: lambda { |error|deferrable_fail deferrable, *args }) do
        deferrable_succeed deferrable, &success_block
      end
    else
      current_state = state
      change_state STATE.Leaving
      send_protocol_message_and_transition_state_to(
        Ably::Models::PresenceMessage::ACTION.Leave,
        deferrable:   deferrable,
        target_state: STATE.Left,
        data:         data,
        client_id:    client_id,
        failed_state: current_state, # return to current state if leave fails
        &success_block
      )
    end
  end
end

#leave_client(client_id, data = nil) {|Ably::Realtime::Presence| ... } ⇒ Ably::Util::SafeDeferrable

Leave a given client_id from this channel. This client will be removed from the presence set and presence subscribers will see a leave message for this client.

Parameters:

  • client_id (String)

    id of the client

  • data (String, Hash, nil) (defaults to: nil)

    optional data (eg a status message) for this member

Yields:

Returns:


162
163
164
165
166
167
# File 'lib/ably/realtime/presence.rb', line 162

def leave_client(client_id, data = nil, &success_block)
  ensure_supported_client_id client_id
  ensure_supported_payload data

  send_presence_action_for_client(Ably::Models::PresenceMessage::ACTION.Leave, client_id, data, &success_block)
end

#loggerObject

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.

Used by Modules::StateEmitter to debug action changes


319
320
321
# File 'lib/ably/realtime/presence.rb', line 319

def logger
  client.logger
end

#subscribe(*actions) {|Ably::Models::PresenceMessage| ... } ⇒ void

This method returns an undefined value.

Subscribe to presence events on the associated Channel. This implicitly attaches the Channel if it is not already attached.

Parameters:

Yields:


263
264
265
266
# File 'lib/ably/realtime/presence.rb', line 263

def subscribe(*actions, &callback)
  implicit_attach
  super
end

#sync_complete?Boolean

Returns true when the initial member SYNC following channel attach is completed

Returns:

  • (Boolean)

324
325
326
# File 'lib/ably/realtime/presence.rb', line 324

def sync_complete?
  members.sync_complete?
end

#unsubscribe(*actions, &callback) ⇒ void

This method returns an undefined value.

Unsubscribe the matching block for presence events on the associated Channel. If a block is not provided, all subscriptions will be unsubscribed

Parameters:


275
276
277
# File 'lib/ably/realtime/presence.rb', line 275

def unsubscribe(*actions, &callback)
  super
end

#update(data = nil) {|Ably::Realtime::Presence| ... } ⇒ Ably::Util::SafeDeferrable

Update the presence data for this client. If the client is not already a member of the presence set it will be added, and presence subscribers will see an enter or update message for this client.

Parameters:

  • data (String, Hash, nil) (defaults to: nil)

    optional data (eg a status message) for this member

Yields:

Returns:


178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/ably/realtime/presence.rb', line 178

def update(data = nil, &success_block)
  deferrable = create_deferrable

  ensure_supported_payload data

  @data = data

  requirements_failed_deferrable = ensure_presence_publishable_on_connection_deferrable
  return requirements_failed_deferrable if requirements_failed_deferrable

  ensure_channel_attached(deferrable) do
    send_protocol_message_and_transition_state_to(
      Ably::Models::PresenceMessage::ACTION.Update,
      deferrable:   deferrable,
      target_state: STATE.Entered,
      client_id:    client_id,
      data:         data,
      &success_block
    )
  end
end

#update_client(client_id, data = nil) {|Ably::Realtime::Presence| ... } ⇒ Ably::Util::SafeDeferrable

Update the presence data for a specified client_id into this channel. If the client is not already a member of the presence set it will be added, and presence subscribers will see an enter or update message for this client. As with #enter_client, the connection must be authenticated in a way that enables it to represent an arbitrary clientId.

Parameters:

  • client_id (String)

    id of the client

  • data (String, Hash, nil) (defaults to: nil)

    optional data (eg a status message) for this member

Yields:

Returns:


211
212
213
214
215
216
# File 'lib/ably/realtime/presence.rb', line 211

def update_client(client_id, data = nil, &success_block)
  ensure_supported_client_id client_id
  ensure_supported_payload data

  send_presence_action_for_client(Ably::Models::PresenceMessage::ACTION.Update, client_id, data, &success_block)
end