ably-pubsub-core 0.0.1.pre → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/lib/ably/auth.rb +5 -5
  3. data/lib/ably/exceptions.rb +5 -0
  4. data/lib/ably/internal.rb +115 -0
  5. data/lib/ably/logger.rb +3 -3
  6. data/lib/ably/models/channel_details.rb +1 -1
  7. data/lib/ably/models/channel_metrics.rb +1 -1
  8. data/lib/ably/models/channel_occupancy.rb +1 -1
  9. data/lib/ably/models/channel_state_change.rb +7 -7
  10. data/lib/ably/models/channel_status.rb +1 -1
  11. data/lib/ably/models/cipher_params.rb +1 -1
  12. data/lib/ably/models/connection_details.rb +2 -2
  13. data/lib/ably/models/connection_state_change.rb +6 -6
  14. data/lib/ably/models/http_paginated_response.rb +3 -3
  15. data/lib/ably/models/message.rb +69 -2
  16. data/lib/ably/models/message_operation.rb +53 -0
  17. data/lib/ably/models/paginated_result.rb +2 -2
  18. data/lib/ably/models/presence_message.rb +1 -1
  19. data/lib/ably/models/protocol_message.rb +14 -1
  20. data/lib/ably/models/publish_result.rb +32 -0
  21. data/lib/ably/models/stats.rb +41 -84
  22. data/lib/ably/models/update_delete_result.rb +32 -0
  23. data/lib/ably/modules/ably.rb +13 -12
  24. data/lib/ably/modules/channels_collection.rb +1 -1
  25. data/lib/ably/modules/http_helpers.rb +11 -11
  26. data/lib/ably/{rest → pubsub/http}/channel/push_channel.rb +3 -3
  27. data/lib/ably/pubsub/http/channel.rb +284 -0
  28. data/lib/ably/pubsub/http/channels.rb +49 -0
  29. data/lib/ably/pubsub/http/client.rb +738 -0
  30. data/lib/ably/pubsub/http/middleware/encoder.rb +51 -0
  31. data/lib/ably/pubsub/http/middleware/exceptions.rb +55 -0
  32. data/lib/ably/pubsub/http/middleware/external_exceptions.rb +26 -0
  33. data/lib/ably/pubsub/http/middleware/fail_if_unsupported_mime_type.rb +24 -0
  34. data/lib/ably/pubsub/http/middleware/logger.rb +60 -0
  35. data/lib/ably/pubsub/http/middleware/parse_json.rb +29 -0
  36. data/lib/ably/pubsub/http/middleware/parse_message_pack.rb +45 -0
  37. data/lib/ably/pubsub/http/presence.rb +117 -0
  38. data/lib/ably/{rest → pubsub/http}/push/admin.rb +5 -5
  39. data/lib/ably/{rest → pubsub/http}/push/channel_subscriptions.rb +1 -1
  40. data/lib/ably/{rest → pubsub/http}/push/device_registrations.rb +1 -1
  41. data/lib/ably/pubsub/http/push.rb +27 -0
  42. data/lib/ably/pubsub/http.rb +38 -0
  43. data/lib/ably/pubsub/realtime/auth.rb +285 -0
  44. data/lib/ably/{realtime → pubsub/realtime}/channel/channel_manager.rb +2 -2
  45. data/lib/ably/{realtime → pubsub/realtime}/channel/channel_properties.rb +5 -5
  46. data/lib/ably/{realtime → pubsub/realtime}/channel/channel_state_machine.rb +2 -2
  47. data/lib/ably/{realtime → pubsub/realtime}/channel/publisher.rb +9 -4
  48. data/lib/ably/{realtime → pubsub/realtime}/channel/push_channel.rb +2 -2
  49. data/lib/ably/pubsub/realtime/channel.rb +545 -0
  50. data/lib/ably/pubsub/realtime/channels.rb +92 -0
  51. data/lib/ably/{realtime → pubsub/realtime}/client/incoming_message_dispatcher.rb +43 -8
  52. data/lib/ably/{realtime → pubsub/realtime}/client/outgoing_message_dispatcher.rb +2 -2
  53. data/lib/ably/pubsub/realtime/client.rb +372 -0
  54. data/lib/ably/{realtime → pubsub/realtime}/connection/connection_manager.rb +6 -6
  55. data/lib/ably/{realtime → pubsub/realtime}/connection/connection_state_machine.rb +2 -2
  56. data/lib/ably/{realtime → pubsub/realtime}/connection/websocket_transport.rb +1 -1
  57. data/lib/ably/pubsub/realtime/connection.rb +724 -0
  58. data/lib/ably/{realtime → pubsub/realtime}/models/nil_channel.rb +3 -3
  59. data/lib/ably/{realtime → pubsub/realtime}/presence/members_map.rb +2 -2
  60. data/lib/ably/{realtime → pubsub/realtime}/presence/presence_manager.rb +3 -3
  61. data/lib/ably/{realtime → pubsub/realtime}/presence/presence_state_machine.rb +2 -2
  62. data/lib/ably/{realtime → pubsub/realtime}/presence.rb +23 -23
  63. data/lib/ably/{realtime → pubsub/realtime}/push/admin.rb +8 -8
  64. data/lib/ably/{realtime → pubsub/realtime}/push/channel_subscriptions.rb +6 -6
  65. data/lib/ably/{realtime → pubsub/realtime}/push/device_registrations.rb +6 -6
  66. data/lib/ably/pubsub/realtime/push.rb +27 -0
  67. data/lib/ably/pubsub/realtime/recovery_key_context.rb +38 -0
  68. data/lib/ably/pubsub/realtime.rb +58 -0
  69. data/lib/ably/pubsub.rb +11 -0
  70. data/lib/ably/version.rb +2 -2
  71. data/lib/ably.rb +6 -2
  72. metadata +50 -46
  73. data/lib/ably/models/stats_types.rb +0 -107
  74. data/lib/ably/realtime/auth.rb +0 -283
  75. data/lib/ably/realtime/channel.rb +0 -440
  76. data/lib/ably/realtime/channels.rb +0 -71
  77. data/lib/ably/realtime/client.rb +0 -363
  78. data/lib/ably/realtime/connection.rb +0 -738
  79. data/lib/ably/realtime/push.rb +0 -25
  80. data/lib/ably/realtime/recovery_key_context.rb +0 -36
  81. data/lib/ably/realtime.rb +0 -60
  82. data/lib/ably/rest/channel.rb +0 -194
  83. data/lib/ably/rest/channels.rb +0 -47
  84. data/lib/ably/rest/client.rb +0 -727
  85. data/lib/ably/rest/middleware/encoder.rb +0 -49
  86. data/lib/ably/rest/middleware/exceptions.rb +0 -53
  87. data/lib/ably/rest/middleware/external_exceptions.rb +0 -24
  88. data/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +0 -22
  89. data/lib/ably/rest/middleware/logger.rb +0 -58
  90. data/lib/ably/rest/middleware/parse_json.rb +0 -27
  91. data/lib/ably/rest/middleware/parse_message_pack.rb +0 -43
  92. data/lib/ably/rest/presence.rb +0 -113
  93. data/lib/ably/rest/push.rb +0 -25
  94. data/lib/ably/rest.rb +0 -40
