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,363 +0,0 @@
1
- require 'uri'
2
- require 'ably/realtime/channel/publisher'
3
- require 'ably/realtime/recovery_key_context'
4
-
5
- module Ably
6
- module Realtime
7
- # A client that extends the functionality of the {Ably::Realtime::Client} and provides additional realtime-specific features.
8
- #
9
- class Client
10
- include Ably::Modules::AsyncWrapper
11
- include Ably::Realtime::Channel::Publisher
12
- include Ably::Modules::Conversions
13
-
14
- extend Forwardable
15
- using Ably::Util::AblyExtensions
16
-
17
- DOMAIN = 'realtime.ably.io'
18
-
19
- # A {Aby::Realtime::Channels} object.
20
- #
21
- # @spec RTC3, RTS1
22
- #
23
- # @return [Aby::Realtime::Channels]
24
- #
25
- attr_reader :channels
26
-
27
- # An {Ably::Auth} object.
28
- #
29
- # @spec RTC4
30
- #
31
- # @return [Ably::Auth]
32
- #
33
- attr_reader :auth
34
-
35
- # A {Aby::Realtime::Connection} object.
36
- #
37
- # @spec RTC2
38
- #
39
- # @return [Aby::Realtime::Connection]
40
- #
41
- attr_reader :connection
42
-
43
- # The {Ably::Rest::Client REST client} instantiated with the same credentials and configuration that is used for all REST operations such as authentication
44
- # @return [Ably::Rest::Client]
45
-
46
- # @private
47
- attr_reader :rest_client
48
-
49
- # When false the client suppresses messages originating from this connection being echoed back on the same connection. Defaults to true
50
- # @return [Boolean]
51
- attr_reader :echo_messages
52
-
53
- # If false, this disables the default behaviour whereby the library queues messages on a connection in the disconnected or connecting states. Defaults to true
54
- # @return [Boolean]
55
- attr_reader :queue_messages
56
-
57
- # The custom realtime websocket host that is being used if it was provided with the option `:ws_host` when the {Client} was created
58
- # @return [String,Nil]
59
- attr_reader :custom_realtime_host
60
-
61
- # 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
62
- # @return [Boolean]
63
- attr_reader :auto_connect
64
-
65
- # 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
66
- # @return [String,Nil]
67
- attr_reader :recover
68
-
69
- # Additional parameters to be sent in the querystring when initiating a realtime connection
70
- # @return [Hash]
71
- attr_reader :transport_params
72
-
73
- def_delegators :auth, :client_id, :auth_options
74
- def_delegators :@rest_client, :encoders
75
- def_delegators :@rest_client, :use_tls?, :protocol, :protocol_binary?
76
- def_delegators :@rest_client, :environment, :custom_host, :custom_port, :custom_tls_port
77
- def_delegators :@rest_client, :log_level
78
- def_delegators :@rest_client, :options
79
-
80
- # Creates a {Ably::Realtime::Client Realtime Client} and configures the {Ably::Auth} object for the connection.
81
- #
82
- # @spec RSC1
83
- #
84
- # @param (see {Ably::Rest::Client#initialize})
85
- # @option options (see Ably::Rest::Client#initialize) An options {Hash} object.
86
- # @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.
87
- # Whilst the proc is called synchronously, it does not block the EventMachine reactor as it is run in a separate thread.
88
- # The Proc should return a token string, {Ably::Models::TokenDetails} or JSON equivalent, {Ably::Models::TokenRequest} or JSON equivalent
89
- # @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
90
- # @option options [Boolean] :echo_messages If false, prevents messages originating from this connection being echoed back on the same connection
91
- # @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
92
- # @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.
93
- # @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)
94
- #
95
- # @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
96
- # @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
97
- # @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
98
- # @option options [Boolean] :disable_websocket_heartbeats WebSocket heartbeats are more efficient than protocol level heartbeats, however they can be disabled for development purposes
99
- #
100
- # @return [Ably::Realtime::Client]
101
- #
102
- # @example
103
- # # Constructs a {Ably::Realtime::Client} object using an Ably API key or token string.
104
- # client = Ably::Realtime::Client.new('key.id:secret')
105
- #
106
- # # Constructs a {Ably::Realtime::Client} object using an Ably options object.
107
- # client = Ably::Realtime::Client.new(key: 'key.id:secret', client_id: 'john')
108
- #
109
- def initialize(options)
110
- raise ArgumentError, 'Options Hash is expected' if options.nil?
111
-
112
- options = options.clone
113
- if options.kind_of?(String)
114
- options = if options.match(Ably::Auth::API_KEY_REGEX)
115
- { key: options }
116
- else
117
- { token: options }
118
- end
119
- end
120
-
121
- @transport_params = options.delete(:transport_params).to_h.each_with_object({}) do |(key, value), acc|
122
- acc[key.to_s] = value.to_s
123
- end
124
- @rest_client = Ably::Rest::Client.new(options.merge(realtime_client: self))
125
- @echo_messages = rest_client.options.fetch_with_default(:echo_messages, true)
126
- @queue_messages = rest_client.options.fetch_with_default(:queue_messages, true)
127
- @custom_realtime_host = rest_client.options[:realtime_host] || rest_client.options[:ws_host]
128
- @auto_connect = rest_client.options.fetch_with_default(:auto_connect, true)
129
- @recover = rest_client.options.fetch_with_default(:recover, '')
130
-
131
- @auth = Ably::Realtime::Auth.new(self)
132
- @channels = Ably::Realtime::Channels.new(self)
133
- @connection = Ably::Realtime::Connection.new(self, options)
134
-
135
- unless @recover.nil_or_empty?
136
- recovery_context = RecoveryKeyContext.from_json(@recover, logger)
137
- unless recovery_context.nil?
138
- @channels.set_channel_serials recovery_context.channel_serials # RTN16j
139
- @connection.set_msg_serial_from_recover = recovery_context.msg_serial # RTN16f
140
- end
141
- end
142
- end
143
-
144
- # Return a {Ably::Realtime::Channel Realtime Channel} for the given name
145
- #
146
- # @param (see Ably::Realtime::Channels#get)
147
- # @return (see Ably::Realtime::Channels#get)
148
- #
149
- def channel(name, channel_options = {})
150
- channels.get(name, channel_options)
151
- end
152
-
153
- # Retrieves the time from the Ably service as milliseconds since the Unix epoch. Clients that do not have access
154
- # to a sufficiently well maintained time source and wish to issue Ably {Ably::Models::TokenRequests} with
155
- # a more accurate timestamp should use the queryTime property instead of this method.
156
- #
157
- # @spec RTC6a
158
- #
159
- # @yield [Time] The time as milliseconds since the Unix epoch.
160
- # @return [Ably::Util::SafeDeferrable]
161
- #
162
- def time(&success_callback)
163
- async_wrap(success_callback) do
164
- rest_client.time
165
- end
166
- end
167
-
168
- # Queries the REST /stats API and retrieves your application's usage statistics.
169
- # Returns a {Ably::Util::SafeDeferrable} object, containing an array of {Ably::Models::Stats} objects. See the Stats docs.
170
- #
171
- # @spec RTC5
172
- #
173
- # @param (see Ably::Rest::Client#stats)
174
- # @option options (see Ably::Rest::Client#stats)
175
- #
176
- # @yield [Ably::Models::PaginatedResult<Ably::Models::Stats>] A {Ably::Util::SafeDeferrable} object containing an array of {Ably::Models::Stats} objects.
177
- #
178
- # @return [Ably::Util::SafeDeferrable]
179
- #
180
- def stats(options = {}, &success_callback)
181
- async_wrap(success_callback) do
182
- rest_client.stats(options)
183
- end
184
- end
185
-
186
- # Calls {Connection#close} and causes the connection to close, entering the closing state.
187
- # Once closed, the library will not attempt to re-establish the connection without an explicit call to {Connection#connect}.
188
- # @spec RTN12
189
- # (see Ably::Realtime::Connection#close)
190
- def close(&block)
191
- connection.close(&block)
192
- end
193
-
194
- # Calls {Ably::Realtime::Connection#connect} and causes the connection to open, entering the connecting
195
- # state. Explicitly calling connect() is unnecessary unless the autoConnect property is disabled.
196
- # @spec RTN11
197
- # (see Ably::Realtime::Connection#connect)
198
- def connect(&block)
199
- connection.connect(&block)
200
- end
201
-
202
- # A {Ably::Realtime::Push} object.
203
- # @return [Ably::Realtime::Push]
204
- def push
205
- @push ||= Push.new(self)
206
- end
207
-
208
- # Makes a REST request to a provided path. This is provided as a convenience for developers who wish to use REST
209
- # API functionality that is either not documented or is not yet included in the public API, without having to
210
- # directly handle features such as authentication, paging, fallback hosts, MsgPack and JSON support.
211
- #
212
- # @spec RTC9
213
- #
214
- # (see {Ably::Rest::Client#request})
215
- # @yield [Ably::Models::HttpPaginatedResponse<>] An Array of Stats
216
- #
217
- # @return [Ably::Util::SafeDeferrable] An {Ably::Util::SafeDeferrable} response object returned by the HTTP request, containing an empty or JSON-encodable object.
218
- def request(method, path, params = {}, body = nil, headers = {}, &callback)
219
- async_wrap(callback) do
220
- rest_client.request(method, path, params, body, headers, async_blocking_operations: true)
221
- end
222
- end
223
-
224
- # Publish one or more messages to the specified channel.
225
- #
226
- # This method allows messages to be efficiently published to Ably without instancing a {Ably::Realtime::Channel} object.
227
- # If you want to publish a high rate of messages to Ably without instancing channels or using the REST API, then this method
228
- # is recommended. However, channel options such as encryption are not supported with this method. If you need to specify channel options
229
- # we recommend you use the {Ably::Realtime::Channel} +publish+ method without attaching to each channel, unless you also want to subscribe
230
- # to published messages on that channel.
231
- #
232
- # Note: This feature is still in beta. As such, we cannot guarantee the API will not change in future.
233
- #
234
- # @param channel [String] The channel name you want to publish the message(s) to
235
- # @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
236
- # @param data [String, ByteArray, nil] The message payload unless an Array of [Ably::Model::Message] objects passed in the first argument
237
- # @param attributes [Hash, nil] Optional additional message attributes such as :client_id or :connection_id, applied when name attribute is nil or a string
238
- #
239
- # @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
240
- # @return [Ably::Util::SafeDeferrable] Deferrable that supports both success (callback) and failure (errback) callbacks
241
- #
242
- # @example
243
- # # Publish a single message
244
- # client.publish 'activityChannel', click', { x: 1, y: 2 }
245
- #
246
- # # Publish an array of message Hashes
247
- # messages = [
248
- # { name: 'click', { x: 1, y: 2 } },
249
- # { name: 'click', { x: 2, y: 3 } }
250
- # ]
251
- # client.publish 'activityChannel', messages
252
- #
253
- # # Publish an array of Ably::Models::Message objects
254
- # messages = [
255
- # Ably::Models::Message(name: 'click', { x: 1, y: 2 })
256
- # Ably::Models::Message(name: 'click', { x: 2, y: 3 })
257
- # ]
258
- # client.publish 'activityChannel', messages
259
- #
260
- # client.publish('activityChannel', 'click', 'body') do |message|
261
- # puts "#{message.name} event received with #{message.data}"
262
- # end
263
- #
264
- # client.publish('activityChannel', 'click', 'body').errback do |error, message|
265
- # puts "#{message.name} was not received, error #{error.message}"
266
- # end
267
- #
268
- def publish(channel_name, name, data = nil, attributes = {}, &success_block)
269
- if !connection.can_publish_messages?
270
- error = Ably::Exceptions::MessageQueueingDisabled.new("Message cannot be published. Client is not allowed to queue messages when connection is in state #{connection.state}")
271
- return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
272
- end
273
-
274
- messages = if name.kind_of?(Enumerable)
275
- name
276
- else
277
- name = ensure_utf_8(:name, name, allow_nil: true)
278
- ensure_supported_payload data
279
- [{ name: name, data: data }.merge(attributes)]
280
- end
281
-
282
- if messages.length > Realtime::Connection::MAX_PROTOCOL_MESSAGE_BATCH_SIZE
283
- 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.")
284
- return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
285
- end
286
-
287
- enqueue_messages_on_connection(self, messages, channel_name).tap do |deferrable|
288
- deferrable.callback(&success_block) if block_given?
289
- end
290
- end
291
-
292
- # @!attribute [r] endpoint
293
- # @return [URI::Generic] Default Ably Realtime endpoint used for all requests
294
- def endpoint
295
- endpoint_for_host(custom_realtime_host || [environment, DOMAIN].compact.join('-'))
296
- end
297
-
298
- # (see Ably::Rest::Client#register_encoder)
299
- def register_encoder(encoder)
300
- rest_client.register_encoder encoder
301
- end
302
-
303
- # (see Ably::Rest::Client#fallback_hosts)
304
- def fallback_hosts
305
- rest_client.fallback_hosts
306
- end
307
-
308
- # (see Ably::Rest::Client#logger)
309
- def logger
310
- @logger ||= Ably::Logger.new(self, log_level, rest_client.logger.custom_logger)
311
- end
312
-
313
- # Disable connection recovery, typically used after a connection has been recovered
314
- # @return [void]
315
- # @api private
316
- def disable_automatic_connection_recovery
317
- @recover = nil
318
- end
319
-
320
- # @!attribute [r] fallback_endpoint
321
- # @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
322
- # @api private
323
- def fallback_endpoint
324
- unless defined?(@fallback_endpoints) && @fallback_endpoints
325
- @fallback_endpoints = fallback_hosts.shuffle.map { |fallback_host| endpoint_for_host(fallback_host) }
326
- @fallback_endpoints << endpoint # Try the original host last if all fallbacks have been used
327
- end
328
-
329
- fallback_endpoint_index = connection.manager.retry_count_for_state(:disconnected) + connection.manager.retry_count_for_state(:suspended) - 1
330
-
331
- @fallback_endpoints[fallback_endpoint_index % @fallback_endpoints.count]
332
- end
333
-
334
- # Retrieves a {Ably::Models::LocalDevice} object that represents the current state of the device as a target for push notifications.
335
- # @spec RSH8
336
- # @return [Ably::Models::LocalDevice] A {Ably::Models::LocalDevice} object.
337
- #
338
- # @note This is unsupported in the Ruby library
339
- def device
340
- raise Ably::Exceptions::PushNotificationsNotSupported, 'This device does not support receiving or subscribing to push notifications. The local device object is not unavailable'
341
- end
342
-
343
- private
344
- def endpoint_for_host(host)
345
- port = if use_tls?
346
- custom_tls_port
347
- else
348
- custom_port
349
- end
350
-
351
- raise ArgumentError, "Custom port must be an Integer or nil" if port && !port.kind_of?(Integer)
352
-
353
- options = {
354
- scheme: use_tls? ? 'wss' : 'ws',
355
- host: host
356
- }
357
- options.merge!(port: port) if port
358
-
359
- URI::Generic.build(options)
360
- end
361
- end
362
- end
363
- end