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,
  :failed
)

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_on, #unsafe_once

Constructor Details

#initialize(channel) ⇒ Presence

Returns a new instance of Presence.



49
50
51
52
53
54
55
56
57
# File 'lib/ably/realtime/presence.rb', line 49

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:



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

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

#channelAbly::Realtime::Channel (readonly)

Channel this Presence object is associated with



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

def channel
  @channel
end

#client_idString (readonly)

The client_id for the member present on this channel

Returns:

  • (String)


33
34
35
# File 'lib/ably/realtime/presence.rb', line 33

def client_id
  @client_id
end

#connection_idString (readonly)

A unique identifier for this channel client based on their connection, disambiguating situations where a given client_id is present on multiple connections simultaneously.

Returns:

  • (String)


29
30
31
# File 'lib/ably/realtime/presence.rb', line 29

def connection_id
  @connection_id
end

#dataString (readonly)

The data for the member present on this channel

Returns:

  • (String)


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

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



47
48
49
# File 'lib/ably/realtime/presence.rb', line 47

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:



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

def members
  @members
end

Instance Method Details

#enter(options = {}) {|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:

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

    an options Hash to specify client data and/or client ID

Options Hash (options):

  • :data (String)

    optional data (eg a status message) for this member

  • :client_id (String)

    the optional id of the client. This option is provided to support connections from 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 clientId.

Yields:

Returns:



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/ably/realtime/presence.rb', line 72

def enter(options = {}, &success_block)
  client_id  = options.fetch(:client_id, self.client_id)
  data       = options.fetch(:data, nil)
  deferrable = create_deferrable

  ensure_supported_client_id client_id
  ensure_supported_payload data unless data.nil?

  @data = data
  @client_id = client_id

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

  ensure_presence_publishable_on_connection
  ensure_channel_attached(deferrable) do
    if entering?
      once_or_if(STATE.Entered, else: proc { |args| deferrable_fail deferrable, *args }) do
        deferrable_succeed deferrable, &success_block
      end
    else
      change_state STATE.Entering
      send_protocol_message_and_transition_state_to(
        Ably::Models::PresenceMessage::ACTION.Enter,
        deferrable:   deferrable,
        target_state: STATE.Entered,
        client_id:    client_id,
        data:         data,
        failed_state: STATE.Failed,
        &success_block
      )
    end
  end
end

#enter_client(client_id, options = {}) {|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

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

    an options Hash for this client event

Options Hash (options):

  • :data (String)

    optional data (eg a status message) for this member

Yields:

Returns:

Raises:

  • (ArgumentError)


121
122
123
124
125
126
127
# File 'lib/ably/realtime/presence.rb', line 121

def enter_client(client_id, options = {}, &success_block)
  raise ArgumentError, 'options must be a Hash' unless options.kind_of?(Hash)
  ensure_supported_client_id client_id
  ensure_supported_payload options[:data] if options.has_key?(:data)

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

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

Get the presence state for this Channel.

Parameters:

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

    an options Hash to filter members

Options Hash (options):

  • :client_id (String)

    optional client_id for the member

  • :connection_id (String)

    optional connection_id for the member

  • :wait_for_sync (String)

    defaults to false, if true the get method waits for the initial presence sync following channel attachment to complete before returning the members present

Yields:

Returns:



247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/ably/realtime/presence.rb', line 247

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

  ensure_channel_attached(deferrable) do
    members.get(options).tap do |members_map_deferrable|
      members_map_deferrable.callback do |*args|
        safe_yield(block, *args) if block_given?
        deferrable.succeed(*args)
      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:



303
304
305
306
307
308
309
310
311
312
# File 'lib/ably/realtime/presence.rb', line 303

def history(options = {}, &callback)
  if options.delete(:until_attach)
    raise ArgumentError, 'option :until_attach cannot be specified if the channel is not attached' unless channel.attached?
    options[:from_serial] = channel.attached_serial
  end

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

