Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
301d73d
removed unnecessary connection serial references from the code
sacOO7 Jun 2, 2024
67dee1d
removed connection serial references from connection file
sacOO7 Jun 2, 2024
58fb431
Refactored comments related to connection serial, removed comments me…
sacOO7 Jun 5, 2024
aaa6211
removed unnecessary has_connection_serial, has_serial method and rela…
sacOO7 Jun 5, 2024
832d9c4
removed connection_serial property from protocol_message, removed all
sacOO7 Jun 5, 2024
d4b2e6c
Updated ably protocol version to 2, updated relevant tests
sacOO7 Jun 6, 2024
21279fa
Renamed VERSION constant to LIB_VERSION for readability
sacOO7 Jun 6, 2024
efb7dad
Set idempotent rest publishing to true by default, updated relevant t…
sacOO7 Jun 6, 2024
4b448ff
Added RecoveryKeyContext class and unit tests for the same
sacOO7 Jun 6, 2024
7b8af6b
Fixed typos in ruby docs for internal modules and realtime channel class
sacOO7 Jun 6, 2024
a0114b9
Added channelSerial field to channel properties, implemented following
sacOO7 Jun 6, 2024
4ba3917
Implemented create_recovery_key method on connection. Implemented
sacOO7 Jun 6, 2024
c0760d6
Fix file location: Moved recovery_key_context from test to lib
sacOO7 Jun 6, 2024
b0ad4fb
Revert "Renamed VERSION constant to LIB_VERSION for readability"
sacOO7 Jun 21, 2024
c4ff9e5
Refactored idempotent publishing test name to be enabled by default
sacOO7 Jun 21, 2024
b1789fa
removed nil check for protocol_message while setting channel serial
sacOO7 Jun 21, 2024
54e186d
channel_name accessor made explicit for better readability
sacOO7 Jun 21, 2024
15b21d7
returning nil for create_recovery_key, same as old deprecated recover…
sacOO7 Jun 21, 2024
32e68cf
Added AblyExtensions for empty/nil string check, updated usages for the
sacOO7 Jun 24, 2024
7a04abc
Updated invalid check impl on protocol_message received
sacOO7 Jun 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