@@ -1,7 +1,7 @@
1
- module Ably::Realtime
1
+ module Ably::PubSub::Realtime
2
2
  class Client
3
3
  # IncomingMessageDispatcher is a (private) class that is used to dispatch {Ably::Models::ProtocolMessage} that are
4
- # received from Ably via the {Ably::Realtime::Connection}
4
+ # received from Ably via the {Ably::PubSub::Realtime::Connection}
5
5
  class IncomingMessageDispatcher
6
6
  ACTION = Ably::Models::ProtocolMessage::ACTION
7
7
 
@@ -28,7 +28,7 @@ module Ably::Realtime
28
28
  def get_channel(channel_name)
29
29
  channels.fetch(channel_name) do
30
30
  logger.warn { "Received channel message for non-existent channel" }
31
- Ably::Realtime::Models::NilChannel.new
31
+ Ably::PubSub::Realtime::Models::NilChannel.new
32
32
  end
33
33
  end
34
34
 
@@ -108,7 +108,7 @@ module Ably::Realtime
108
108
  channel.manager.duplicate_attached_received protocol_message
109
109
  else
110
110
  if channel.failed?
111
- logger.warn "Ably::Realtime::Client::IncomingMessageDispatcher - Received an ATTACHED protocol message for FAILED channel #{channel.name}. Ignoring ATTACHED message"
111
+ logger.warn "Ably::PubSub::Realtime::Client::IncomingMessageDispatcher - Received an ATTACHED protocol message for FAILED channel #{channel.name}. Ignoring ATTACHED message"
112
112
  else
