ably 0.7.2 → 0.7.4
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +1 -1
- data/README.md +107 -24
- data/SPEC.md +531 -398
- data/lib/ably/auth.rb +23 -15
- data/lib/ably/exceptions.rb +9 -0
- data/lib/ably/models/message.rb +17 -9
- data/lib/ably/models/paginated_resource.rb +12 -8
- data/lib/ably/models/presence_message.rb +18 -10
- data/lib/ably/models/protocol_message.rb +15 -4
- data/lib/ably/modules/async_wrapper.rb +4 -3
- data/lib/ably/modules/event_emitter.rb +31 -2
- data/lib/ably/modules/message_emitter.rb +77 -0
- data/lib/ably/modules/safe_deferrable.rb +71 -0
- data/lib/ably/modules/safe_yield.rb +41 -0
- data/lib/ably/modules/state_emitter.rb +28 -8
- data/lib/ably/realtime.rb +0 -5
- data/lib/ably/realtime/channel.rb +24 -29
- data/lib/ably/realtime/channel/channel_manager.rb +54 -11
- data/lib/ably/realtime/channel/channel_state_machine.rb +21 -6
- data/lib/ably/realtime/client.rb +7 -2
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +29 -26
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +4 -4
- data/lib/ably/realtime/connection.rb +41 -9
- data/lib/ably/realtime/connection/connection_manager.rb +72 -24
- data/lib/ably/realtime/connection/connection_state_machine.rb +26 -4
- data/lib/ably/realtime/connection/websocket_transport.rb +19 -6
- data/lib/ably/realtime/presence.rb +74 -208
- data/lib/ably/realtime/presence/members_map.rb +264 -0
- data/lib/ably/realtime/presence/presence_manager.rb +59 -0
- data/lib/ably/realtime/presence/presence_state_machine.rb +64 -0
- data/lib/ably/rest/channel.rb +1 -1
- data/lib/ably/rest/client.rb +6 -2
- data/lib/ably/rest/presence.rb +1 -1
- data/lib/ably/util/pub_sub.rb +3 -1
- data/lib/ably/util/safe_deferrable.rb +18 -0
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/channel_history_spec.rb +2 -2
- data/spec/acceptance/realtime/channel_spec.rb +28 -6
- data/spec/acceptance/realtime/connection_failures_spec.rb +116 -46
- data/spec/acceptance/realtime/connection_spec.rb +55 -10
- data/spec/acceptance/realtime/message_spec.rb +32 -0
- data/spec/acceptance/realtime/presence_spec.rb +456 -96
- data/spec/acceptance/realtime/stats_spec.rb +2 -2
- data/spec/acceptance/realtime/time_spec.rb +2 -2
- data/spec/acceptance/rest/auth_spec.rb +75 -7
- data/spec/shared/client_initializer_behaviour.rb +8 -0
- data/spec/shared/safe_deferrable_behaviour.rb +71 -0
- data/spec/support/api_helper.rb +1 -1
- data/spec/support/event_machine_helper.rb +1 -1
- data/spec/support/test_app.rb +13 -7
- data/spec/unit/models/message_spec.rb +15 -14
- data/spec/unit/models/paginated_resource_spec.rb +4 -4
- data/spec/unit/models/presence_message_spec.rb +17 -17
- data/spec/unit/models/stat_spec.rb +4 -4
- data/spec/unit/modules/async_wrapper_spec.rb +28 -9
- data/spec/unit/modules/event_emitter_spec.rb +50 -0
- data/spec/unit/modules/state_emitter_spec.rb +76 -2
- data/spec/unit/realtime/channel_spec.rb +51 -20
- data/spec/unit/realtime/channels_spec.rb +3 -3
- data/spec/unit/realtime/connection_spec.rb +30 -0
- data/spec/unit/realtime/presence_spec.rb +52 -26
- data/spec/unit/realtime/safe_deferrable_spec.rb +12 -0
- metadata +85 -39
- checksums.yaml +0 -7
- data/.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'
|