_[Ably](https://ably.com) is the platform that powers synchronized digital experiences in realtime. Whether attending an event in a virtual venue, receiving realtime financial information, or monitoring live car performance data – consumers simply expect realtime digital experiences as standard. Ably provides a suite of APIs to build, extend, and deliver powerful digital experiences in realtime for more than 250 million devices across 80 countries each month. Organizations like Bloomberg, HubSpot, Verizon, and Hopin depend on Ably’s platform to offload the growing complexity of business-critical realtime data synchronization at global scale. For more information, see the [Ably documentation](https://ably.com/documentation)._

This is a Ruby client library for Ably. The library currently targets the [Ably 1.2 client library specification](https://ably.com/documentation/client-lib-development-guide/features/). You can see the complete list of features this client library supports in [our client library SDKs feature support matrix](https://ably.com/download/sdk-feature-support-matrix).
This is a Ruby client library for Ably. The library currently targets the [Ably 2.0.0 client library specification](https://ably.com/documentation/client-lib-development-guide/features/). You can see the complete list of features this client library supports in [our client library SDKs feature support matrix](https://ably.com/download/sdk-feature-support-matrix).

## Supported platforms

Expand Down
31 changes: 5 additions & 26 deletions lib/ably/models/protocol_message.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ module Ably::Models
# @return [String] Contains a serial number for a message on the current channel
# @!attribute [r] connection_id
# @return [String] Contains a string private connection key used to recover this connection
# @!attribute [r] connection_serial
# @return [Bignum] Contains a serial number for a message sent from the server to the client
# @!attribute [r] message_serial
# @return [Bignum] Contains a serial number for a message sent from the client to the server
# @!attribute [r] timestamp
Expand Down Expand Up @@ -129,12 +127,6 @@ def message_serial
raise TypeError, "msg_serial '#{attributes[:msg_serial]}' is invalid, a positive Integer is expected for a ProtocolMessage"
end

def connection_serial
Integer(attributes[:connection_serial])
rescue TypeError
raise TypeError, "connection_serial '#{attributes[:connection_serial]}' is invalid, a positive Integer is expected for a ProtocolMessage"
end

def count
[1, attributes[:count].to_i].max
end
Expand All @@ -146,26 +138,12 @@ def has_message_serial?
false
end

# @api private
def has_connection_serial?
connection_serial && true
def has_channel_serial?
channel_serial && true
rescue TypeError
false
end

def serial
if has_connection_serial?
connection_serial
else
message_serial
end
end

# @api private
def has_serial?
has_connection_serial? || has_message_serial?
end

def messages
@messages ||=
Array(attributes[:messages]).map do |message|
Expand Down Expand Up @@ -271,7 +249,7 @@ def attributes
# Return a JSON ready object from the underlying #attributes using Ably naming conventions for keys
def as_json(*args)
raise TypeError, ':action is missing, cannot generate a valid Hash for ProtocolMessage' unless action
raise TypeError, ':msg_serial or :connection_serial is missing, cannot generate a valid Hash for ProtocolMessage' if ack_required? && !has_serial?
raise TypeError, ':msg_serial is missing, cannot generate a valid Hash for ProtocolMessage' if ack_required? && !has_message_serial?

attributes.dup.tap do |hash_object|
hash_object['action'] = action.to_i
Expand All @@ -296,11 +274,12 @@ def to_s
end

# True if the ProtocolMessage appears to be invalid, however this is not a guarantee
# Used for validating incoming protocol messages, so no need to add unnecessary checks
# @return [Boolean]
# @api private
def invalid?
action_enum = action rescue nil
!action_enum || (ack_required? && !has_serial?)
!action_enum
end

# @!attribute [r] logger
Expand Down
4 changes: 2 additions & 2 deletions lib/ably/modules/safe_deferrable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def errback(&block)
end
end

# Mark the Deferrable as succeeded and trigger all callbacks.
# Mark the Deferrable as succeeded and trigger all success callbacks.
# See http://www.rubydoc.info/gems/eventmachine/1.0.7/EventMachine/Deferrable#succeed-instance_method
#
# @return [void]
Expand All @@ -48,7 +48,7 @@ def succeed(*args)
super(*args)
end

# Mark the Deferrable as failed and trigger all callbacks.
# Mark the Deferrable as failed and trigger all error callbacks.
# See http://www.rubydoc.info/gems/eventmachine/1.0.7/EventMachine/Deferrable#fail-instance_method
#
# @return [void]
Expand Down
2 changes: 1 addition & 1 deletion lib/ably/modules/state_emitter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Ably::Modules
# the instance variable @state is used exclusively, the {Enum} STATE is defined prior to inclusion of this
# module, and the class is an {EventEmitter}. It then emits state changes.
#
# It also ensures the EventEmitter is configured to retrict permitted events to the
# It also ensures the EventEmitter is configured to restrict permitted events to the
# the available STATEs or EVENTs if defined i.e. if EVENTs includes an additional type such as
# :update, then it will support all EVENTs being emitted. EVENTs must be a superset of STATEs
#
Expand Down
2 changes: 1 addition & 1 deletion lib/ably/realtime/channel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class Channel
#
# @spec RTL2b
#
# The permited states for this channel
# The permitted states for this channel
STATE = ruby_enum('STATE',
:initialized,
:attaching,
Expand Down
1 change: 1 addition & 0 deletions lib/ably/realtime/channel/channel_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ def send_attach_protocol_message
message_options[:flags] = message_options[:flags].to_i | Ably::Models::ProtocolMessage::ATTACH_FLAGS_MAPPING[:resume]
end

message_options[:channelSerial] = channel.properties.channel_serial # RTL4c1
send_state_change_protocol_message Ably::Models::ProtocolMessage::ACTION.Attach, :suspended, message_options
end

Expand Down
9 changes: 9 additions & 0 deletions lib/ably/realtime/channel/channel_properties.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ class ChannelProperties
#
attr_reader :attach_serial

# ChannelSerial contains the channelSerial from latest ProtocolMessage of action type
# Message/PresenceMessage received on the channel.
#
# @spec CP2b, RTL15b
#
# @return [String]
#
attr_accessor :channel_serial

def initialize(channel)
@channel = channel
end
Expand Down
1 change: 1 addition & 0 deletions lib/ably/realtime/channel/channel_state_machine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class ChannelStateMachine
end

after_transition(to: [:detached, :failed, :suspended]) do |channel, current_transition|
channel.properties.channel_serial = nil # RTP5a1
err = error_from_state_change(current_transition)
channel.manager.fail_queued_messages(err) if channel.failed? or channel.suspended? #RTL11
channel.manager.log_channel_error err if err
Expand Down
20 changes: 20 additions & 0 deletions lib/ably/realtime/channels.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,26 @@ def release(channel)
@channels.delete(channel)
end if @channels.has_key?(channel)
end

# Sets channel serial to each channel from given serials hashmap
# @param [Hash] serials - map of channel name to respective channel serial
# @api private
def set_channel_serials(serials)
serials.each do |channel_name, channel_serial|
get(channel_name).properties.channel_serial = channel_serial
end
end

# @return [Hash] serials - map of channel name to respective channel serial
# @api private
def get_channel_serials
channel_serials = {}
self.each do |channel|
channel_serials[channel.name] = channel.properties.channel_serial if channel.state == :attached
end
channel_serials
end

end
end
end
20 changes: 14 additions & 6 deletions lib/ably/realtime/client.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require 'uri'
require 'ably/realtime/channel/publisher'
require 'ably/realtime/recovery_key_context'

module Ably
module Realtime
Expand All @@ -11,6 +12,7 @@ class Client
include Ably::Modules::Conversions

extend Forwardable
using Ably::Util::AblyExtensions

DOMAIN = 'realtime.ably.io'

Expand Down Expand Up @@ -120,17 +122,23 @@ def initialize(options)
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(:echo_messages, true) == false ? false : true
@queue_messages = rest_client.options.fetch(:queue_messages, true) == false ? false : true
@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(:auto_connect, true) == false ? false : true
@recover = rest_client.options[:recover]

raise ArgumentError, "Recovery key '#{recover}' is invalid" if recover && !recover.match(Connection::RECOVER_REGEX)
@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

# Return a {Ably::Realtime::Channel Realtime Channel} for the given name
Expand Down
27 changes: 14 additions & 13 deletions lib/ably/realtime/client/incoming_message_dispatcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,24 @@ def dispatch_protocol_message(*args)
raise ArgumentError, "Expected a ProtocolMessage. Received #{protocol_message}"
end

unless protocol_message.action.match_any?(:nack, :error)
logger.debug { "#{protocol_message.action} received: #{protocol_message}" }
# RTL15b
if protocol_message.has_channel_serial? &&
(
protocol_message.action == :message ||
protocol_message.action == :presence ||
protocol_message.action == :attached
)
get_channel(protocol_message.channel).tap do |channel|
logger.info "Setting channel serial for channel #{channel.name}, " <<
"Previous: #{channel.properties.channel_serial}, New: #{protocol_message.channel_serial}"
channel.properties.channel_serial = protocol_message.channel_serial
end
end

if protocol_message.action.match_any?(:sync, :presence, :message)
if connection.serial && protocol_message.has_connection_serial? && protocol_message.connection_serial <= connection.serial
error_message = "Protocol error, duplicate message received for serial #{protocol_message.connection_serial}"
logger.error error_message
return
end
unless protocol_message.action.match_any?(:nack, :error)
logger.debug { "#{protocol_message.action} received: #{protocol_message}" }
end

update_connection_recovery_info protocol_message
connection.set_connection_confirmed_alive

case protocol_message.action
Expand Down Expand Up @@ -172,10 +177,6 @@ def process_connected_update_message(protocol_message)
end
end

def update_connection_recovery_info(protocol_message)
connection.update_connection_serial protocol_message.connection_serial if protocol_message.has_connection_serial?
end

def ack_pending_queue_for_message_serial(ack_protocol_message)
drop_pending_queue_from_ack(ack_protocol_message) do |protocol_message|
ack_messages protocol_message.messages
Expand Down
Loading