113
113
  channel.transition_state_machine :attached, reason: protocol_message.error, resumed: protocol_message.has_channel_resumed_flag?, protocol_message: protocol_message
114
114
  end
@@ -178,9 +178,12 @@ module Ably::Realtime
178
178
  end
179
179
 
180
180
  def ack_pending_queue_for_message_serial(ack_protocol_message)
181
+ res_index = 0
181
182
  drop_pending_queue_from_ack(ack_protocol_message) do |protocol_message|
182
- ack_messages protocol_message.messages
183
+ publish_result = ack_protocol_message.res[res_index] if ack_protocol_message.res
184
+ ack_messages protocol_message.messages, publish_result
183
185
  ack_messages protocol_message.presence
186
+ res_index += 1
184
187
  end
185
188
  end
186
189
 
@@ -191,11 +194,43 @@ module Ably::Realtime
191
194
  end
192
195
  end
193
196
 
194
- def ack_messages(messages)
195
- messages.each do |message|
197
+ def ack_messages(messages, publish_result = nil)
198
+ messages.each_with_index do |message, index|
196
199
  logger.debug { "Calling ACK success callbacks for #{message.class.name} - #{message.to_json}" }
197
- message.succeed message
200
+ if publish_result && message.respond_to?(:action) && message.action &&
201
+ message.action.match_any?(
202
+ Ably::Models::Message::ACTION.MessageUpdate,
203
+ Ably::Models::Message::ACTION.MessageDelete,
204
+ Ably::Models::Message::ACTION.MessageAppend
205
+ )
206
+ serials = extract_serials(publish_result)
207
+ version_serial = serials[index] if serials
208
+ result = Ably::Models::UpdateDeleteResult.new(version_serial: version_serial)
209
+ message.succeed result
210
+ elsif publish_result && message.is_a?(Ably::Models::Message)
211
+ serials = extract_serials(publish_result)
212
+ serial = serials ? serials[index] : nil
213
+ result = Ably::Models::PublishResult.new(serials: [serial])
214
+ message.succeed result
215
+ else
216
+ message.succeed message
217
+ end
218
+ end
219
+ end
220
+
221
+ def extract_serials(publish_result)
222
+ return nil unless publish_result
223
+
224
+ if publish_result.is_a?(Hash)
225
+ publish_result['serials'] || publish_result[:serials]
226
+ elsif publish_result.respond_to?(:[])
227
+ publish_result[:serials]
228
+ else
229
+ nil
198
230
  end
231
+ rescue StandardError => e
232
+ logger.warn { "Failed to extract serials from publish_result (#{publish_result.class}): #{e.message}" }
233
+ nil
199
234
  end
200
235
 
201
236
  def nack_messages(messages, protocol_message)
@@ -1,7 +1,7 @@
1
- module Ably::Realtime
1
+ module Ably::PubSub::Realtime
2
2
  class Client
3
3
  # OutgoingMessageDispatcher is a (private) class that is used to deliver
4
- # outgoing {Ably::Models::ProtocolMessage}s using the {Ably::Realtime::Connection}
4
+ # outgoing {Ably::Models::ProtocolMessage}s using the {Ably::PubSub::Realtime::Connection}
5
5
  # when the connection state is capable of delivering messages
