ably-rest 0.7.3 → 0.7.5
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.
- checksums.yaml +8 -8
- data/.travis.yml +1 -0
- data/SPEC.md +480 -472
- data/lib/ably-rest.rb +1 -1
- data/lib/submodules/ably-ruby/LICENSE.txt +1 -1
- data/lib/submodules/ably-ruby/README.md +107 -24
- data/lib/submodules/ably-ruby/SPEC.md +531 -398
- data/lib/submodules/ably-ruby/lib/ably/auth.rb +24 -16
- data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +9 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message.rb +17 -9
- data/lib/submodules/ably-ruby/lib/ably/models/paginated_resource.rb +12 -8
- data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +18 -10
- data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +15 -4
- data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +4 -3
- data/lib/submodules/ably-ruby/lib/ably/modules/event_emitter.rb +31 -2
- data/lib/submodules/ably-ruby/lib/ably/modules/message_emitter.rb +77 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/safe_deferrable.rb +71 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/safe_yield.rb +41 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +28 -8
- data/lib/submodules/ably-ruby/lib/ably/realtime.rb +0 -5
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +24 -29
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +54 -11
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +21 -6
- data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +7 -2
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +29 -26
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +4 -4
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +41 -9
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +72 -24
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_state_machine.rb +26 -4
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/websocket_transport.rb +19 -6
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +74 -208
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence/members_map.rb +264 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence/presence_manager.rb +59 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence/presence_state_machine.rb +64 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +6 -2
- data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/util/pub_sub.rb +3 -1
- data/lib/submodules/ably-ruby/lib/ably/util/safe_deferrable.rb +18 -0
- data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +2 -2
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +28 -6
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +116 -46
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +55 -10
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +32 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +456 -96
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +2 -2
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +2 -2
- data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +96 -7
- data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +8 -0
- data/lib/submodules/ably-ruby/spec/shared/safe_deferrable_behaviour.rb +71 -0
- data/lib/submodules/ably-ruby/spec/support/api_helper.rb +1 -1
- data/lib/submodules/ably-ruby/spec/support/event_machine_helper.rb +1 -1
- data/lib/submodules/ably-ruby/spec/support/test_app.rb +13 -7
- data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +15 -14
- data/lib/submodules/ably-ruby/spec/unit/models/paginated_resource_spec.rb +4 -4
- data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +17 -17
- data/lib/submodules/ably-ruby/spec/unit/models/stat_spec.rb +4 -4
- data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +28 -9
- data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +50 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/state_emitter_spec.rb +76 -2
- data/lib/submodules/ably-ruby/spec/unit/realtime/channel_spec.rb +51 -20
- data/lib/submodules/ably-ruby/spec/unit/realtime/channels_spec.rb +3 -3
- data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +30 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/presence_spec.rb +52 -26
- data/lib/submodules/ably-ruby/spec/unit/realtime/safe_deferrable_spec.rb +12 -0
- data/spec/spec_helper.rb +5 -0
- metadata +12 -4
- data/lib/submodules/ably-ruby/.ruby-version.old +0 -1
@@ -40,8 +40,16 @@ module Ably::Realtime
|
|
40
40
|
connection.manager.reconnect_transport
|
41
41
|
end
|
42
42
|
|
43
|
+
before_transition(to: [:connected]) do |connection, current_transition|
|
44
|
+
connection.manager.connected current_transition.metadata
|
45
|
+
end
|
46
|
+
|
43
47
|
after_transition(to: [:connected]) do |connection, current_transition|
|
44
|
-
|
48
|
+
protocol_message = current_transition.metadata
|
49
|
+
if is_error_type?(protocol_message.error)
|
50
|
+
connection.logger.warn "ConnectionManager: Connected with error - #{protocol_message.error.message}"
|
51
|
+
connection.trigger :error, protocol_message.error
|
52
|
+
end
|
45
53
|
end
|
46
54
|
|
47
55
|
after_transition(to: [:disconnected, :suspended], from: [:connecting]) do |connection, current_transition|
|
@@ -73,9 +81,23 @@ module Ably::Realtime
|
|
73
81
|
end
|
74
82
|
|
75
83
|
# Transitions responsible for updating connection#error_reason
|
76
|
-
before_transition(to: [:
|
77
|
-
|
78
|
-
|
84
|
+
before_transition(to: [:disconnected, :suspended, :failed]) do |connection, current_transition|
|
85
|
+
connection.set_failed_connection_error_reason current_transition.metadata
|
86
|
+
end
|
87
|
+
|
88
|
+
before_transition(to: [:connected, :closed]) do |connection, current_transition|
|
89
|
+
error = if current_transition.metadata.kind_of?(Ably::Models::ProtocolMessage)
|
90
|
+
current_transition.metadata.error
|
91
|
+
else
|
92
|
+
current_transition.metadata
|
93
|
+
end
|
94
|
+
|
95
|
+
if is_error_type?(error)
|
96
|
+
connection.set_failed_connection_error_reason error
|
97
|
+
else
|
98
|
+
# Connected & Closed are "healthy" final states so reset the error reason
|
99
|
+
connection.clear_error_reason
|
100
|
+
end
|
79
101
|
end
|
80
102
|
|
81
103
|
private
|
@@ -33,7 +33,7 @@ module Ably::Realtime
|
|
33
33
|
change_state STATE.Disconnecting
|
34
34
|
create_timer(2) do
|
35
35
|
# if connection is not disconnected within 2s, set state as disconnected
|
36
|
-
change_state STATE.Disconnected
|
36
|
+
change_state STATE.Disconnected unless disconnected?
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
@@ -63,7 +63,7 @@ module Ably::Realtime
|
|
63
63
|
# Called whenever a connection (either a server or client connection) is closed
|
64
64
|
# Required {http://www.rubydoc.info/github/eventmachine/eventmachine/EventMachine/Connection EventMachine::Connection} interface
|
65
65
|
def unbind
|
66
|
-
change_state STATE.Disconnected
|
66
|
+
change_state STATE.Disconnected, reason_closed || 'Websocket connection closed unexpectedly'
|
67
67
|
end
|
68
68
|
|
69
69
|
# URL end point including initialization configuration
|
@@ -98,7 +98,7 @@ module Ably::Realtime
|
|
98
98
|
end
|
99
99
|
|
100
100
|
private
|
101
|
-
attr_reader :connection, :driver
|
101
|
+
attr_reader :connection, :driver, :reason_closed
|
102
102
|
|
103
103
|
# Send object down the WebSocket driver connection as a serialized string/byte array based on protocol
|
104
104
|
# @param [Object] object to serialize and send to the WebSocket driver
|
@@ -143,15 +143,28 @@ module Ably::Realtime
|
|
143
143
|
|
144
144
|
driver.on("message") do |event|
|
145
145
|
event_data = parse_event_data(event.data).freeze
|
146
|
-
protocol_message = Ably::Models::ProtocolMessage.new(event_data)
|
147
|
-
|
146
|
+
protocol_message = Ably::Models::ProtocolMessage.new(event_data, logger: logger)
|
147
|
+
action_name = Ably::Models::ProtocolMessage::ACTION[event_data['action']] rescue event_data['action']
|
148
|
+
logger.debug "WebsocketTransport: Prot msg recv <=: #{action_name} - #{event_data}"
|
148
149
|
|
149
150
|
if protocol_message.invalid?
|
150
|
-
|
151
|
+
error = Ably::Exceptions::ProtocolError.new("Invalid Protocol Message received: #{event_data}\nMessage has been discarded", 400, 80013)
|
152
|
+
connection.trigger :error, error
|
153
|
+
logger.fatal "WebsocketTransport: #{error.message}"
|
151
154
|
else
|
152
155
|
__incoming_protocol_msgbus__.publish :protocol_message, protocol_message
|
153
156
|
end
|
154
157
|
end
|
158
|
+
|
159
|
+
driver.on("error") do |error|
|
160
|
+
logger.error "WebsocketTransport: Protocol Error on transports - #{error.message}"
|
161
|
+
end
|
162
|
+
|
163
|
+
@reason_closed = nil
|
164
|
+
driver.on("closed") do |event|
|
165
|
+
@reason_closed = "#{event.code}: #{event.reason}"
|
166
|
+
logger.warn "WebsocketTransport: Driver reported transport as closed - #{reason_closed}"
|
167
|
+
end
|
155
168
|
end
|
156
169
|
|
157
170
|
def client
|
@@ -3,6 +3,8 @@ module Ably::Realtime
|
|
3
3
|
class Presence
|
4
4
|
include Ably::Modules::EventEmitter
|
5
5
|
include Ably::Modules::AsyncWrapper
|
6
|
+
include Ably::Modules::MessageEmitter
|
7
|
+
include Ably::Modules::SafeYield
|
6
8
|
extend Ably::Modules::Enum
|
7
9
|
|
8
10
|
STATE = ruby_enum('STATE',
|
@@ -14,6 +16,7 @@ module Ably::Realtime
|
|
14
16
|
:failed
|
15
17
|
)
|
16
18
|
include Ably::Modules::StateEmitter
|
19
|
+
include Ably::Modules::UsesStateMachine
|
17
20
|
|
18
21
|
# {Ably::Realtime::Channel} this Presence object is associated with
|
19
22
|
# @return [Ably::Realtime::Channel]
|
@@ -32,14 +35,24 @@ module Ably::Realtime
|
|
32
35
|
# @return [String]
|
33
36
|
attr_reader :data
|
34
37
|
|
38
|
+
# {MembersMap} containing an up to date list of members on this channel
|
39
|
+
# @return [MembersMap]
|
40
|
+
# @api private
|
41
|
+
attr_reader :members
|
42
|
+
|
43
|
+
# The Presence manager responsible for actions relating to state changes such as entering a channel
|
44
|
+
# @return [Ably::Realtime::Presence::PresenceManager]
|
45
|
+
# @api private
|
46
|
+
attr_reader :manager
|
47
|
+
|
35
48
|
def initialize(channel)
|
36
49
|
@channel = channel
|
37
|
-
@state = STATE.Initialized
|
38
|
-
@members = Hash.new
|
39
|
-
@subscriptions = Hash.new { |hash, key| hash[key] = [] }
|
40
50
|
@client_id = client.client_id
|
41
51
|
|
42
|
-
|
52
|
+
@state_machine = PresenceStateMachine.new(self)
|
53
|
+
@state = STATE(state_machine.current_state)
|
54
|
+
@members = MembersMap.new(self)
|
55
|
+
@manager = PresenceManager.new(self)
|
43
56
|
end
|
44
57
|
|
45
58
|
# Enter this client into this channel. This client will be added to the presence set
|
@@ -53,12 +66,12 @@ module Ably::Realtime
|
|
53
66
|
# library must have been instanced either with a key, or with a token bound to the wildcard clientId.
|
54
67
|
#
|
55
68
|
# @yield [Ably::Realtime::Presence] On success, will call the block with this {Ably::Realtime::Presence} object
|
56
|
-
# @return [
|
69
|
+
# @return [Ably::Util::SafeDeferrable] Deferrable that supports both success (callback) and failure (errback) callbacks
|
57
70
|
#
|
58
71
|
def enter(options = {}, &success_block)
|
59
72
|
@client_id = options.fetch(:client_id, client_id)
|
60
73
|
@data = options.fetch(:data, nil)
|
61
|
-
deferrable =
|
74
|
+
deferrable = create_deferrable
|
62
75
|
|
63
76
|
raise Ably::Exceptions::Standard.new('Unable to enter presence channel without a client_id', 400, 91000) unless client_id
|
64
77
|
return deferrable_succeed(deferrable, &success_block) if state == STATE.Entered
|
@@ -96,7 +109,7 @@ module Ably::Realtime
|
|
96
109
|
# @option options [String] :data optional data (eg a status message) for this member
|
97
110
|
#
|
98
111
|
# @yield [Ably::Realtime::Presence] On success, will call the block with this {Ably::Realtime::Presence} object
|
99
|
-
# @return [
|
112
|
+
# @return [Ably::Util::SafeDeferrable] Deferrable that supports both success (callback) and failure (errback) callbacks
|
100
113
|
#
|
101
114
|
def enter_client(client_id, options = {}, &success_block)
|
102
115
|
raise ArgumentError, 'options must be a Hash' unless options.kind_of?(Hash)
|
@@ -116,7 +129,7 @@ module Ably::Realtime
|
|
116
129
|
#
|
117
130
|
def leave(options = {}, &success_block)
|
118
131
|
@data = options.fetch(:data, data) # nil value defaults leave data to existing value
|
119
|
-
deferrable =
|
132
|
+
deferrable = create_deferrable
|
120
133
|
|
121
134
|
raise Ably::Exceptions::Standard.new('Unable to leave presence channel that is not entered', 400, 91002) unless able_to_leave?
|
122
135
|
return deferrable_succeed(deferrable, &success_block) if state == STATE.Left
|
@@ -169,7 +182,7 @@ module Ably::Realtime
|
|
169
182
|
#
|
170
183
|
def update(options = {}, &success_block)
|
171
184
|
@data = options.fetch(:data, nil)
|
172
|
-
deferrable =
|
185
|
+
deferrable = create_deferrable
|
173
186
|
|
174
187
|
ensure_channel_attached(deferrable) do
|
175
188
|
send_protocol_message_and_transition_state_to(
|
@@ -204,39 +217,22 @@ module Ably::Realtime
|
|
204
217
|
|
205
218
|
# Get the presence state for this Channel.
|
206
219
|
#
|
207
|
-
# @param
|
208
|
-
# @option options
|
209
|
-
# @
|
210
|
-
# @
|
211
|
-
#
|
212
|
-
# @yield [Array<Ably::Models::PresenceMessage>] array of members or the member
|
213
|
-
#
|
214
|
-
# @return [EventMachine::Deferrable] Deferrable that supports both success (callback) and failure (errback) callbacks
|
220
|
+
# @param (see Ably::Realtime::Presence::MembersMap#get)
|
221
|
+
# @option options (see Ably::Realtime::Presence::MembersMap#get)
|
222
|
+
# @yield (see Ably::Realtime::Presence::MembersMap#get)
|
223
|
+
# @return (see Ably::Realtime::Presence::MembersMap#get)
|
215
224
|
#
|
216
|
-
def get(options = {})
|
217
|
-
|
218
|
-
deferrable = EventMachine::DefaultDeferrable.new
|
225
|
+
def get(options = {}, &block)
|
226
|
+
deferrable = create_deferrable
|
219
227
|
|
220
228
|
ensure_channel_attached(deferrable) do
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
end.tap do |current_members|
|
226
|
-
yield current_members if block_given?
|
227
|
-
deferrable.succeed current_members
|
229
|
+
members.get(options).tap do |members_map_deferrable|
|
230
|
+
members_map_deferrable.callback do |*args|
|
231
|
+
safe_yield block, *args if block_given?
|
232
|
+
deferrable.succeed *args
|
228
233
|
end
|
229
|
-
|
230
|
-
|
231
|
-
if !wait_for_sync || sync_complete?
|
232
|
-
result_block.call
|
233
|
-
else
|
234
|
-
sync_pubsub.once(:done) do
|
235
|
-
result_block.call
|
236
|
-
end
|
237
|
-
|
238
|
-
sync_pubsub.once(:failed) do |error|
|
239
|
-
deferrable.fail error
|
234
|
+
members_map_deferrable.errback do |*args|
|
235
|
+
deferrable.fail *args
|
240
236
|
end
|
241
237
|
end
|
242
238
|
end
|
@@ -245,34 +241,26 @@ module Ably::Realtime
|
|
245
241
|
# Subscribe to presence events on the associated Channel.
|
246
242
|
# This implicitly attaches the Channel if it is not already attached.
|
247
243
|
#
|
248
|
-
# @param
|
244
|
+
# @param actions [Ably::Models::PresenceMessage::ACTION] Optional, the state change action to subscribe to. Defaults to all presence actions
|
249
245
|
# @yield [Ably::Models::PresenceMessage] For each presence state change event, the block is called
|
250
246
|
#
|
251
247
|
# @return [void]
|
252
248
|
#
|
253
|
-
def subscribe(
|
249
|
+
def subscribe(*actions, &callback)
|
254
250
|
ensure_channel_attached do
|
255
|
-
|
251
|
+
super
|
256
252
|
end
|
257
253
|
end
|
258
254
|
|
259
255
|
# Unsubscribe the matching block for presence events on the associated Channel.
|
260
256
|
# If a block is not provided, all subscriptions will be unsubscribed
|
261
257
|
#
|
262
|
-
# @param
|
258
|
+
# @param actions [Ably::Models::PresenceMessage::ACTION] Optional, the state change action to subscribe to. Defaults to all presence actions
|
263
259
|
#
|
264
260
|
# @return [void]
|
265
261
|
#
|
266
|
-
def unsubscribe(
|
267
|
-
|
268
|
-
subscriptions.keys
|
269
|
-
else
|
270
|
-
Array(message_action_key(action))
|
271
|
-
end.each do |key|
|
272
|
-
subscriptions[key].delete_if do |block|
|
273
|
-
!block_given? || callback == block
|
274
|
-
end
|
275
|
-
end
|
262
|
+
def unsubscribe(*actions, &callback)
|
263
|
+
super
|
276
264
|
end
|
277
265
|
|
278
266
|
# Return the presence messages history for the channel
|
@@ -282,7 +270,7 @@ module Ably::Realtime
|
|
282
270
|
#
|
283
271
|
# @yield [Ably::Models::PaginatedResource<Ably::Models::PresenceMessage>] An Array of {Ably::Models::PresenceMessage} objects that supports paging (#next_page, #first_page)
|
284
272
|
#
|
285
|
-
# @return [
|
273
|
+
# @return [Ably::Util::SafeDeferrable]
|
286
274
|
#
|
287
275
|
def history(options = {}, &callback)
|
288
276
|
async_wrap(callback) do
|
@@ -290,62 +278,8 @@ module Ably::Realtime
|
|
290
278
|
end
|
291
279
|
end
|
292
280
|
|
293
|
-
# When attaching to a channel that has members present, the client and server
|
294
|
-
# initiate a sync automatically so that the client has a complete list of members.
|
295
|
-
#
|
296
|
-
# Whilst this sync is happening, this method returns false
|
297
|
-
#
|
298
|
-
# @return [Boolean]
|
299
|
-
def sync_complete?
|
300
|
-
sync_complete
|
301
|
-
end
|
302
|
-
|
303
|
-
# Expect SYNC ProtocolMessages with a list of current members on this channel from the server
|
304
|
-
#
|
305
|
-
# @return [void]
|
306
|
-
#
|
307
|
-
# @api private
|
308
|
-
def sync_started
|
309
|
-
@sync_complete = false
|
310
|
-
|
311
|
-
sync_pubsub.once(:sync_complete) do
|
312
|
-
sync_changes_backlog.each do |presence_message|
|
313
|
-
apply_member_presence_changes presence_message
|
314
|
-
end
|
315
|
-
sync_completed
|
316
|
-
sync_pubsub.trigger :done
|
317
|
-
end
|
318
|
-
|
319
|
-
channel.once_or_if [:detached, :failed] do |error|
|
320
|
-
sync_completed
|
321
|
-
sync_pubsub.trigger :failed, error
|
322
|
-
end
|
323
|
-
end
|
324
|
-
|
325
|
-
# The server has indicated that no members are present on this channel and no SYNC is expected,
|
326
|
-
# or that the SYNC has now completed
|
327
|
-
#
|
328
|
-
# @return [void]
|
329
|
-
#
|
330
|
-
# @api private
|
331
|
-
def sync_completed
|
332
|
-
@sync_complete = true
|
333
|
-
@sync_changes_backlog = []
|
334
|
-
end
|
335
|
-
|
336
|
-
# Update the SYNC serial from the ProtocolMessage so that SYNC can be resumed.
|
337
|
-
# If the serial is nil, or the part after the first : is empty, then the SYNC is complete
|
338
|
-
#
|
339
|
-
# @return [void]
|
340
|
-
#
|
341
|
-
# @api private
|
342
|
-
def update_sync_serial(serial)
|
343
|
-
@sync_serial = serial
|
344
|
-
sync_pubsub.trigger :sync_complete if sync_serial_cursor_at_end?
|
345
|
-
end
|
346
|
-
|
347
281
|
# @!attribute [r] __incoming_msgbus__
|
348
|
-
# @return [Ably::Util::PubSub] Client library internal channel incoming message bus
|
282
|
+
# @return [Ably::Util::PubSub] Client library internal channel incoming protocol message bus
|
349
283
|
# @api private
|
350
284
|
def __incoming_msgbus__
|
351
285
|
@__incoming_msgbus__ ||= Ably::Util::PubSub.new(
|
@@ -353,54 +287,29 @@ module Ably::Realtime
|
|
353
287
|
)
|
354
288
|
end
|
355
289
|
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
def sync_pubsub
|
362
|
-
@sync_pubsub ||= Ably::Util::PubSub.new
|
290
|
+
# Configure the connection ID for this presence channel.
|
291
|
+
# Typically configured only once when a user first enters a presence channel.
|
292
|
+
# @api private
|
293
|
+
def set_connection_id(new_connection_id)
|
294
|
+
@connection_id = new_connection_id
|
363
295
|
end
|
364
296
|
|
365
|
-
#
|
366
|
-
|
367
|
-
|
297
|
+
# Used by {Ably::Modules::StateEmitter} to debug action changes
|
298
|
+
# @api private
|
299
|
+
def logger
|
300
|
+
client.logger
|
368
301
|
end
|
369
302
|
|
370
|
-
#
|
371
|
-
|
372
|
-
|
373
|
-
def sync_serial_cursor_at_end?
|
374
|
-
sync_serial.nil? || sync_serial.to_s.match(/^[\w-]+:?$/)
|
303
|
+
# Returns true when the initial member SYNC following channel attach is completed
|
304
|
+
def sync_complete?
|
305
|
+
members.sync_complete?
|
375
306
|
end
|
376
307
|
|
308
|
+
private
|
377
309
|
def able_to_leave?
|
378
310
|
entering? || entered?
|
379
311
|
end
|
380
312
|
|
381
|
-
def setup_event_handlers
|
382
|
-
__incoming_msgbus__.subscribe(:presence, :sync) do |presence_message|
|
383
|
-
presence_message.decode self.channel
|
384
|
-
update_members_from_presence_message presence_message
|
385
|
-
end
|
386
|
-
|
387
|
-
channel.on(Channel::STATE.Detaching) do
|
388
|
-
change_state STATE.Leaving
|
389
|
-
end
|
390
|
-
|
391
|
-
channel.on(Channel::STATE.Detached) do
|
392
|
-
change_state STATE.Left
|
393
|
-
end
|
394
|
-
|
395
|
-
channel.on(Channel::STATE.Failed) do
|
396
|
-
change_state STATE.Failed unless left? || initialized?
|
397
|
-
end
|
398
|
-
|
399
|
-
on(STATE.Entered) do |message|
|
400
|
-
@connection_id = message.connection_id
|
401
|
-
end
|
402
|
-
end
|
403
|
-
|
404
313
|
# @return [Ably::Models::PresenceMessage] presence message is returned allowing callbacks to be added
|
405
314
|
def send_presence_protocol_message(presence_action, client_id, options = {})
|
406
315
|
presence_message = create_presence_message(presence_action, client_id, options)
|
@@ -426,54 +335,11 @@ module Ably::Realtime
|
|
426
335
|
}
|
427
336
|
model.merge!(data: options.fetch(:data)) if options.has_key?(:data)
|
428
337
|
|
429
|
-
Ably::Models::PresenceMessage.new(model,
|
338
|
+
Ably::Models::PresenceMessage.new(model, logger: logger).tap do |presence_message|
|
430
339
|
presence_message.encode self.channel
|
431
340
|
end
|
432
341
|
end
|
433
342
|
|
434
|
-
def update_members_from_presence_message(presence_message)
|
435
|
-
unless presence_message.connection_id
|
436
|
-
Ably::Exceptions::ProtocolError.new("Protocol error, presence message is missing connectionId", 400, 80013)
|
437
|
-
end
|
438
|
-
|
439
|
-
if sync_complete?
|
440
|
-
apply_member_presence_changes presence_message
|
441
|
-
else
|
442
|
-
if presence_message.action == Ably::Models::PresenceMessage::ACTION.Present
|
443
|
-
add_presence_member presence_message
|
444
|
-
publish_presence_member_state_change presence_message
|
445
|
-
else
|
446
|
-
sync_changes_backlog << presence_message
|
447
|
-
end
|
448
|
-
end
|
449
|
-
end
|
450
|
-
|
451
|
-
def apply_member_presence_changes(presence_message)
|
452
|
-
case presence_message.action
|
453
|
-
when Ably::Models::PresenceMessage::ACTION.Enter, Ably::Models::PresenceMessage::ACTION.Update
|
454
|
-
add_presence_member presence_message
|
455
|
-
when Ably::Models::PresenceMessage::ACTION.Leave
|
456
|
-
remove_presence_member presence_message
|
457
|
-
else
|
458
|
-
Ably::Exceptions::ProtocolError.new("Protocol error, unknown presence action #{presence_message.action}", 400, 80013)
|
459
|
-
end
|
460
|
-
|
461
|
-
publish_presence_member_state_change presence_message
|
462
|
-
end
|
463
|
-
|
464
|
-
def add_presence_member(presence_message)
|
465
|
-
members[presence_message.member_key] = presence_message
|
466
|
-
end
|
467
|
-
|
468
|
-
def remove_presence_member(presence_message)
|
469
|
-
members.delete presence_message.member_key
|
470
|
-
end
|
471
|
-
|
472
|
-
def publish_presence_member_state_change(presence_message)
|
473
|
-
subscriptions[:all].each { |cb| cb.call(presence_message) }
|
474
|
-
subscriptions[presence_message.action].each { |cb| cb.call(presence_message) }
|
475
|
-
end
|
476
|
-
|
477
343
|
def ensure_channel_attached(deferrable = nil)
|
478
344
|
if channel.attached?
|
479
345
|
yield
|
@@ -508,34 +374,34 @@ module Ably::Realtime
|
|
508
374
|
end
|
509
375
|
end
|
510
376
|
|
511
|
-
def deferrable_succeed(deferrable, *args)
|
512
|
-
|
377
|
+
def deferrable_succeed(deferrable, *args, &block)
|
378
|
+
safe_yield block, self, *args if block_given?
|
513
379
|
EventMachine.next_tick { deferrable.succeed self, *args } # allow callback to be added to the returned Deferrable before calling succeed
|
514
380
|
deferrable
|
515
381
|
end
|
516
382
|
|
517
|
-
def deferrable_fail(deferrable, *args)
|
518
|
-
|
383
|
+
def deferrable_fail(deferrable, *args, &block)
|
384
|
+
safe_yield block, self, *args if block_given?
|
519
385
|
EventMachine.next_tick { deferrable.fail self, *args } # allow errback to be added to the returned Deferrable
|
520
386
|
deferrable
|
521
|
-
|
387
|
+
end
|
522
388
|
|
523
389
|
def send_presence_action_for_client(action, client_id, options = {}, &success_block)
|
524
|
-
deferrable =
|
390
|
+
deferrable = create_deferrable
|
525
391
|
|
526
392
|
ensure_channel_attached(deferrable) do
|
527
393
|
send_presence_protocol_message(action, client_id, options).tap do |protocol_message|
|
528
394
|
protocol_message.callback { |message| deferrable_succeed deferrable, &success_block }
|
529
|
-
protocol_message.errback { |message| deferrable_fail
|
395
|
+
protocol_message.errback { |message, error| deferrable_fail deferrable, error }
|
530
396
|
end
|
531
397
|
end
|
532
398
|
end
|
533
399
|
|
534
400
|
def attach_channel_then
|
535
401
|
if channel.detached? || channel.failed?
|
536
|
-
raise Ably::Exceptions::
|
402
|
+
raise Ably::Exceptions::IncompatibleStateForOperation.new("Operation is not allowed when channel is in #{channel.state}", 400, 91001)
|
537
403
|
else
|
538
|
-
channel.
|
404
|
+
channel.unsafe_once(Channel::STATE.Attached) { yield }
|
539
405
|
channel.attach
|
540
406
|
end
|
541
407
|
end
|
@@ -548,17 +414,17 @@ module Ably::Realtime
|
|
548
414
|
client.rest_client.channel(channel.name).presence
|
549
415
|
end
|
550
416
|
|
551
|
-
#
|
552
|
-
def
|
553
|
-
|
417
|
+
# Force subscriptions to match valid PresenceMessage actions
|
418
|
+
def message_emitter_subscriptions_coerce_message_key(name)
|
419
|
+
Ably::Models::PresenceMessage.ACTION(name)
|
554
420
|
end
|
555
421
|
|
556
|
-
def
|
557
|
-
|
558
|
-
:all
|
559
|
-
else
|
560
|
-
Ably::Models::PresenceMessage.ACTION(action)
|
561
|
-
end
|
422
|
+
def create_deferrable
|
423
|
+
Ably::Util::SafeDeferrable.new(logger)
|
562
424
|
end
|
563
425
|
end
|
564
426
|
end
|
427
|
+
|
428
|
+
require 'ably/realtime/presence/presence_manager'
|
429
|
+
require 'ably/realtime/presence/members_map'
|
430
|
+
require 'ably/realtime/presence/presence_state_machine'
|