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
@@ -0,0 +1,545 @@
1
+ require 'ably/pubsub/realtime/channel/publisher'
2
+
3
+ module Ably
4
+ module PubSub
5
+ module Realtime
6
+ # Enables messages to be published and subscribed to. Also enables historic messages to be retrieved and provides
7
+ # access to the {Ably::PubSub::Realtime::Channel} object of a channel.
8
+ #
9
+ # Channels will always be in one of the following states:
10
+ #
11
+ # initialized: 0
12
+ # attaching: 1
13
+ # attached: 2
14
+ # detaching: 3
15
+ # detached: 4
16
+ # failed: 5
17
+ #
18
+ # Note that the states are available as Enum-like constants:
19
+ #
20
+ # Channel::STATE.Initialized The channel has been initialized but no attach has yet been attempted.
21
+ # Channel::STATE.Attaching An attach has been initiated by sending a request to Ably.
22
+ # This is a transient state, followed either by a transition to ATTACHED, SUSPENDED, or FAILED.
23
+ # Channel::STATE.Attached The attach has succeeded. In the ATTACHED state a client may publish and subscribe to messages, or be present on the channel.
24
+ # Channel::STATE.Detaching A detach has been initiated on an ATTACHED channel by sending a request to Ably.
25
+ # This is a transient state, followed either by a transition to DETACHED or FAILED.
26
+ # Channel::STATE.Detached The channel, having previously been ATTACHED, has been detached by the user.
27
+ # Channel::STATE.Suspended The channel, having previously been ATTACHED, has lost continuity, usually due to
28
+ # the client being disconnected from Ably for longer than two minutes. It will automatically attempt to reattach as soon as connectivity is restored.
29
+ # Channel::STATE.Failed An indefinite failure condition. This state is entered if a channel error
30
+ # has been received from the Ably service, such as an attempt to attach without the necessary access rights.
31
+ #
32
+ class Channel
33
+ include Ably::Modules::Conversions
34
+ include Ably::Modules::EventEmitter
35
+ include Ably::Modules::EventMachineHelpers
36
+ include Ably::Modules::AsyncWrapper
37
+ include Ably::Modules::MessageEmitter
38
+ include Ably::PubSub::Realtime::Channel::Publisher
39
+ extend Ably::Modules::Enum
40
+ extend Forwardable
41
+
42
+ # The current {Abbly::Realtime::Channel::STATE} of the channel.
43
+ #
44
+ # @spec RTL2b
45
+ #
46
+ # The permitted states for this channel
47
+ STATE = ruby_enum('STATE',
48
+ :initialized,
49
+ :attaching,
50
+ :attached,
51
+ :detaching,
52
+ :detached,
53
+ :suspended,
54
+ :failed
55
+ )
56
+
57
+ # Describes the events emitted by a {Ably::PubSub::Http::Channel} or {Ably::PubSub::Realtime::Channel} object.
58
+ # An event is either an UPDATE or a {Ably::PubSub::Http::Channel::STATE}.
59
+ #
60
+ # The permitted channel events that are emitted for this channel
61
+ #
62
+ # @spec RTL2g
63
+ #
64
+ EVENT = ruby_enum('EVENT',
65
+ STATE.to_sym_arr + [:update]
66
+ )
67
+
68
+ include Ably::Modules::StateEmitter
69
+ include Ably::Modules::UsesStateMachine
70
+ ensure_state_machine_emits 'Ably::Models::ChannelStateChange'
71
+
72
+ # Max number of messages to bundle in a single ProtocolMessage
73
+ MAX_PROTOCOL_MESSAGE_BATCH_SIZE = 50
74
+
75
+ # {Ably::PubSub::Realtime::Client} associated with this channel
76
+ #
77
+ # @return [Ably::PubSub::Realtime::Client]
78
+ #
79
+ # @api private
80
+ attr_reader :client
81
+
82
+ # The channel name.
83
+ # @return [String]
84
+ attr_reader :name
85
+
86
+ # A {Ably::PubSub::Realtime::Channel::PushChannel} object.
87
+ #
88
+ # @return [Ably::PubSub::Realtime::Channel::PushChannel]
89
+ attr_reader :push
90
+
91
+ # Channel options configured for this channel, see {#initialize} for channel_options
92
+ # @return [Hash]
93
+ attr_reader :options
94
+
95
+ # A {Ably::PubSub::Realtime::Channel::ChannelProperties} object.
96
+ #
97
+ # @spec CP1, RTL15
98
+ #
99
+ # @return [{Ably::PubSub::Realtime::Channel::ChannelProperties}]
100
+ attr_reader :properties
101
+
102
+ # An {Ably::Models::ErrorInfo} object describing the last error which occurred on the channel, if any.
103
+ # @spec RTL4e
104
+ # @return [Ably::Models::ErrorInfo,Ably::Exceptions::BaseAblyException]
105
+ attr_reader :error_reason
106
+
107
+ # The Channel manager responsible for attaching, detaching and handling failures for this channel
108
+ # @return [Ably::PubSub::Realtime::Channel::ChannelManager]
109
+ # @api private
110
+ attr_reader :manager
111
+
112
+ # Flag that specifies whether channel is resuming attachment(reattach) or is doing a 'clean attach' RTL4j1
113
+ # @return [Boolean]
114
+ # @api private
115
+ attr_reader :attach_resume
116
+
117
+ # Optional channel parameters that configure the behavior of the channel.
118
+ # @spec RTL4k1
119
+ # return [Hash]
120
+ def_delegators :options, :params
121
+
122
+ # Initialize a new Channel object
123
+ #
124
+ # @param client [Ably::PubSub::Http::Client]
125
+ # @param name [String] The name of the channel
126
+ # @param channel_options [Hash, Ably::Models::ChannelOptions] A hash of options or a {Ably::Models::ChannelOptions}
127
+ #
128
+ def initialize(client, name, channel_options = {})
129
+ name = ensure_utf_8(:name, name)
130
+
131
+ @options = Ably::Models::ChannelOptions(channel_options)
132
+ @client = client
133
+ @name = name
134
+ @queue = []
135
+
136
+ @state_machine = ChannelStateMachine.new(self)
137
+ @state = STATE(state_machine.current_state)
138
+ @manager = ChannelManager.new(self, client.connection)
139
+ @push = PushChannel.new(self)
140
+ @properties = ChannelProperties.new(self)
141
+ @attach_resume = false
142
+
143
+ setup_event_handlers
144
+ setup_presence
145
+ end
146
+
147
+ # Publish a message to the channel. A callback may optionally be passed in to this call to be notified of success
148
+ # or failure of the operation. When publish is called with this client library, it won't attempt to implicitly
149
+ # attach to the channel.
150
+ #
151
+ # @spec RTL6i
152
+ #
153
+ # @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
154
+ # @param data [String, ByteArray, nil] The message payload unless an Array of [Ably::Model::Message] objects passed in the first argument
155
+ # @param attributes [Hash, nil] Optional additional message attributes such as :client_id or :connection_id, applied when name attribute is nil or a string
156
+ #
157
+ # @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
158
+ # @return [Ably::Util::SafeDeferrable] Deferrable that supports both success (callback) and failure (errback) callbacks
159
+ #
160
+ # @example
161
+ # # Publish a single message form
162
+ # channel.publish 'click', { x: 1, y: 2 }
163
+ #
164
+ # # Publish a single message with single Hash form
165
+ # message = { name: 'click', data: { x: 1, y: 2 } }
166
+ # channel.publish message
167
+ #
168
+ # # Publish an array of message Hashes form
169
+ # messages = [
170
+ # { name: 'click', data: { x: 1, y: 2 } },
171
+ # { name: 'click', data: { x: 2, y: 3 } }
172
+ # ]
173
+ # channel.publish messages
174
+ #
175
+ # # Publish an array of Ably::Models::Message objects form
176
+ # messages = [
177
+ # Ably::Models::Message(name: 'click', data: { x: 1, y: 2 })
178
+ # Ably::Models::Message(name: 'click', data: { x: 2, y: 3 })
179
+ # ]
180
+ # channel.publish messages
181
+ #
182
+ # # Publish an array of Ably::Models::Message objects form
183
+ # message = Ably::Models::Message(name: 'click', data: { x: 1, y: 2 })
184
+ # channel.publish message
185
+ #
186
+ # channel.publish('click', 'body') do |message|
187
+ # puts "#{message.name} event received with #{message.data}"
188
+ # end
189
+ #
190
+ # channel.publish('click', 'body').errback do |error, message|
191
+ # puts "#{message.name} was not received, error #{error.message}"
192
+ # end
193
+ #
194
+ def publish(name, data = nil, attributes = {}, &success_block)
195
+ if suspended? || failed?
196
+ error = Ably::Exceptions::ChannelInactive.new("Cannot publish messages on a channel in state #{state}")
197
+ return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
198
+ end
199
+
200
+ if !connection.can_publish_messages?
201
+ error = Ably::Exceptions::MessageQueueingDisabled.new("Message cannot be published. Client is not allowed to queue messages when connection is in state #{connection.state}")
202
+ return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
203
+ end
204
+
205
+ messages = build_messages(name, data, attributes) # (RSL1a, RSL1b)
206
+
207
+ if messages.length > Realtime::Connection::MAX_PROTOCOL_MESSAGE_BATCH_SIZE
208
+ 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.")
209
+ return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
210
+ end
211
+
212
+ enqueue_messages_on_connection(client, messages, channel_name, options).tap do |deferrable|
213
+ deferrable.callback(&success_block) if block_given?
214
+ end
215
+ end
216
+
217
+ # Updates a previously published message on the channel. A callback may optionally be passed in to this
218
+ # call to be notified of success or failure of the operation.
219
+ #
220
+ # @spec RTL32
221
+ #
222
+ # @param message [Ably::Models::Message, Hash] A Message object or Hash containing a populated :serial field
223
+ # and the fields to update.
224
+ # @param operation [Hash, Ably::Models::MessageOperation, nil] Optional operation metadata.
225
+ # @param params [Hash, nil] Optional parameters sent as part of the protocol message.
226
+ #
227
+ # @yield [Ably::Models::UpdateDeleteResult] On success, calls the block with the result containing version_serial.
228
+ # @return [Ably::Util::SafeDeferrable] Deferrable that supports both success (callback) and failure (errback) callbacks
229
+ #
230
+ def update_message(message, operation = nil, params = {}, &success_block)
231
+ send_message_action(message, Ably::Models::Message::ACTION.MessageUpdate, operation, params, &success_block)
232
+ end
233
+
234
+ # Deletes a previously published message on the channel. A callback may optionally be passed in to this
235
+ # call to be notified of success or failure of the operation.
236
+ #
237
+ # @spec RTL32
238
+ #
239
+ # @param message [Ably::Models::Message, Hash] A Message object or Hash containing a populated :serial field.
240
+ # @param operation [Hash, Ably::Models::MessageOperation, nil] Optional operation metadata.
241
+ # @param params [Hash, nil] Optional parameters sent as part of the protocol message.
242
+ #
243
+ # @yield [Ably::Models::UpdateDeleteResult] On success, calls the block with the result containing version_serial.
244
+ # @return [Ably::Util::SafeDeferrable] Deferrable that supports both success (callback) and failure (errback) callbacks
245
+ #
246
+ def delete_message(message, operation = nil, params = {}, &success_block)
247
+ send_message_action(message, Ably::Models::Message::ACTION.MessageDelete, operation, params, &success_block)
248
+ end
249
+
250
+ # Appends data to a previously published message on the channel. A callback may optionally be passed in to this
251
+ # call to be notified of success or failure of the operation.
252
+ #
253
+ # @spec RTL32
254
+ #
255
+ # @param message [Ably::Models::Message, Hash] A Message object or Hash containing a populated :serial field
256
+ # and the data to append.
257
+ # @param operation [Hash, Ably::Models::MessageOperation, nil] Optional operation metadata.
258
+ # @param params [Hash, nil] Optional parameters sent as part of the protocol message.
259
+ #
260
+ # @yield [Ably::Models::UpdateDeleteResult] On success, calls the block with the result containing version_serial.
261
+ # @return [Ably::Util::SafeDeferrable] Deferrable that supports both success (callback) and failure (errback) callbacks
262
+ #
263
+ def append_message(message, operation = nil, params = {}, &success_block)
264
+ send_message_action(message, Ably::Models::Message::ACTION.MessageAppend, operation, params, &success_block)
265
+ end
266
+
267
+ # Registers a listener for messages on this channel. The caller supplies a listener function, which is called
268
+ # each time one or more messages arrives on the channel. A callback may optionally be passed in to this call
269
+ # to be notified of success or failure of the channel {Ably::PubSub::Realtime::Channel#attach} operation.
270
+ #
271
+ # @spec RTL7a
272
+ #
273
+ # @param names [String] The event name of the message to subscribe to if provided. Defaults to all events.
274
+ # @yield [Ably::Models::Message] For each message received, the block is called
275
+ #
276
+ # @return [void]
277
+ #
278
+ def subscribe(*names, &callback)
279
+ attach unless attached? || attaching?
280
+ super
281
+ end
282
+
283
+ # Deregisters the given listener for the specified event name(s). This removes an earlier event-specific subscription.
284
+ #
285
+ # @spec RTL8a
286
+ #
287
+ # @param names [String] The event name of the message to subscribe to if provided. Defaults to all events.
288
+ #
289
+ # @return [void]
290
+ #
291
+ def unsubscribe(*names, &callback)
292
+ super
293
+ end
294
+
295
+ # Attach to this channel ensuring the channel is created in the Ably system and all messages published on
296
+ # the channel are received by any channel listeners registered using {Ably::PubSub::Realtime::Channel#subscribe}.
297
+ # Any resulting channel state change will be emitted to any listeners registered using the {Ably::Modules::EventEmitter#on}
298
+ # or {Ably::Modules::EventEmitter#once} methods. A callback may optionally be passed in to this call to be notified
299
+ # of success or failure of the operation. As a convenience, attach() is called implicitly
300
+ # if {Ably::PubSub::Realtime::Channel#subscribe} for the channel is called, or {Ably::PubSub::Realtime::Presence#enter}
301
+ # or {Ably::PubSub::Realtime::Presence#subscribe} are called on the {Ably::PubSub::Realtime::Presence} object for this channel.
302
+ #
303
+ # @spec RTL4d
304
+ #
305
+ # @yield [Ably::PubSub::Realtime::Channel] Block is called as soon as this channel is in the Attached state
306
+ # @return [Ably::Util::SafeDeferrable] Deferrable that supports both success (callback) and failure (errback) callback
307
+ #
308
+ def attach(&success_block)
309
+ if connection.closing? || connection.closed? || connection.suspended? || connection.failed?
310
+ error = Ably::Exceptions::InvalidStateChange.new("Cannot ATTACH channel when the connection is in a closed, suspended or failed state. Connection state: #{connection.state}")
311
+ return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
312
+ end
313
+
314
+ if !attached?
315
+ if detaching?
316
+ # Let the pending operation complete (#RTL4h)
317
+ once_state_changed { transition_state_machine :attaching if can_transition_to?(:attaching) }
318
+ else
319
+ transition_state_machine :attaching if can_transition_to?(:attaching)
320
+ end
321
+ end
322
+
323
+ deferrable_for_state_change_to(STATE.Attached, &success_block)
324
+ end
325
+
326
+ # Detach from this channel. Any resulting channel state change is emitted to any listeners registered using
327
+ # the {Ably::Modules::EventEmitter#on} or {Ably::Modules::EventEmitter#once} methods. A callback may optionally
328
+ # be passed in to this call to be notified of success or failure of the operation. Once all clients globally
329
+ # have detached from the channel, the channel will be released in the Ably service within two minutes.
330
+ #
331
+ # @spec RTL5e
332
+ #
333
+ # @yield [Ably::PubSub::Realtime::Channel] Block is called as soon as this channel is in the Detached or Failed state
334
+ # @return [Ably::Util::SafeDeferrable] Deferrable that supports both success (callback) and failure (errback) callback
335
+ #
336
+ def detach(&success_block)
337
+ if initialized?
338
+ success_block.call if block_given?
339
+ return Ably::Util::SafeDeferrable.new_and_succeed_immediately(logger)
340
+ end
341
+
342
+ if failed? || connection.closing? || connection.failed?
343
+ return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, exception_for_state_change_to(:detaching))
344
+ end
345
+
346
+ if !detached?
347
+ if attaching?
348
+ # Let the pending operation complete (#RTL5i)
349
+ once_state_changed { transition_state_machine :detaching if can_transition_to?(:detaching) }
350
+ elsif can_transition_to?(:detaching)
351
+ transition_state_machine :detaching
352
+ else
353
+ transition_state_machine! :detached
354
+ end
355
+ end
356
+
357
+ deferrable_for_state_change_to(STATE.Detached, &success_block)
358
+ end
359
+
360
+ # A {Ably::PubSub::Realtime::Presence} object.
361
+ #
362
+ # @spec RTL9
363
+ #
364
+ # @return {Ably::PubSub::Realtime::Presence}
365
+ #
366
+ def presence
367
+ @presence
368
+ end
369
+
370
+ # Retrieves a {Ably::Models::PaginatedResult} object, containing an array of historical
371
+ # {Ably::Models::Message} objects for the channel. If the channel is configured to persist messages,
372
+ # then messages can be retrieved from history for up to 72 hours in the past. If not, messages can only
373
+ # be retrieved from history for up to two minutes in the past.
374
+ #
375
+ # @spec RSL2a
376
+ #
377
+ # @param (see {Ably::PubSub::Http::Channel#history})
378
+ # @option options (see {Ably::PubSub::Http::Channel#history})
379
+ # @option options [Boolean] :until_attach When true, the history request will be limited only to messages published before this channel was attached. Channel must be attached
380
+ #
381
+ # @yield [Ably::Models::PaginatedResult<Ably::Models::Message>] First {Ably::Models::PaginatedResult page} of {Ably::Models::Message} objects accessible with {Ably::Models::PaginatedResult#items #items}.
382
+ #
383
+ # @return [Ably::Util::SafeDeferrable]
384
+ #
385
+ def history(options = {}, &callback)
386
+ # RTL10b
387
+ if options.delete(:until_attach)
388
+ unless attached?
389
+ error = Ably::Exceptions::InvalidRequest.new('option :until_attach is invalid as the channel is not attached' )
390
+ return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
391
+ end
392
+ options[:from_serial] = properties.attach_serial
393
+ end
394
+
395
+ async_wrap(callback) do
396
+ rest_channel.history(options.merge(async_blocking_operations: true))
397
+ end
398
+ end
399
+
400
+ # @return [Ably::Util::PubSub] Client library internal channel incoming message bus
401
+ #
402
+ # @api private
403
+ def __incoming_msgbus__
404
+ @__incoming_msgbus__ ||= Ably::Util::PubSub.new(
405
+ coerce_into: lambda { |event| Ably::Models::ProtocolMessage::ACTION(event) }
406
+ )
407
+ end
408
+
409
+ # Sets the {Ably::Models::ChannelOptions} for the channel.
410
+ # An optional callback may be provided to notify of the success or failure of the operation.
411
+ #
412
+ # @spec RTL16
413
+ #
414
+ # @param channel_options [Hash, Ably::Models::ChannelOptions] A hash of options or a {Ably::Models::ChannelOptions}
415
+ # @return [Ably::Models::ChannelOptions]
416
+ def set_options(channel_options)
417
+ @options = Ably::Models::ChannelOptions(channel_options)
418
+ # RTL4i
419
+ manager.request_reattach if (need_reattach? and connection.state?(:connected))
420
+ end
421
+ alias options= set_options
422
+
423
+ # @api private
424
+ def set_channel_error_reason(error)
425
+ @error_reason = error
426
+ end
427
+
428
+ # @api private
429
+ def clear_error_reason
430
+ @error_reason = nil
431
+ end
432
+
433
+ # Used by {Ably::Modules::StateEmitter} to debug state changes
434
+ # @api private
435
+ def logger
436
+ client.logger
437
+ end
438
+
439
+ # @api private
440
+ def attach_resume!
441
+ @attach_resume = true
442
+ end
443
+
444
+ # @api private
445
+ def reset_attach_resume!
446
+ @attach_resume = false
447
+ end
448
+
449
+ # As we are using a state machine, do not allow change_state to be used
450
+ # #transition_state_machine must be used instead
451
+ private :change_state
452
+
453
+ def need_reattach?
454
+ !!(attaching? || attached?) && !!(options.modes || options.params)
455
+ end
456
+
457
+ private
458
+
459
+ def send_message_action(message, action_enum, operation = nil, params = {}, &success_block)
460
+ if suspended? || failed?
461
+ error = Ably::Exceptions::ChannelInactive.new("Cannot send message action on a channel in state #{state}")
462
+ return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
463
+ end
464
+
465
+ if !connection.can_publish_messages?
466
+ error = Ably::Exceptions::MessageQueueingDisabled.new(
467
+ "Message action cannot be sent. Client is not allowed to queue messages when connection is in state #{connection.state}"
468
+ )
469
+ return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
470
+ end
471
+
472
+ message = Ably::Models::Message(message)
473
+
474
+ unless message.serial
475
+ error = Ably::Exceptions::InvalidRequest.new('Message serial is required. Ensure the message has a serial field.')
476
+ return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
477
+ end
478
+
479
+ update_attrs = message.as_json
480
+ update_attrs['action'] = action_enum.to_i
481
+
482
+ if operation
483
+ op_hash = operation.respond_to?(:as_json) ? operation.as_json : operation
484
+ update_attrs['version'] = op_hash
485
+ end
486
+
487
+ updated_message = Ably::Models::Message.new(update_attrs)
488
+ updated_message.encode(client.encoders, options) do |encode_error, error_message|
489
+ client.logger.error error_message
490
+ end
491
+
492
+ pm_params = { action: Ably::Models::ProtocolMessage::ACTION.Message.to_i, channel: name, messages: [updated_message] }
493
+ pm_params[:params] = params.transform_values(&:to_s) if params && !params.empty?
494
+
495
+ connection.send_protocol_message(pm_params)
496
+
497
+ Ably::Util::SafeDeferrable.new(logger).tap do |deferrable|
498
+ updated_message.callback do |result|
499
+ if result.is_a?(Ably::Models::UpdateDeleteResult)
500
+ deferrable.succeed result
501
+ else
502
+ deferrable.succeed Ably::Models::UpdateDeleteResult.new(version_serial: nil)
503
+ end
504
+ end
505
+ updated_message.errback do |error|
506
+ deferrable.fail error
507
+ end
508
+ deferrable.callback(&success_block) if block_given?
509
+ end
510
+ end
511
+
512
+ def setup_event_handlers
513
+ __incoming_msgbus__.subscribe(:message) do |message|
514
+ message.decode(client.encoders, options) do |encode_error, error_message|
515
+ client.logger.error error_message
516
+ end
517
+ emit_message message.name, message
518
+ end
519
+ end
520
+
521
+ def rest_channel
522
+ client.rest_client.channel(name)
523
+ end
524
+
525
+ def connection
526
+ client.connection
527
+ end
528
+
529
+ def setup_presence
530
+ @presence ||= Presence.new(self)
531
+ end
532
+
533
+ # Alias useful for methods with a name argument
534
+ def channel_name
535
+ name
536
+ end
537
+ end
538
+ end
539
+ end
540
+ end
541
+
542
+ require 'ably/pubsub/realtime/channel/channel_manager'
543
+ require 'ably/pubsub/realtime/channel/channel_state_machine'
544
+ require 'ably/pubsub/realtime/channel/push_channel'
545
+ require 'ably/pubsub/realtime/channel/channel_properties'
@@ -0,0 +1,92 @@
1
+ module Ably
2
+ module PubSub
3
+ module Realtime
4
+ # Class that maintains a map of Channels ensuring Channels are reused
5
+ class Channels
6
+ include Ably::Modules::ChannelsCollection
7
+
8
+ # @return [Ably::PubSub::Realtime::Channels]
9
+ #
10
+ def initialize(client)
11
+ super client, Ably::PubSub::Realtime::Channel
12
+ end
13
+
14
+ # Return a {Ably::PubSub::Realtime::Channel} for the given name
15
+ #
16
+ # @param name [String] The name of the channel
17
+ # @param channel_options [Hash, Ably::Models::ChannelOptions] A hash of options or a {Ably::Models::ChannelOptions}
18
+ #
19
+ # @return [Ably::PubSub::Realtime::Channel]
20
+ #
21
+ def get(*args)
22
+ super
23
+ end
24
+
25
+ # Return a {Ably::PubSub::Realtime::Channel} for the given name if it exists, else the block will be called.
26
+ # This method is intentionally similar to {http://ruby-doc.org/core-2.1.3/Hash.html#method-i-fetch Hash#fetch} providing a simple way to check if a channel exists or not without creating one
27
+ #
28
+ # @param name [String] The name of the channel
29
+ # @yield [options] (optional) if a missing_block is passed to this method and no channel exists matching the name, this block is called
30
+ # @yieldparam [String] name of the missing channel
31
+ #
32
+ # @return [Ably::PubSub::Realtime::Channel]
33
+ #
34
+ def fetch(*args)
35
+ super
36
+ end
37
+
38
+ # Releases the {Ably::PubSub::Realtime::Channel Realtime Channel} with the given name and all associated resources.
39
+ #
40
+ # Releasing a Realtime Channel is not typically necessary as a channel, once detached, consumes no resources other than
41
+ # the memory footprint of the {Ably::PubSub::Realtime::Channel Realtime Channel object}. Release channels to free up resources if required
42
+ #
43
+ # A realtime channel can only be released when it is in the +INITIALIZED+, +DETACHED+, or +FAILED+ state.
44
+ #
45
+ # @spec RTS4c, RTS4d, RTS4e
46
+ #
47
+ # @param channel [String] The name of the channel
48
+ #
49
+ # @raise [Ably::Exceptions::InvalidState] if the channel is not in the +INITIALIZED+, +DETACHED+, or +FAILED+ state
50
+ #
51
+ # @return [void]
52
+ #
53
+ def release(channel)
54
+ return unless @channels.has_key?(channel)
55
+
56
+ released_channel = get(channel)
57
+ unless released_channel.initialized? || released_channel.detached? || released_channel.failed?
58
+ raise Ably::Exceptions::InvalidState.new(
59
+ "Can only release a channel in a state where there is no possibility of further updates from the server being received " \
60
+ "(initialized, detached, or failed). The current state is #{released_channel.state.to_sym}",
61
+ 400,
62
+ 90011
63
+ )
64
+ end
65
+
66
+ @channels.delete(channel)
67
+ nil
68
+ end
69
+
70
+ # Sets channel serial to each channel from given serials hashmap
71
+ # @param [Hash] serials - map of channel name to respective channel serial
72
+ # @api private
73
+ def set_channel_serials(serials)
74
+ serials.each do |channel_name, channel_serial|
75
+ get(channel_name).properties.channel_serial = channel_serial
76
+ end
77
+ end
78
+
79
+ # @return [Hash] serials - map of channel name to respective channel serial
80
+ # @api private
81
+ def get_channel_serials
82
+ channel_serials = {}
83
+ self.each do |channel|
84
+ channel_serials[channel.name] = channel.properties.channel_serial if channel.state == :attached
85
+ end
86
+ channel_serials
87
+ end
88
+
89
+ end
90
+ end
91
+ end
92
+ end