#leave(options = {}) {|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:

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

    an options Hash to specify client data and/or client ID

Options Hash (options):

  • :data (String)

    optional data (eg a status message) for this member

Yields:

Returns:

Raises:



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/ably/realtime/presence.rb', line 138

def leave(options = {}, &success_block)
  data       = options.fetch(:data, self.data) # nil value defaults leave data to existing value
  deferrable = create_deferrable

  ensure_supported_client_id client_id
  ensure_supported_payload data unless data.nil?
  raise Ably::Exceptions::Standard.new('Unable to leave presence channel that is not entered', 400, 91002) unless able_to_leave?

  @data = data

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

  ensure_presence_publishable_on_connection
  ensure_channel_attached(deferrable) do
    if leaving?
      once_or_if(STATE.Left, else: proc { |error|deferrable_fail deferrable, *args }) do
        deferrable_succeed deferrable, &success_block
      end
    else
      change_state STATE.Leaving
      send_protocol_message_and_transition_state_to(
        Ably::Models::PresenceMessage::ACTION.Leave,
        deferrable:   deferrable,
        target_state: STATE.Left,
        client_id:    client_id,
        data:         data,
        failed_state: STATE.Failed,
        &success_block
      )
    end
  end
end

#leave_client(client_id, options = {}) {|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

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

    an options Hash for this client event

Options Hash (options):

  • :data (String)

    optional data (eg a status message) for this member

Yields:

Returns:

Raises:

  • (ArgumentError)


180
181
182
183
184
185
186
# File 'lib/ably/realtime/presence.rb', line 180

def leave_client(client_id, options = {}, &success_block)
  raise ArgumentError, 'options must be a Hash' unless options.kind_of?(Hash)
  ensure_supported_client_id client_id
  ensure_supported_payload options[:data] if options.has_key?(:data)

  send_presence_action_for_client(Ably::Models::PresenceMessage::ACTION.Leave, client_id, options, &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



332
333
334
# File 'lib/ably/realtime/presence.rb', line 332

def logger
  client.logger
end

#set_connection_id(new_connection_id) ⇒ 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.

Configure the connection ID for this presence channel. Typically configured only once when a user first enters a presence channel.



326
327
328
# File 'lib/ably/realtime/presence.rb', line 326

def set_connection_id(new_connection_id)
  @connection_id = new_connection_id
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:



271
272
273
274
275
# File 'lib/ably/realtime/presence.rb', line 271

def subscribe(*actions, &callback)
  ensure_channel_attached do
    super
  end
end

#sync_complete?Boolean

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

Returns:

  • (Boolean)


337
338
339
# File 'lib/ably/realtime/presence.rb', line 337

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:



284
285
286
# File 'lib/ably/realtime/presence.rb', line 284

def unsubscribe(*actions, &callback)
  super
end

#update(options = {}) {|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:

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

    an options Hash to specify client data

Options Hash (options):

  • :data (String)

    optional data (eg a status message) for this member

Yields:

Returns:



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/ably/realtime/presence.rb', line 198

def update(options = {}, &success_block)
  data       = options.fetch(:data, nil)
  deferrable = create_deferrable

  ensure_supported_client_id client_id
  ensure_supported_payload data unless data.nil?

  @data = data

  ensure_presence_publishable_on_connection
  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, options = {}) {|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

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

    an options Hash for this client event

Options Hash (options):

  • :data (String)

    optional data (eg a status message) for this member

Yields:

Returns:

Raises:

  • (ArgumentError)


232
233
234
235
236
237
238
# File 'lib/ably/realtime/presence.rb', line 232

def update_client(client_id, options = {}, &success_block)
  raise ArgumentError, 'options must be a Hash' unless options.kind_of?(Hash)
  ensure_supported_client_id client_id
  ensure_supported_payload options[:data] if options.has_key?(:data)

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