6
6
  class OutgoingMessageDispatcher
7
7
  include Ably::Modules::EventMachineHelpers
@@ -0,0 +1,372 @@
1
+ require 'uri'
2
+ require 'ably/pubsub/realtime/channel/publisher'
3
+ require 'ably/pubsub/realtime/recovery_key_context'
4
+
5
+ module Ably
6
+ module PubSub
7
+ module Realtime
8
+ # A client that extends the functionality of the {Ably::PubSub::Realtime::Client} and provides additional realtime-specific features.
9
+ #
10
+ class Client
11
+ include Ably::Modules::AsyncWrapper
12
+ include Ably::PubSub::Realtime::Channel::Publisher
13
+ include Ably::Modules::Conversions
14
+
15
+ extend Forwardable
16
+ using Ably::Util::AblyExtensions
17
+
18
+ # A {Aby::Realtime::Channels} object.
19
+ #
20
+ # @spec RTC3, RTS1
21
+ #
22
+ # @return [Aby::Realtime::Channels]
23
+ #
24
+ attr_reader :channels
25
+
26
+ # An {Ably::Auth} object.
27
+ #
28
+ # @spec RTC4
29
+ #
30
+ # @return [Ably::Auth]
31
+ #
32
+ attr_reader :auth
33
+
34
+ # A {Aby::Realtime::Connection} object.
35
+ #
36
+ # @spec RTC2
37
+ #
38
+ # @return [Aby::Realtime::Connection]
39
+ #
40
+ attr_reader :connection
41
+
42
+ # The {Ably::PubSub::Http::Client REST client} instantiated with the same credentials and configuration that is used for all REST operations such as authentication
43
+ # @return [Ably::PubSub::Http::Client]
44
+
45
+ # @private
46
+ attr_reader :rest_client
47
+
48
+ # When false the client suppresses messages originating from this connection being echoed back on the same connection. Defaults to true
49
+ # @return [Boolean]
50
+ attr_reader :echo_messages
51
+
52
+ # If false, this disables the default behaviour whereby the library queues messages on a connection in the disconnected or connecting states. Defaults to true
53
+ # @return [Boolean]
54
+ attr_reader :queue_messages
55
+
56
+ # 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
57
+ # @return [Boolean]
58
+ attr_reader :auto_connect
59
+
60
+ # 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
61
+ # @return [String,Nil]
62
+ attr_reader :recover
63
+
64
+ # Additional parameters to be sent in the querystring when initiating a realtime connection
65
+ # @return [Hash]
66
+ attr_reader :transport_params
67
+
68
+ def_delegators :auth, :client_id, :auth_options
69
+ def_delegators :@rest_client, :encoders
70
+ def_delegators :@rest_client, :use_tls?, :protocol, :protocol_binary?
71
+ def_delegators :@rest_client, :endpoint, :primary_domain, :custom_port, :custom_tls_port
72
+ def_delegators :@rest_client, :log_level
73
+ def_delegators :@rest_client, :options
74
+
75
+ # Refuses direct construction: the package a client is created from is what declares
76
+ # the client's side to the platform, and a directly constructed client declares none.
77
+ # Use {Ably::PubSub::Server.create_realtime_client} from the +ably-pubsub-server+ gem.
78
+ #
79
+ # @raise [Ably::Exceptions::DirectConstructionNotSupported] always
80
+ def self.new(*args, **kwargs, &block)
81
+ raise Ably::Internal.direct_construction_error(
82
+ 'Ably::PubSub::Realtime::Client.new', 'Ably::PubSub::Server.create_realtime_client(options)'
83
+ )
84
+ end
85
+
86
+ # Creates a {Ably::PubSub::Realtime::Client Realtime Client} and configures the {Ably::Auth} object for the connection.
87
+ #
88
+ # @spec RSC1
89
+ #
90
+ # @param (see {Ably::PubSub::Http::Client#initialize})
91
+ # @option options (see Ably::PubSub::Http::Client#initialize) An options {Hash} object.
92
+ # @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.
93
+ # Whilst the proc is called synchronously, it does not block the EventMachine reactor as it is run in a separate thread.
94
+ # The Proc should return a token string, {Ably::Models::TokenDetails} or JSON equivalent, {Ably::Models::TokenRequest} or JSON equivalent
95
+ # @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
96
+ # @option options [Boolean] :echo_messages If false, prevents messages originating from this connection being echoed back on the same connection
97
+ # @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
98
+ # @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.
99
+ # @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)
100
+ #
101
+ # @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
102
+ # @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
103
+ # @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
104
+ # @option options [Boolean] :disable_websocket_heartbeats WebSocket heartbeats are more efficient than protocol level heartbeats, however they can be disabled for development purposes
105
+ #
106
+ # @return [Ably::PubSub::Realtime::Client]
107
+ #
108
+ # @example
109
+ # # Constructs a client using an Ably API key or token string.
110
+ # client = Ably::PubSub::Server.create_realtime_client('key.id:secret')
111
+ #
112
+ # # Constructs a client using an Ably options object.
113
+ # client = Ably::PubSub::Server.create_realtime_client(key: 'key.id:secret', client_id: 'john')
114
+ #
115
+ # Applications do not call this constructor: {.new} refuses direct construction, and
116
+ # a per-side package reaches it through {Ably::Internal.create_realtime_client}.
117
+ #
118
+ def initialize(options)
119
+ raise ArgumentError, 'Options Hash is expected' if options.nil?
120
+
121
+ options = options.clone
122
+ if options.kind_of?(String)
123
+ options = if options.match(Ably::Auth::API_KEY_REGEX)
124
+ { key: options }
125
+ else
126
+ { token: options }
127
+ end
128
+ end
129
+
130
+ @transport_params = options.delete(:transport_params).to_h.each_with_object({}) do |(key, value), acc|
131
+ acc[key.to_s] = value.to_s
132
+ end
133
+ @rest_client = Ably::Internal.create_http_client(options.merge(realtime_client: self))
134
+ @echo_messages = rest_client.options.fetch_with_default(:echo_messages, true)
135
+ @queue_messages = rest_client.options.fetch_with_default(:queue_messages, true)
136
+ @auto_connect = rest_client.options.fetch_with_default(:auto_connect, true)
137
+ @recover = rest_client.options.fetch_with_default(:recover, '')
138
+
139
+ @auth = Ably::PubSub::Realtime::Auth.new(self)
140
+ @channels = Ably::PubSub::Realtime::Channels.new(self)
141
+ @connection = Ably::PubSub::Realtime::Connection.new(self, options)
142
+
143
+ unless @recover.nil_or_empty?
144
+ recovery_context = RecoveryKeyContext.from_json(@recover, logger)
145
+ unless recovery_context.nil?
146
+ @channels.set_channel_serials recovery_context.channel_serials # RTN16j
147
+ @connection.set_msg_serial_from_recover = recovery_context.msg_serial # RTN16f
148
+ end
149
+ end
150
+ end
151
+
152
+ # Return a {Ably::PubSub::Realtime::Channel Realtime Channel} for the given name
153
+ #
154
+ # @param (see Ably::PubSub::Realtime::Channels#get)
155
+ # @return (see Ably::PubSub::Realtime::Channels#get)
156
+ #
157
+ def channel(name, channel_options = {})
158
+ channels.get(name, channel_options)
159
+ end
160
+
161
+ # Retrieves the time from the Ably service as milliseconds since the Unix epoch. Clients that do not have access
162
+ # to a sufficiently well maintained time source and wish to issue Ably {Ably::Models::TokenRequests} with
163
+ # a more accurate timestamp should use the queryTime property instead of this method.
164
+ #
165
+ # @spec RTC6a
166
+ #
167
+ # @yield [Time] The time as milliseconds since the Unix epoch.
168
+ # @return [Ably::Util::SafeDeferrable]
169
+ #
170
+ def time(&success_callback)
171
+ async_wrap(success_callback) do
172
+ rest_client.time
173
+ end
174
+ end
175
+
176
+ # Queries the REST /stats API and retrieves your application's usage statistics.
177
+ # Returns a {Ably::Util::SafeDeferrable} object, containing an array of {Ably::Models::Stats} objects. See the Stats docs.
178
+ #
179
+ # @spec RTC5
180
+ #
181
+ # @param (see Ably::PubSub::Http::Client#stats)
182
+ # @option options (see Ably::PubSub::Http::Client#stats)
183
+ #
184
+ # @yield [Ably::Models::PaginatedResult<Ably::Models::Stats>] A {Ably::Util::SafeDeferrable} object containing an array of {Ably::Models::Stats} objects.
185
+ #
186
+ # @return [Ably::Util::SafeDeferrable]
187
+ #
188
+ def stats(options = {}, &success_callback)
189
+ async_wrap(success_callback) do
190
+ rest_client.stats(options)
191
+ end
192
+ end
193
+
194
+ # Calls {Connection#close} and causes the connection to close, entering the closing state.
195
+ # Once closed, the library will not attempt to re-establish the connection without an explicit call to {Connection#connect}.
196
+ # @spec RTN12
197
+ # (see Ably::PubSub::Realtime::Connection#close)
198
+ def close(&block)
199
+ connection.close(&block)
200
+ end
201
+
202
+ # Calls {Ably::PubSub::Realtime::Connection#connect} and causes the connection to open, entering the connecting
203
+ # state. Explicitly calling connect() is unnecessary unless the autoConnect property is disabled.
204
+ # @spec RTN11
205
+ # (see Ably::PubSub::Realtime::Connection#connect)
206
+ def connect(&block)
207
+ connection.connect(&block)
208
+ end
209
+
210
+ # A {Ably::PubSub::Realtime::Push} object.
211
+ # @return [Ably::PubSub::Realtime::Push]
212
+ def push
213
+ @push ||= Push.new(self)
214
+ end
215
+
216
+ # Makes a REST request to a provided path. This is provided as a convenience for developers who wish to use REST
217
+ # API functionality that is either not documented or is not yet included in the public API, without having to
218
+ # directly handle features such as authentication, paging, fallback hosts, MsgPack and JSON support.
219
+ #
220
+ # @spec RTC9
221
+ #
222
+ # (see {Ably::PubSub::Http::Client#request})
223
+ # @yield [Ably::Models::HttpPaginatedResponse<>] An Array of Stats
224
+ #
225
+ # @return [Ably::Util::SafeDeferrable] An {Ably::Util::SafeDeferrable} response object returned by the HTTP request, containing an empty or JSON-encodable object.
226
+ def request(method, path, params = {}, body = nil, headers = {}, &callback)
227
+ async_wrap(callback) do
228
+ rest_client.request(method, path, params, body, headers, async_blocking_operations: true)
229
+ end
230
+ end
231
+
232
+ # Publish one or more messages to the specified channel.
233
+ #
234
+ # This method allows messages to be efficiently published to Ably without instancing a {Ably::PubSub::Realtime::Channel} object.
235
+ # If you want to publish a high rate of messages to Ably without instancing channels or using the REST API, then this method
236
+ # is recommended. However, channel options such as encryption are not supported with this method. If you need to specify channel options
237
+ # we recommend you use the {Ably::PubSub::Realtime::Channel} +publish+ method without attaching to each channel, unless you also want to subscribe
238
+ # to published messages on that channel.
239
+ #
240
+ # Note: This feature is still in beta. As such, we cannot guarantee the API will not change in future.
241
+ #
242
+ # @param channel [String] The channel name you want to publish the message(s) to
243
+ # @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
244
+ # @param data [String, ByteArray, nil] The message payload unless an Array of [Ably::Model::Message] objects passed in the first argument
245
+ # @param attributes [Hash, nil] Optional additional message attributes such as :client_id or :connection_id, applied when name attribute is nil or a string
246
+ #
247
+ # @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
248
+ # @return [Ably::Util::SafeDeferrable] Deferrable that supports both success (callback) and failure (errback) callbacks
249
+ #
250
+ # @example
251
+ # # Publish a single message
252
+ # client.publish 'activityChannel', click', { x: 1, y: 2 }
253
+ #
254
+ # # Publish an array of message Hashes
255
+ # messages = [
256
+ # { name: 'click', { x: 1, y: 2 } },
257
+ # { name: 'click', { x: 2, y: 3 } }
258
+ # ]
259
+ # client.publish 'activityChannel', messages
260
+ #
261
+ # # Publish an array of Ably::Models::Message objects
262
+ # messages = [
263
+ # Ably::Models::Message(name: 'click', { x: 1, y: 2 })
264
+ # Ably::Models::Message(name: 'click', { x: 2, y: 3 })
265
+ # ]
266
+ # client.publish 'activityChannel', messages
267
+ #
268
+ # client.publish('activityChannel', 'click', 'body') do |message|
269
+ # puts "#{message.name} event received with #{message.data}"
270
+ # end
271
+ #
272
+ # client.publish('activityChannel', 'click', 'body').errback do |error, message|
273
+ # puts "#{message.name} was not received, error #{error.message}"
274
+ # end
275
+ #
276
+ def publish(channel_name, name, data = nil, attributes = {}, &success_block)
277
+ if !connection.can_publish_messages?
278
+ error = Ably::Exceptions::MessageQueueingDisabled.new("Message cannot be published. Client is not allowed to queue messages when connection is in state #{connection.state}")
279
+ return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
280
+ end
281
+
282
+ messages = if name.kind_of?(Enumerable)
283
+ name
284
+ else
285
+ name = ensure_utf_8(:name, name, allow_nil: true)
286
+ ensure_supported_payload data
287
+ [{ name: name, data: data }.merge(attributes)]
288
+ end
289
+
290
+ if messages.length > Realtime::Connection::MAX_PROTOCOL_MESSAGE_BATCH_SIZE
291
+ 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.")
292
+ return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
293
+ end
294
+
295
+ enqueue_messages_on_connection(self, messages, channel_name).tap do |deferrable|
296
+ deferrable.callback(&success_block) if block_given?
297
+ end
298
+ end
299
+
300
+ # @!attribute [r] uri
301
+ # @return [URI::Generic] Ably WebSocket URI for the {#primary_domain}, shared with REST requests (RTN2)
302
+ def uri
303
+ uri_for_host(primary_domain)
304
+ end
305
+
306
+ # (see Ably::PubSub::Http::Client#register_encoder)
307
+ def register_encoder(encoder)
308
+ rest_client.register_encoder encoder
309
+ end
310
+
311
+ # (see Ably::PubSub::Http::Client#fallback_hosts)
312
+ def fallback_hosts
313
+ rest_client.fallback_hosts
314
+ end
315
+
316
+ # (see Ably::PubSub::Http::Client#logger)
317
+ def logger
318
+ @logger ||= Ably::Logger.new(self, log_level, rest_client.logger.custom_logger)
319
+ end
320
+
321
+ # Disable connection recovery, typically used after a connection has been recovered
322
+ # @return [void]
323
+ # @api private
324
+ def disable_automatic_connection_recovery
325
+ @recover = nil
326
+ end
327
+
328
+ # @!attribute [r] fallback_endpoint
329
+ # @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
330
+ # @api private
331
+ def fallback_endpoint
332
+ unless defined?(@fallback_endpoints) && @fallback_endpoints
333
+ @fallback_endpoints = fallback_hosts.shuffle.map { |fallback_host| uri_for_host(fallback_host) }
334
+ @fallback_endpoints << uri # Try the original host last if all fallbacks have been used
335
+ end
336
+
337
+ fallback_endpoint_index = connection.manager.retry_count_for_state(:disconnected) + connection.manager.retry_count_for_state(:suspended) - 1
338
+
339
+ @fallback_endpoints[fallback_endpoint_index % @fallback_endpoints.count]
340
+ end
341
+
342
+ # Retrieves a {Ably::Models::LocalDevice} object that represents the current state of the device as a target for push notifications.
343
+ # @spec RSH8
344
+ # @return [Ably::Models::LocalDevice] A {Ably::Models::LocalDevice} object.
345
+ #
346
+ # @note This is unsupported in the Ruby library
347
+ def device
348
+ raise Ably::Exceptions::PushNotificationsNotSupported, 'This device does not support receiving or subscribing to push notifications. The local device object is not unavailable'
349
+ end
350
+
351
+ private
352
+ def uri_for_host(host)
353
+ port = if use_tls?
354
+ custom_tls_port
355
+ else
356
+ custom_port
357
+ end
358
+
359
+ raise ArgumentError, "Custom port must be an Integer or nil" if port && !port.kind_of?(Integer)
360
+
361
+ options = {
362
+ scheme: use_tls? ? 'wss' : 'ws',
363
+ host: host
364
+ }
365
+ options.merge!(port: port) if port
366
+
367
+ URI::Generic.build(options)
368
+ end
369
+ end
370
+ end
371
+ end
372
+ end
@@ -1,6 +1,6 @@
1
- require 'ably/rest/middleware/exceptions'
1
+ require 'ably/pubsub/http/middleware/exceptions'
2
2
 
