ably 0.8.2 → 0.8.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/CHANGELOG.md +185 -0
- data/LICENSE +15 -0
- data/README.md +8 -4
- data/SPEC.md +999 -531
- data/ably.gemspec +1 -1
- data/lib/ably.rb +1 -1
- data/lib/ably/auth.rb +114 -87
- data/lib/ably/exceptions.rb +40 -14
- data/lib/ably/models/message.rb +3 -5
- data/lib/ably/models/paginated_result.rb +3 -12
- data/lib/ably/models/presence_message.rb +8 -2
- data/lib/ably/models/protocol_message.rb +15 -3
- data/lib/ably/models/stat.rb +1 -1
- data/lib/ably/models/token_details.rb +1 -1
- data/lib/ably/modules/channels_collection.rb +7 -1
- data/lib/ably/modules/conversions.rb +1 -1
- data/lib/ably/modules/encodeable.rb +6 -3
- data/lib/ably/modules/message_pack.rb +2 -2
- data/lib/ably/modules/model_common.rb +1 -1
- data/lib/ably/modules/state_machine.rb +2 -2
- data/lib/ably/realtime.rb +1 -0
- data/lib/ably/realtime/auth.rb +191 -0
- data/lib/ably/realtime/channel.rb +97 -25
- data/lib/ably/realtime/channel/channel_manager.rb +11 -3
- data/lib/ably/realtime/client.rb +22 -6
- data/lib/ably/realtime/connection.rb +74 -41
- data/lib/ably/realtime/connection/connection_manager.rb +48 -33
- data/lib/ably/realtime/presence.rb +17 -3
- data/lib/ably/rest/channel.rb +43 -16
- data/lib/ably/rest/client.rb +57 -26
- data/lib/ably/rest/middleware/exceptions.rb +3 -1
- data/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +4 -2
- data/lib/ably/rest/presence.rb +1 -0
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/auth_spec.rb +242 -0
- data/spec/acceptance/realtime/channel_spec.rb +277 -5
- data/spec/acceptance/realtime/channels_spec.rb +64 -0
- data/spec/acceptance/realtime/client_spec.rb +26 -5
- data/spec/acceptance/realtime/connection_failures_spec.rb +23 -6
- data/spec/acceptance/realtime/connection_spec.rb +167 -16
- data/spec/acceptance/realtime/message_spec.rb +9 -8
- data/spec/acceptance/realtime/presence_history_spec.rb +1 -0
- data/spec/acceptance/realtime/presence_spec.rb +121 -10
- data/spec/acceptance/realtime/stats_spec.rb +13 -1
- data/spec/acceptance/rest/auth_spec.rb +161 -79
- data/spec/acceptance/rest/base_spec.rb +3 -3
- data/spec/acceptance/rest/channel_spec.rb +142 -15
- data/spec/acceptance/rest/channels_spec.rb +23 -0
- data/spec/acceptance/rest/client_spec.rb +180 -18
- data/spec/acceptance/rest/message_spec.rb +8 -8
- data/spec/acceptance/rest/presence_spec.rb +136 -25
- data/spec/acceptance/rest/stats_spec.rb +60 -4
- data/spec/shared/client_initializer_behaviour.rb +54 -3
- data/spec/unit/auth_spec.rb +7 -6
- data/spec/unit/models/message_spec.rb +1 -9
- data/spec/unit/models/paginated_result_spec.rb +1 -18
- data/spec/unit/models/presence_message_spec.rb +1 -1
- data/spec/unit/models/protocol_message_spec.rb +21 -1
- data/spec/unit/realtime/channel_spec.rb +10 -3
- data/spec/unit/realtime/channels_spec.rb +27 -8
- data/spec/unit/rest/channel_spec.rb +0 -8
- data/spec/unit/rest/client_spec.rb +7 -7
- metadata +13 -7
- data/LICENSE.txt +0 -22
@@ -26,7 +26,7 @@ module Ably::Realtime
|
|
26
26
|
|
27
27
|
# Commence attachment
|
28
28
|
def detach(error = nil)
|
29
|
-
if connection.closed? || connection.connecting?
|
29
|
+
if connection.closed? || connection.connecting? || connection.suspended?
|
30
30
|
channel.transition_state_machine :detached, error
|
31
31
|
elsif can_transition_to?(:detached)
|
32
32
|
send_detach_protocol_message
|
@@ -59,7 +59,7 @@ module Ably::Realtime
|
|
59
59
|
def fail_messages_awaiting_ack(error)
|
60
60
|
# Allow a short time for other queued operations to complete before failing all messages
|
61
61
|
EventMachine.add_timer(0.1) do
|
62
|
-
error = Ably::Exceptions::
|
62
|
+
error = Ably::Exceptions::MessageDeliveryFailed.new("Channel cannot publish messages whilst state is '#{channel.state}'") unless error
|
63
63
|
fail_messages_in_queue connection.__pending_message_ack_queue__, error
|
64
64
|
fail_messages_in_queue connection.__outgoing_message_queue__, error
|
65
65
|
end
|
@@ -123,8 +123,16 @@ module Ably::Realtime
|
|
123
123
|
channel.transition_state_machine :detaching if can_transition_to?(:detaching)
|
124
124
|
end
|
125
125
|
|
126
|
+
connection.unsafe_on(:suspended) do |error|
|
127
|
+
if can_transition_to?(:detaching)
|
128
|
+
channel.transition_state_machine :detaching, Ably::Exceptions::ConnectionSuspended.new('Connection suspended', nil, 80002, error)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
126
132
|
connection.unsafe_on(:failed) do |error|
|
127
|
-
|
133
|
+
if can_transition_to?(:failed)
|
134
|
+
channel.transition_state_machine :failed, Ably::Exceptions::ConnectionFailed.new('Connection failed', nil, 80002, error)
|
135
|
+
end
|
128
136
|
end
|
129
137
|
end
|
130
138
|
|
data/lib/ably/realtime/client.rb
CHANGED
@@ -34,10 +34,14 @@ module Ably
|
|
34
34
|
# @return [Ably::Rest::Client]
|
35
35
|
attr_reader :rest_client
|
36
36
|
|
37
|
-
# When false the client suppresses messages originating from this connection being echoed back on the same connection.
|
37
|
+
# When false the client suppresses messages originating from this connection being echoed back on the same connection. Defaults to true
|
38
38
|
# @return [Boolean]
|
39
39
|
attr_reader :echo_messages
|
40
40
|
|
41
|
+
# If false, this disables the default behaviour whereby the library queues messages on a connection in the disconnected or connecting states. Defaults to true
|
42
|
+
# @return [Boolean]
|
43
|
+
attr_reader :queue_messages
|
44
|
+
|
41
45
|
# The custom realtime websocket host that is being used if it was provided with the option `:ws_host` when the {Client} was created
|
42
46
|
# @return [String,Nil]
|
43
47
|
attr_reader :custom_realtime_host
|
@@ -52,7 +56,8 @@ module Ably
|
|
52
56
|
|
53
57
|
def_delegators :auth, :client_id, :auth_options
|
54
58
|
def_delegators :@rest_client, :encoders
|
55
|
-
def_delegators :@rest_client, :
|
59
|
+
def_delegators :@rest_client, :use_tls?, :protocol, :protocol_binary?
|
60
|
+
def_delegators :@rest_client, :environment, :custom_host, :custom_port, :custom_tls_port
|
56
61
|
def_delegators :@rest_client, :log_level
|
57
62
|
|
58
63
|
# Creates a {Ably::Realtime::Client Realtime Client} and configures the {Ably::Auth} object for the connection.
|
@@ -75,9 +80,10 @@ module Ably
|
|
75
80
|
#
|
76
81
|
def initialize(options)
|
77
82
|
@rest_client = Ably::Rest::Client.new(options)
|
78
|
-
@auth =
|
83
|
+
@auth = Ably::Realtime::Auth.new(self)
|
79
84
|
@channels = Ably::Realtime::Channels.new(self)
|
80
85
|
@echo_messages = @rest_client.options.fetch(:echo_messages, true) == false ? false : true
|
86
|
+
@queue_messages = @rest_client.options.fetch(:queue_messages, true) == false ? false : true
|
81
87
|
@custom_realtime_host = @rest_client.options[:realtime_host] || @rest_client.options[:ws_host]
|
82
88
|
@auto_connect = @rest_client.options.fetch(:auto_connect, true) == false ? false : true
|
83
89
|
@recover = @rest_client.options[:recover]
|
@@ -174,12 +180,22 @@ module Ably
|
|
174
180
|
|
175
181
|
private
|
176
182
|
def endpoint_for_host(host)
|
177
|
-
|
183
|
+
port = if use_tls?
|
184
|
+
custom_tls_port
|
185
|
+
else
|
186
|
+
custom_port
|
187
|
+
end
|
188
|
+
|
189
|
+
raise ArgumentError, "Custom port must be an Integer or nil" if port && !port.kind_of?(Integer)
|
190
|
+
|
191
|
+
options = {
|
178
192
|
scheme: use_tls? ? 'wss' : 'ws',
|
179
193
|
host: host
|
180
|
-
|
194
|
+
}
|
195
|
+
options.merge!(port: port) if port
|
196
|
+
|
197
|
+
URI::Generic.build(options)
|
181
198
|
end
|
182
199
|
end
|
183
200
|
end
|
184
201
|
end
|
185
|
-
|
@@ -184,23 +184,30 @@ module Ably
|
|
184
184
|
# @return [EventMachine::Deferrable]
|
185
185
|
# @api private
|
186
186
|
def internet_up?
|
187
|
+
url = "http#{'s' if client.use_tls?}:#{Ably::INTERNET_CHECK.fetch(:url)}"
|
187
188
|
EventMachine::DefaultDeferrable.new.tap do |deferrable|
|
188
|
-
EventMachine::HttpRequest.new(
|
189
|
+
EventMachine::HttpRequest.new(url).get.tap do |http|
|
189
190
|
http.errback do
|
190
191
|
yield false if block_given?
|
191
|
-
deferrable.fail
|
192
|
+
deferrable.fail "Unable to connect to #{url}"
|
192
193
|
end
|
193
194
|
http.callback do
|
194
|
-
|
195
|
-
|
196
|
-
|
195
|
+
EventMachine.next_tick do
|
196
|
+
result = http.response_header.status == 200 && http.response.strip == Ably::INTERNET_CHECK.fetch(:ok_text)
|
197
|
+
yield result if block_given?
|
198
|
+
if result
|
199
|
+
deferrable.succeed
|
200
|
+
else
|
201
|
+
deferrable.fail "Unexpected response from #{url} (#{http.response_header.status})"
|
202
|
+
end
|
203
|
+
end
|
197
204
|
end
|
198
205
|
end
|
199
206
|
end
|
200
207
|
end
|
201
208
|
|
202
209
|
# @!attribute [r] recovery_key
|
203
|
-
#
|
210
|
+
# @return [String] recovery key that can be used by another client to recover this connection with the :recover option
|
204
211
|
def recovery_key
|
205
212
|
"#{key}:#{serial}" if connection_resumable?
|
206
213
|
end
|
@@ -275,7 +282,7 @@ module Ably
|
|
275
282
|
# @!attribute [r] port
|
276
283
|
# @return [Integer] The default port used for this connection
|
277
284
|
def port
|
278
|
-
client.use_tls? ? 443 : 80
|
285
|
+
client.use_tls? ? client.custom_tls_port || 443 : client.custom_port || 80
|
279
286
|
end
|
280
287
|
|
281
288
|
# @!attribute [r] logger
|
@@ -311,48 +318,51 @@ module Ably
|
|
311
318
|
__outgoing_protocol_msgbus__.publish :protocol_message, protocol_message
|
312
319
|
end
|
313
320
|
|
321
|
+
# @return [EventMachine::Deferrable]
|
314
322
|
# @api private
|
315
323
|
def create_websocket_transport
|
316
|
-
|
324
|
+
EventMachine::DefaultDeferrable.new.tap do |websocket_deferrable|
|
325
|
+
# Getting auth params can be blocking so uses a Deferrable
|
326
|
+
client.auth.auth_params.tap do |auth_deferrable|
|
327
|
+
auth_deferrable.callback do |auth_params|
|
328
|
+
url_params = auth_params.merge(
|
329
|
+
timestamp: as_since_epoch(Time.now),
|
330
|
+
format: client.protocol,
|
331
|
+
echo: client.echo_messages
|
332
|
+
)
|
333
|
+
|
334
|
+
if connection_resumable?
|
335
|
+
url_params.merge! resume: key, connection_serial: serial
|
336
|
+
logger.debug "Resuming connection key #{key} with serial #{serial}"
|
337
|
+
elsif connection_recoverable?
|
338
|
+
url_params.merge! recover: connection_recover_parts[:recover], connection_serial: connection_recover_parts[:connection_serial]
|
339
|
+
logger.debug "Recovering connection with key #{client.recover}"
|
340
|
+
once(:connected, :closed, :failed) do
|
341
|
+
client.disable_automatic_connection_recovery
|
342
|
+
end
|
343
|
+
end
|
317
344
|
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
logger.debug "Recovering connection with key #{client.recover}"
|
332
|
-
once(:connected, :closed, :failed) do
|
333
|
-
client.disable_automatic_connection_recovery
|
345
|
+
url = URI(client.endpoint).tap do |endpoint|
|
346
|
+
endpoint.query = URI.encode_www_form(url_params)
|
347
|
+
end.to_s
|
348
|
+
|
349
|
+
determine_host do |host|
|
350
|
+
begin
|
351
|
+
logger.debug "Connection: Opening socket connection to #{host}:#{port} and URL '#{url}'"
|
352
|
+
@transport = EventMachine.connect(host, port, WebsocketTransport, self, url) do |websocket_transport|
|
353
|
+
websocket_deferrable.succeed websocket_transport
|
354
|
+
end
|
355
|
+
rescue EventMachine::ConnectionError => error
|
356
|
+
websocket_deferrable.fail error
|
357
|
+
end
|
334
358
|
end
|
335
359
|
end
|
336
360
|
|
337
|
-
|
338
|
-
|
339
|
-
end
|
340
|
-
|
341
|
-
callback = proc do |url|
|
342
|
-
determine_host do |host|
|
343
|
-
begin
|
344
|
-
logger.debug "Connection: Opening socket connection to #{host}:#{port} and URL '#{url}'"
|
345
|
-
@transport = EventMachine.connect(host, port, WebsocketTransport, self, url) do |websocket_transport|
|
346
|
-
yield websocket_transport if block_given?
|
347
|
-
end
|
348
|
-
rescue EventMachine::ConnectionError => error
|
349
|
-
manager.connection_opening_failed error
|
361
|
+
auth_deferrable.errback do |error|
|
362
|
+
websocket_deferrable.fail error
|
350
363
|
end
|
351
364
|
end
|
352
365
|
end
|
353
|
-
|
354
|
-
# client.auth.auth_params is a blocking call, so defer this into a thread
|
355
|
-
EventMachine.defer blocking_operation, callback
|
356
366
|
end
|
357
367
|
|
358
368
|
# @api private
|
@@ -388,6 +398,13 @@ module Ably
|
|
388
398
|
resume_callbacks.delete(callback)
|
389
399
|
end
|
390
400
|
|
401
|
+
# Returns false if messages cannot be published as a result of message queueing being disabled
|
402
|
+
# @api private
|
403
|
+
def can_publish_messages?
|
404
|
+
connected? ||
|
405
|
+
( (initialized? || connecting? || disconnected?) && client.queue_messages )
|
406
|
+
end
|
407
|
+
|
391
408
|
# As we are using a state machine, do not allow change_state to be used
|
392
409
|
# #transition_state_machine must be used instead
|
393
410
|
private :change_state
|
@@ -451,8 +468,24 @@ module Ably
|
|
451
468
|
client.recover.to_s.match(RECOVER_REGEX)
|
452
469
|
end
|
453
470
|
|
471
|
+
def production?
|
472
|
+
client.environment.nil? || client.environment == :production
|
473
|
+
end
|
474
|
+
|
475
|
+
def custom_port?
|
476
|
+
if client.use_tls?
|
477
|
+
!!client.custom_tls_port
|
478
|
+
else
|
479
|
+
!!client.custom_port
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
def custom_host?
|
484
|
+
!!client.custom_realtime_host
|
485
|
+
end
|
486
|
+
|
454
487
|
def can_use_fallback_hosts?
|
455
|
-
if
|
488
|
+
if production? && !custom_port? && !custom_host?
|
456
489
|
if connecting? && previous_state
|
457
490
|
use_fallback_if_disconnected? || use_fallback_if_suspended?
|
458
491
|
end
|
@@ -53,20 +53,25 @@ module Ably::Realtime
|
|
53
53
|
end
|
54
54
|
|
55
55
|
unless client.auth.authentication_security_requirements_met?
|
56
|
-
connection.transition_state_machine :failed, Ably::Exceptions::
|
56
|
+
connection.transition_state_machine :failed, Ably::Exceptions::InsecureRequest.new('Cannot use Basic Auth over non-TLS connections', 401, 40103)
|
57
57
|
return
|
58
58
|
end
|
59
59
|
|
60
60
|
logger.debug 'ConnectionManager: Opening a websocket transport connection'
|
61
61
|
|
62
|
-
connection.create_websocket_transport do |
|
63
|
-
|
64
|
-
|
62
|
+
connection.create_websocket_transport.tap do |socket_deferrable|
|
63
|
+
socket_deferrable.callback do |websocket_transport|
|
64
|
+
subscribe_to_transport_events websocket_transport
|
65
|
+
yield websocket_transport if block_given?
|
66
|
+
end
|
67
|
+
socket_deferrable.errback do |error|
|
68
|
+
connection_opening_failed error
|
69
|
+
end
|
65
70
|
end
|
66
71
|
|
67
72
|
logger.debug "ConnectionManager: Setting up automatic connection timeout timer for #{TIMEOUTS.fetch(:open)}s"
|
68
73
|
create_timeout_timer_whilst_in_state(:connect, TIMEOUTS.fetch(:open)) do
|
69
|
-
connection_opening_failed Ably::Exceptions::
|
74
|
+
connection_opening_failed Ably::Exceptions::ConnectionTimeout.new("Connection to Ably timed out after #{TIMEOUTS.fetch(:open)}s", nil, 80014)
|
70
75
|
end
|
71
76
|
end
|
72
77
|
|
@@ -161,10 +166,10 @@ module Ably::Realtime
|
|
161
166
|
end
|
162
167
|
|
163
168
|
unless connection_retry_from_suspended_state?
|
164
|
-
return if connection_retry_for(:disconnected
|
169
|
+
return if connection_retry_for(:disconnected)
|
165
170
|
end
|
166
171
|
|
167
|
-
return if connection_retry_for(:suspended
|
172
|
+
return if connection_retry_for(:suspended)
|
168
173
|
|
169
174
|
# Fallback if no other criteria met
|
170
175
|
connection.transition_state_machine :failed, current_transition.metadata
|
@@ -264,10 +269,10 @@ module Ably::Realtime
|
|
264
269
|
#
|
265
270
|
# @return [Boolean] True if a connection attempt has been set up, false if no further connection attempts can be made for this state
|
266
271
|
#
|
267
|
-
def connection_retry_for(from_state
|
272
|
+
def connection_retry_for(from_state)
|
268
273
|
retry_params = CONNECT_RETRY_CONFIG.fetch(from_state)
|
269
274
|
|
270
|
-
if
|
275
|
+
if can_reattempt_connect_for_state?(from_state)
|
271
276
|
if retries_for_state(from_state, ignore_states: [:connecting]).empty?
|
272
277
|
logger.debug "ConnectionManager: Will attempt reconnect immediately as no previous reconnect attempts made in this state"
|
273
278
|
EventMachine.next_tick { connection.connect }
|
@@ -281,6 +286,14 @@ module Ably::Realtime
|
|
281
286
|
end
|
282
287
|
end
|
283
288
|
|
289
|
+
# True if the client library has not exceeded the configured max_time_in_state for the current State
|
290
|
+
# For example, if the state is disconnected, and has been in a cycle of disconnected > connect > disconnected
|
291
|
+
# so long as the time in this cycle of states is less than max_time_in_state, this will return true
|
292
|
+
def can_reattempt_connect_for_state?(state)
|
293
|
+
retry_params = CONNECT_RETRY_CONFIG.fetch(state)
|
294
|
+
time_spent_attempting_state(state, ignore_states: [:connecting]) < retry_params.fetch(:max_time_in_state)
|
295
|
+
end
|
296
|
+
|
284
297
|
# Returns a float representing the amount of time passed since the first consecutive attempt of this state
|
285
298
|
#
|
286
299
|
# @param (see #retries_for_state)
|
@@ -335,9 +348,13 @@ module Ably::Realtime
|
|
335
348
|
connection.transition_state_machine :closed
|
336
349
|
elsif !connection.closed? && !connection.disconnected?
|
337
350
|
exception = if reason
|
338
|
-
Ably::Exceptions::
|
351
|
+
Ably::Exceptions::ConnectionClosed.new(reason)
|
352
|
+
end
|
353
|
+
if connection_retry_from_suspended_state? || !can_reattempt_connect_for_state?(:disconnected)
|
354
|
+
connection.transition_state_machine :suspended, exception
|
355
|
+
else
|
356
|
+
connection.transition_state_machine :disconnected, exception
|
339
357
|
end
|
340
|
-
connection.transition_state_machine :disconnected, exception
|
341
358
|
end
|
342
359
|
end
|
343
360
|
end
|
@@ -350,35 +367,33 @@ module Ably::Realtime
|
|
350
367
|
end
|
351
368
|
|
352
369
|
@renewing_token = true
|
353
|
-
logger.
|
370
|
+
logger.info "ConnectionManager: Token has expired and is renewable, renewing token now"
|
354
371
|
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
rescue StandardError => auth_error
|
359
|
-
connection.transition_state_machine :failed, auth_error
|
360
|
-
nil
|
361
|
-
end
|
362
|
-
end
|
372
|
+
client.auth.authorise.tap do |authorise_deferrable|
|
373
|
+
authorise_deferrable.callback do |token_details|
|
374
|
+
logger.info 'ConnectionManager: Token renewed succesfully following expiration'
|
363
375
|
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
end
|
376
|
+
state_changed_callback = proc do
|
377
|
+
@renewing_token = false
|
378
|
+
connection.off &state_changed_callback
|
379
|
+
end
|
369
380
|
|
370
|
-
|
381
|
+
connection.unsafe_once :connected, :closed, :failed, &state_changed_callback
|
371
382
|
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
383
|
+
if token_details && !token_details.expired?
|
384
|
+
connection.connect
|
385
|
+
else
|
386
|
+
connection.transition_state_machine :failed, error unless connection.failed?
|
387
|
+
end
|
376
388
|
end
|
377
|
-
end
|
378
389
|
|
379
|
-
|
390
|
+
authorise_deferrable.errback do |auth_error|
|
391
|
+
logger.error "ConnectionManager: Error authorising following token expiry: #{auth_error}"
|
392
|
+
connection.transition_state_machine :failed, auth_error
|
393
|
+
end
|
394
|
+
end
|
380
395
|
else
|
381
|
-
logger.error "ConnectionManager: Token has expired and is not renewable"
|
396
|
+
logger.error "ConnectionManager: Token has expired and is not renewable - #{error}"
|
382
397
|
connection.transition_state_machine :failed, error
|
383
398
|
end
|
384
399
|
end
|
@@ -82,6 +82,7 @@ module Ably::Realtime
|
|
82
82
|
|
83
83
|
return deferrable_succeed(deferrable, &success_block) if state == STATE.Entered
|
84
84
|
|
85
|
+
ensure_presence_publishable_on_connection
|
85
86
|
ensure_channel_attached(deferrable) do
|
86
87
|
if entering?
|
87
88
|
once_or_if(STATE.Entered, else: proc { |args| deferrable_fail deferrable, *args }) do
|
@@ -145,6 +146,7 @@ module Ably::Realtime
|
|
145
146
|
|
146
147
|
return deferrable_succeed(deferrable, &success_block) if state == STATE.Left
|
147
148
|
|
149
|
+
ensure_presence_publishable_on_connection
|
148
150
|
ensure_channel_attached(deferrable) do
|
149
151
|
if leaving?
|
150
152
|
once_or_if(STATE.Left, else: proc { |error|deferrable_fail deferrable, *args }) do
|
@@ -201,6 +203,7 @@ module Ably::Realtime
|
|
201
203
|
|
202
204
|
@data = data
|
203
205
|
|
206
|
+
ensure_presence_publishable_on_connection
|
204
207
|
ensure_channel_attached(deferrable) do
|
205
208
|
send_protocol_message_and_transition_state_to(
|
206
209
|
Ably::Models::PresenceMessage::ACTION.Update,
|
@@ -369,6 +372,12 @@ module Ably::Realtime
|
|
369
372
|
end
|
370
373
|
end
|
371
374
|
|
375
|
+
def ensure_presence_publishable_on_connection
|
376
|
+
if !connection.can_publish_messages?
|
377
|
+
raise Ably::Exceptions::MessageQueueingDisabled.new("Message cannot be published. Client is configured to disallow queueing of messages and connection is currently #{connection.state}")
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
372
381
|
def ensure_channel_attached(deferrable = nil)
|
373
382
|
if channel.attached?
|
374
383
|
yield
|
@@ -413,11 +422,12 @@ module Ably::Realtime
|
|
413
422
|
safe_yield block, self, *args if block_given?
|
414
423
|
EventMachine.next_tick { deferrable.fail self, *args } # allow errback to be added to the returned Deferrable
|
415
424
|
deferrable
|
416
|
-
|
425
|
+
end
|
417
426
|
|
418
427
|
def send_presence_action_for_client(action, client_id, options = {}, &success_block)
|
419
|
-
|
428
|
+
ensure_presence_publishable_on_connection
|
420
429
|
|
430
|
+
deferrable = create_deferrable
|
421
431
|
ensure_channel_attached(deferrable) do
|
422
432
|
send_presence_protocol_message(action, client_id, options).tap do |protocol_message|
|
423
433
|
protocol_message.callback { |message| deferrable_succeed deferrable, &success_block }
|
@@ -428,7 +438,7 @@ module Ably::Realtime
|
|
428
438
|
|
429
439
|
def attach_channel_then
|
430
440
|
if channel.detached? || channel.failed?
|
431
|
-
raise Ably::Exceptions::
|
441
|
+
raise Ably::Exceptions::InvalidStateChange.new("Operation is not allowed when channel is in #{channel.state}", 400, 91001)
|
432
442
|
else
|
433
443
|
channel.unsafe_once(Channel::STATE.Attached) { yield }
|
434
444
|
channel.attach
|
@@ -439,6 +449,10 @@ module Ably::Realtime
|
|
439
449
|
channel.client
|
440
450
|
end
|
441
451
|
|
452
|
+
def connection
|
453
|
+
client.connection
|
454
|
+
end
|
455
|
+
|
442
456
|
def rest_presence
|
443
457
|
client.rest_client.channel(channel.name).presence
|
444
458
|
end
|