class Ably::Realtime::Client

A client that extends the functionality of the {Ably::Realtime::Client} and provides additional realtime-specific features.

Constants

DOMAIN

Attributes

auth[R]

An {Ably::Auth} object.

@spec RTC4

@return [Ably::Auth]

auto_connect[R]

When true, as soon as the client library is instantiated it will connect to Ably. If this attribute is false, a connection must be opened explicitly @return [Boolean]

channels[R]

A {Aby::Realtime::Channels} object.

@spec RTC3, RTS1

@return [Aby::Realtime::Channels]

connection[R]

A {Aby::Realtime::Connection} object.

@spec RTC2

@return [Aby::Realtime::Connection]

custom_realtime_host[R]

The custom realtime websocket host that is being used if it was provided with the option ‘:ws_host` when the {Client} was created @return [String,Nil]

echo_messages[R]

When false the client suppresses messages originating from this connection being echoed back on the same connection. Defaults to true @return [Boolean]

queue_messages[R]

If false, this disables the default behaviour whereby the library queues messages on a connection in the disconnected or connecting states. Defaults to true @return [Boolean]

recover[R]

When a recover option is specified a connection inherits the state of a previous connection that may have existed under a different instance of the Realtime library, please refer to the API documentation for further information on connection state recovery @return [String,Nil]

rest_client[R]

@private

transport_params[R]

Additional parameters to be sent in the querystring when initiating a realtime connection @return [Hash]

Public Class Methods

new(options) click to toggle source

Creates a {Ably::Realtime::Client Realtime Client} and configures the {Ably::Auth} object for the connection.

@spec RSC1

@param (see {Ably::Rest::Client#initialize}) @option options (see Ably::Rest::Client#initialize) An options {Hash} object. @option options [Proc] :auth_callback when provided, the Proc will be called with the token params hash as the first argument, whenever a new token is required.

Whilst the proc is called synchronously, it does not block the EventMachine reactor as it is run in a separate thread.
The Proc should return a token string, {Ably::Models::TokenDetails} or JSON equivalent, {Ably::Models::TokenRequest} or JSON equivalent

@option options [Boolean] :queue_messages If false, this disables the default behaviour whereby the library queues messages on a connection in the disconnected or connecting states @option options [Boolean] :echo_messages If false, prevents messages originating from this connection being echoed back on the same connection @option options [String] :recover When a recover option is specified a connection inherits the state of a previous connection that may have existed under a different instance of the Realtime library, please refer to the API documentation for further information on connection state recovery @option options [Boolean] :auto_connect By default as soon as the client library is instantiated it will connect to Ably. You can optionally set this to false and explicitly connect. @option options [Hash] :transport_params Additional parameters to be sent in the querystring when initiating a realtime connection. Keys are Strings, values are Stringifiable(a value must respond to to_s)

@option options [Integer] :channel_retry_timeout (15 seconds). When a channel becomes SUSPENDED, after this delay in seconds, the channel will automatically attempt to reattach if the connection is CONNECTED @option options [Integer] :disconnected_retry_timeout (15 seconds). When the connection enters the DISCONNECTED state, after this delay in seconds, if the state is still DISCONNECTED, the client library will attempt to reconnect automatically @option options [Integer] :suspended_retry_timeout (30 seconds). When the connection enters the SUSPENDED state, after this delay in seconds, if the state is still SUSPENDED, the client library will attempt to reconnect automatically @option options [Boolean] :disable_websocket_heartbeats WebSocket heartbeats are more efficient than protocol level heartbeats, however they can be disabled for development purposes

@return [Ably::Realtime::Client]

@example

# Constructs a {Ably::Realtime::Client} object using an Ably API key or token string.
client = Ably::Realtime::Client.new('key.id:secret')

# Constructs a {Ably::Realtime::Client} object using an Ably options object.
client = Ably::Realtime::Client.new(key: 'key.id:secret', client_id: 'john')
# File lib/submodules/ably-ruby/lib/ably/realtime/client.rb, line 109
def initialize(options)
  raise ArgumentError, 'Options Hash is expected' if options.nil?

  options = options.clone
  if options.kind_of?(String)
    options = if options.match(Ably::Auth::API_KEY_REGEX)
      { key: options }
    else
      { token: options }
    end
  end

  @transport_params      = options.delete(:transport_params).to_h.each_with_object({}) do |(key, value), acc|
    acc[key.to_s] = value.to_s
  end
  @rest_client           = Ably::Rest::Client.new(options.merge(realtime_client: self))
  @echo_messages         = rest_client.options.fetch_with_default(:echo_messages, true)
  @queue_messages        = rest_client.options.fetch_with_default(:queue_messages, true)
  @custom_realtime_host  = rest_client.options[:realtime_host] || rest_client.options[:ws_host]
  @auto_connect          = rest_client.options.fetch_with_default(:auto_connect, true)
  @recover               = rest_client.options.fetch_with_default(:recover, '')

  @auth       = Ably::Realtime::Auth.new(self)
  @channels   = Ably::Realtime::Channels.new(self)
  @connection = Ably::Realtime::Connection.new(self, options)

  unless @recover.nil_or_empty?
    recovery_context = RecoveryKeyContext.from_json(@recover, logger)
    unless recovery_context.nil?
      @channels.set_channel_serials recovery_context.channel_serials # RTN16j
      @connection.set_msg_serial_from_recover = recovery_context.msg_serial  # RTN16f
    end
  end
end

Public Instance Methods

channel(name, channel_options = {}) click to toggle source

Return a {Ably::Realtime::Channel Realtime Channel} for the given name

@param (see Ably::Realtime::Channels#get) @return (see Ably::Realtime::Channels#get)

# File lib/submodules/ably-ruby/lib/ably/realtime/client.rb, line 149
def channel(name, channel_options = {})
  channels.get(name, channel_options)
end
close(&block) click to toggle source

Calls {Connection#close} and causes the connection to close, entering the closing state. Once closed, the library will not attempt to re-establish the connection without an explicit call to {Connection#connect}. @spec RTN12 (see Ably::Realtime::Connection#close)

# File lib/submodules/ably-ruby/lib/ably/realtime/client.rb, line 190
def close(&block)
  connection.close(&block)
end
connect(&block) click to toggle source

Calls {Ably::Realtime::Connection#connect} and causes the connection to open, entering the connecting state. Explicitly calling connect() is unnecessary unless the autoConnect property is disabled. @spec RTN11 (see Ably::Realtime::Connection#connect)

# File lib/submodules/ably-ruby/lib/ably/realtime/client.rb, line 198
def connect(&block)
  connection.connect(&block)
end
device() click to toggle source

Retrieves a {Ably::Models::LocalDevice} object that represents the current state of the device as a target for push notifications. @spec RSH8 @return [Ably::Models::LocalDevice] A {Ably::Models::LocalDevice} object.

@note This is unsupported in the Ruby library

# File lib/submodules/ably-ruby/lib/ably/realtime/client.rb, line 339
def device
  raise Ably::Exceptions::PushNotificationsNotSupported, 'This device does not support receiving or subscribing to push notifications. The local device object is not unavailable'
end
disable_automatic_connection_recovery() click to toggle source

Disable connection recovery, typically used after a connection has been recovered @return [void] @api private

# File lib/submodules/ably-ruby/lib/ably/realtime/client.rb, line 316
def disable_automatic_connection_recovery
  @recover = nil
end
endpoint() click to toggle source

@!attribute [r] endpoint @return [URI::Generic] Default Ably Realtime endpoint used for all requests

# File lib/submodules/ably-ruby/lib/ably/realtime/client.rb, line 294
def endpoint
  endpoint_for_host(custom_realtime_host || [environment, DOMAIN].compact.join('-'))
end
fallback_endpoint() click to toggle source

@!attribute [r] fallback_endpoint @return [URI::Generic] Fallback endpoint used to connect to the realtime Ably service. Note, after each connection attempt, a new random {Ably::FALLBACK_HOSTS fallback host} or provided fallback hosts are used @api private

# File lib/submodules/ably-ruby/lib/ably/realtime/client.rb, line 323
def fallback_endpoint
  unless defined?(@fallback_endpoints) && @fallback_endpoints
    @fallback_endpoints = fallback_hosts.shuffle.map { |fallback_host| endpoint_for_host(fallback_host) }
    @fallback_endpoints << endpoint # Try the original host last if all fallbacks have been used
  end

  fallback_endpoint_index = connection.manager.retry_count_for_state(:disconnected) + connection.manager.retry_count_for_state(:suspended) - 1

  @fallback_endpoints[fallback_endpoint_index % @fallback_endpoints.count]
end
fallback_hosts() click to toggle source

(see Ably::Rest::Client#fallback_hosts)

# File lib/submodules/ably-ruby/lib/ably/realtime/client.rb, line 304
def fallback_hosts
  rest_client.fallback_hosts
end
logger() click to toggle source

(see Ably::Rest::Client#logger)

# File lib/submodules/ably-ruby/lib/ably/realtime/client.rb, line 309
def logger
  @logger ||= Ably::Logger.new(self, log_level, rest_client.logger.custom_logger)
end
publish(channel_name, name, data = nil, attributes = {}, &success_block) click to toggle source

Publish one or more messages to the specified channel.

This method allows messages to be efficiently published to Ably without instancing a {Ably::Realtime::Channel} object. If you want to publish a high rate of messages to Ably without instancing channels or using the REST API, then this method is recommended. However, channel options such as encryption are not supported with this method. If you need to specify channel options we recommend you use the {Ably::Realtime::Channel} publish method without attaching to each channel, unless you also want to subscribe to published messages on that channel.

Note: This feature is still in beta. As such, we cannot guarantee the API will not change in future.

@param channel [String] The channel name you want to publish the message(s) to @param name [String, Array<Ably::Models::Message|Hash>, nil] The event name of the message to publish, or an Array of [Ably::Model::Message] objects or [Hash] objects with :name and :data pairs @param data [String, ByteArray, nil] The message payload unless an Array of [Ably::Model::Message] objects passed in the first argument @param attributes [Hash, nil] Optional additional message attributes such as :client_id or :connection_id, applied when name attribute is nil or a string

@yield [Ably::Models::Message,Array<Ably::Models::Message>] On success, will call the block with the {Ably::Models::Message} if a single message is published, or an Array of {Ably::Models::Message} when multiple messages are published @return [Ably::Util::SafeDeferrable] Deferrable that supports both success (callback) and failure (errback) callbacks

@example

# Publish a single message
client.publish 'activityChannel', click', { x: 1, y: 2 }

# Publish an array of message Hashes
messages = [
  { name: 'click', { x: 1, y: 2 } },
  { name: 'click', { x: 2, y: 3 } }
]
client.publish 'activityChannel', messages

# Publish an array of Ably::Models::Message objects
messages = [
  Ably::Models::Message(name: 'click', { x: 1, y: 2 })
  Ably::Models::Message(name: 'click', { x: 2, y: 3 })
]
client.publish 'activityChannel', messages

client.publish('activityChannel', 'click', 'body') do |message|
  puts "#{message.name} event received with #{message.data}"
end

client.publish('activityChannel', 'click', 'body').errback do |error, message|
  puts "#{message.name} was not received, error #{error.message}"
end
# File lib/submodules/ably-ruby/lib/ably/realtime/client.rb, line 268
def publish(channel_name, name, data = nil, attributes = {}, &success_block)
  if !connection.can_publish_messages?
    error = Ably::Exceptions::MessageQueueingDisabled.new("Message cannot be published. Client is not allowed to queue messages when connection is in state #{connection.state}")
    return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
  end

  messages = if name.kind_of?(Enumerable)
    name
  else
    name = ensure_utf_8(:name, name, allow_nil: true)
    ensure_supported_payload data
    [{ name: name, data: data }.merge(attributes)]
  end

  if messages.length > Realtime::Connection::MAX_PROTOCOL_MESSAGE_BATCH_SIZE
    error = Ably::Exceptions::InvalidRequest.new("It is not possible to publish more than #{Realtime::Connection::MAX_PROTOCOL_MESSAGE_BATCH_SIZE} messages with a single publish request.")
    return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
  end

  enqueue_messages_on_connection(self, messages, channel_name).tap do |deferrable|
    deferrable.callback(&success_block) if block_given?
  end
end
push() click to toggle source

A {Ably::Realtime::Push} object. @return [Ably::Realtime::Push]

# File lib/submodules/ably-ruby/lib/ably/realtime/client.rb, line 204
def push
  @push ||= Push.new(self)
end
register_encoder(encoder) click to toggle source

(see Ably::Rest::Client#register_encoder)

# File lib/submodules/ably-ruby/lib/ably/realtime/client.rb, line 299
def register_encoder(encoder)
  rest_client.register_encoder encoder
end
request(method, path, params = {}, body = nil, headers = {}, &callback) click to toggle source

Makes a REST request to a provided path. This is provided as a convenience for developers who wish to use REST API functionality that is either not documented or is not yet included in the public API, without having to directly handle features such as authentication, paging, fallback hosts, MsgPack and JSON support.

@spec RTC9

(see {Ably::Rest::Client#request}) @yield [Ably::Models::HttpPaginatedResponse<>] An Array of Stats

@return [Ably::Util::SafeDeferrable] An {Ably::Util::SafeDeferrable} response object returned by the HTTP request, containing an empty or JSON-encodable object.

# File lib/submodules/ably-ruby/lib/ably/realtime/client.rb, line 218
def request(method, path, params = {}, body = nil, headers = {}, &callback)
  async_wrap(callback) do
    rest_client.request(method, path, params, body, headers, async_blocking_operations: true)
  end
end
stats(options = {}, &success_callback) click to toggle source

Queries the REST /stats API and retrieves your application’s usage statistics. Returns a {Ably::Util::SafeDeferrable} object, containing an array of {Ably::Models::Stats} objects. See the Stats docs.

@spec RTC5

@param (see Ably::Rest::Client#stats) @option options (see Ably::Rest::Client#stats)

@yield [Ably::Models::PaginatedResult<Ably::Models::Stats>] A {Ably::Util::SafeDeferrable} object containing an array of {Ably::Models::Stats} objects.

@return [Ably::Util::SafeDeferrable]

# File lib/submodules/ably-ruby/lib/ably/realtime/client.rb, line 180
def stats(options = {}, &success_callback)
  async_wrap(success_callback) do
    rest_client.stats(options)
  end
end
time(&success_callback) click to toggle source

Retrieves the time from the Ably service as milliseconds since the Unix epoch. Clients that do not have access to a sufficiently well maintained time source and wish to issue Ably {Ably::Models::TokenRequests} with a more accurate timestamp should use the queryTime property instead of this method.

@spec RTC6a

@yield [Time] The time as milliseconds since the Unix epoch. @return [Ably::Util::SafeDeferrable]

# File lib/submodules/ably-ruby/lib/ably/realtime/client.rb, line 162
def time(&success_callback)
  async_wrap(success_callback) do
    rest_client.time
  end
end

Private Instance Methods

endpoint_for_host(host) click to toggle source
# File lib/submodules/ably-ruby/lib/ably/realtime/client.rb, line 344
def endpoint_for_host(host)
  port = if use_tls?
    custom_tls_port
  else
    custom_port
  end

  raise ArgumentError, "Custom port must be an Integer or nil" if port && !port.kind_of?(Integer)

  options = {
    scheme: use_tls? ? 'wss' : 'ws',
    host:   host
  }
  options.merge!(port: port) if port

  URI::Generic.build(options)
end