3
- module Ably::Realtime
3
+ module Ably::PubSub::Realtime
4
4
  class Connection
5
5
  # ConnectionManager is responsible for all actions relating to underlying connection and transports,
6
6
  # such as opening, closing, attempting reconnects etc.
@@ -35,9 +35,9 @@ module Ably::Realtime
35
35
  end
36
36
  end
37
37
 
38
- # Creates and sets up a new {Ably::Realtime::Connection::WebsocketTransport} available on attribute #transport
38
+ # Creates and sets up a new {Ably::PubSub::Realtime::Connection::WebsocketTransport} available on attribute #transport
39
39
  #
40
- # @yield [Ably::Realtime::Connection::WebsocketTransport] block is called with new websocket transport
40
+ # @yield [Ably::PubSub::Realtime::Connection::WebsocketTransport] block is called with new websocket transport
41
41
  # @api private
42
42
  def setup_transport
43
43
  if transport && !transport.ready_for_release?
@@ -151,7 +151,7 @@ module Ably::Realtime
151
151
  state_change = Ably::Models::ConnectionStateChange.new(
152
152
  current: connection.state,
153
153
  previous: connection.state,
154
- event: Ably::Realtime::Connection::EVENT(:update),
154
+ event: Ably::PubSub::Realtime::Connection::EVENT(:update),
155
155
  reason: protocol_message.error,
156
156
  protocol_message: protocol_message
157
157
  )
@@ -175,7 +175,7 @@ module Ably::Realtime
175
175
  setup_transport
176
176
  end
177
177
 
178
- # Reconnect the {Ably::Realtime::Connection::WebsocketTransport} if possible, otherwise set up a new transport
178
+ # Reconnect the {Ably::PubSub::Realtime::Connection::WebsocketTransport} if possible, otherwise set up a new transport
179
179
  #
180
180
  # @api private
181
181
  def reconnect_transport
@@ -1,8 +1,8 @@
1
1
  require 'ably/modules/state_machine'
2
2
 
3
- module Ably::Realtime
3
+ module Ably::PubSub::Realtime
4
4
  class Connection
5
- # Internal class to manage connection state, recovery and state transitions for {Ably::Realtime::Connection}
5
+ # Internal class to manage connection state, recovery and state transitions for {Ably::PubSub::Realtime::Connection}
6
6
  class ConnectionStateMachine
7
7
  include Ably::Modules::StateMachine
8
8
 
@@ -1,6 +1,6 @@
1
1
  require 'openssl'
2
2
 
3
- module Ably::Realtime
3
+ module Ably::PubSub::Realtime
4
4
  class Connection
5
5
  # EventMachine WebSocket transport
6
6
  # @api private