ably 1.1.2 → 1.1.6
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 +4 -4
- data/.github/workflows/check.yml +27 -0
- data/CHANGELOG.md +67 -0
- data/COPYRIGHT +1 -0
- data/LICENSE +172 -11
- data/MAINTAINERS.md +1 -0
- data/README.md +11 -21
- data/SPEC.md +1020 -922
- data/ably.gemspec +4 -4
- data/lib/ably/auth.rb +12 -2
- data/lib/ably/exceptions.rb +2 -2
- data/lib/ably/modules/ably.rb +11 -1
- data/lib/ably/realtime/channel.rb +7 -11
- data/lib/ably/realtime/channel/channel_manager.rb +2 -2
- data/lib/ably/realtime/channel/channel_properties.rb +24 -0
- data/lib/ably/realtime/client.rb +9 -0
- data/lib/ably/realtime/connection.rb +5 -4
- data/lib/ably/realtime/connection/websocket_transport.rb +67 -1
- data/lib/ably/realtime/presence.rb +0 -14
- data/lib/ably/rest/channel.rb +27 -19
- data/lib/ably/rest/client.rb +31 -15
- data/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +4 -1
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/auth_spec.rb +3 -3
- data/spec/acceptance/realtime/channel_spec.rb +10 -0
- data/spec/acceptance/realtime/client_spec.rb +72 -16
- data/spec/acceptance/realtime/connection_failures_spec.rb +26 -11
- data/spec/acceptance/realtime/connection_spec.rb +36 -17
- data/spec/acceptance/realtime/presence_history_spec.rb +0 -58
- data/spec/acceptance/realtime/presence_spec.rb +54 -0
- data/spec/acceptance/realtime/push_admin_spec.rb +3 -19
- data/spec/acceptance/rest/auth_spec.rb +6 -75
- data/spec/acceptance/rest/base_spec.rb +8 -4
- data/spec/acceptance/rest/channel_spec.rb +42 -4
- data/spec/acceptance/rest/client_spec.rb +121 -26
- data/spec/acceptance/rest/push_admin_spec.rb +3 -19
- data/spec/shared/client_initializer_behaviour.rb +131 -8
- data/spec/spec_helper.rb +1 -0
- data/spec/support/serialization_helper.rb +21 -0
- data/spec/support/test_app.rb +2 -2
- data/spec/unit/realtime/client_spec.rb +19 -6
- metadata +20 -15
- data/.travis.yml +0 -19
data/ably.gemspec
CHANGED
@@ -20,9 +20,9 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_runtime_dependency 'eventmachine', '~> 1.2.6'
|
22
22
|
spec.add_runtime_dependency 'em-http-request', '~> 1.1'
|
23
|
-
spec.add_runtime_dependency 'statesman', '~>
|
24
|
-
spec.add_runtime_dependency 'faraday', '~> 0
|
25
|
-
spec.add_runtime_dependency '
|
23
|
+
spec.add_runtime_dependency 'statesman', '~> 7.4'
|
24
|
+
spec.add_runtime_dependency 'faraday', '~> 1.0'
|
25
|
+
spec.add_runtime_dependency 'typhoeus', '~> 1.4'
|
26
26
|
|
27
27
|
if RUBY_VERSION.match(/^1\./)
|
28
28
|
spec.add_runtime_dependency 'json', '< 2.0'
|
@@ -46,7 +46,7 @@ Gem::Specification.new do |spec|
|
|
46
46
|
spec.add_development_dependency 'webmock', '2.2'
|
47
47
|
spec.add_development_dependency 'parallel_tests', '~> 2.9.0'
|
48
48
|
else
|
49
|
-
spec.add_development_dependency 'webmock', '~>
|
49
|
+
spec.add_development_dependency 'webmock', '~> 3.11'
|
50
50
|
spec.add_development_dependency 'coveralls'
|
51
51
|
spec.add_development_dependency 'parallel_tests', '~> 2.22'
|
52
52
|
if !RUBY_VERSION.match(/^2\.[0123]/)
|
data/lib/ably/auth.rb
CHANGED
@@ -103,7 +103,6 @@ module Ably
|
|
103
103
|
end
|
104
104
|
|
105
105
|
if has_client_id? && !token_creatable_externally? && !token_option
|
106
|
-
raise ArgumentError, 'client_id cannot be provided without a complete API key or means to authenticate. An API key is needed to automatically authenticate with Ably and obtain a token' unless api_key_present?
|
107
106
|
@client_id = ensure_utf_8(:client_id, client_id) if client_id
|
108
107
|
end
|
109
108
|
|
@@ -377,7 +376,7 @@ module Ably
|
|
377
376
|
# True when Token Auth is being used to authenticate with Ably
|
378
377
|
def using_token_auth?
|
379
378
|
return options[:use_token_auth] if options.has_key?(:use_token_auth)
|
380
|
-
!!(token_option || current_token_details ||
|
379
|
+
!!(token_option || current_token_details || token_creatable_externally?)
|
381
380
|
end
|
382
381
|
|
383
382
|
def client_id
|
@@ -408,6 +407,17 @@ module Ably
|
|
408
407
|
end
|
409
408
|
end
|
410
409
|
|
410
|
+
# Extra headers that may be used during authentication
|
411
|
+
#
|
412
|
+
# @return [Hash] headers
|
413
|
+
def extra_auth_headers
|
414
|
+
if client_id && using_basic_auth?
|
415
|
+
{ 'X-Ably-ClientId' => Base64.urlsafe_encode64(client_id) }
|
416
|
+
else
|
417
|
+
{}
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
411
421
|
# Auth params used in URI endpoint for Realtime connections
|
412
422
|
# Will reauthorize implicitly if required and capable
|
413
423
|
#
|
data/lib/ably/exceptions.rb
CHANGED
@@ -5,7 +5,7 @@ module Ably
|
|
5
5
|
TOKEN_EXPIRED_CODE = 40140..40149
|
6
6
|
|
7
7
|
# Base Ably exception class that contains status and code values used by Ably
|
8
|
-
# Refer to https://github.com/ably/ably-common/blob/
|
8
|
+
# Refer to https://github.com/ably/ably-common/blob/main/protocol/errors.json
|
9
9
|
#
|
10
10
|
# @!attribute [r] message
|
11
11
|
# @return [String] Error message from Ably
|
@@ -116,7 +116,7 @@ module Ably
|
|
116
116
|
class InvalidState < BaseAblyException; end
|
117
117
|
|
118
118
|
# A generic Ably exception taht supports a status & code.
|
119
|
-
# See https://github.com/ably/ably-common/blob/
|
119
|
+
# See https://github.com/ably/ably-common/blob/main/protocol/errors.json for a list of Ably errors
|
120
120
|
class Standard < BaseAblyException; end
|
121
121
|
|
122
122
|
# The HTTP request has returned a 500 error
|
data/lib/ably/modules/ably.rb
CHANGED
@@ -6,8 +6,18 @@
|
|
6
6
|
module Ably
|
7
7
|
# Fallback hosts to use when a connection to rest/realtime.ably.io is not possible due to
|
8
8
|
# network failures either at the client, between the client and Ably, within an Ably data center, or at the IO domain registrar
|
9
|
+
# see https://docs.ably.io/client-lib-development-guide/features/#RSC15a
|
9
10
|
#
|
10
|
-
|
11
|
+
FALLBACK_DOMAIN = 'ably-realtime.com'.freeze
|
12
|
+
FALLBACK_IDS = %w(a b c d e).freeze
|
13
|
+
|
14
|
+
# Default production fallbacks a.ably-realtime.com ... e.ably-realtime.com
|
15
|
+
FALLBACK_HOSTS = FALLBACK_IDS.map { |host| "#{host}.#{FALLBACK_DOMAIN}".freeze }.freeze
|
16
|
+
|
17
|
+
# Custom environment default fallbacks {ENV}-a-fallback.ably-realtime.com ... {ENV}-a-fallback.ably-realtime.com
|
18
|
+
CUSTOM_ENVIRONMENT_FALLBACKS_SUFFIXES = FALLBACK_IDS.map do |host|
|
19
|
+
"-#{host}-fallback.#{FALLBACK_DOMAIN}".freeze
|
20
|
+
end.freeze
|
11
21
|
|
12
22
|
INTERNET_CHECK = {
|
13
23
|
url: '//internet-up.ably-realtime.com/is-the-internet-up.txt',
|
@@ -79,6 +79,10 @@ module Ably
|
|
79
79
|
# @return [Hash]
|
80
80
|
attr_reader :options
|
81
81
|
|
82
|
+
# Properties of a channel and its state
|
83
|
+
# @return [{Ably::Realtime::Channel::ChannelProperties}]
|
84
|
+
attr_reader :properties
|
85
|
+
|
82
86
|
# When a channel failure occurs this attribute contains the Ably Exception
|
83
87
|
# @return [Ably::Models::ErrorInfo,Ably::Exceptions::BaseAblyException]
|
84
88
|
attr_reader :error_reason
|
@@ -88,11 +92,6 @@ module Ably
|
|
88
92
|
# @api private
|
89
93
|
attr_reader :manager
|
90
94
|
|
91
|
-
# Serial number assigned to this channel when it was attached
|
92
|
-
# @return [Integer]
|
93
|
-
# @api private
|
94
|
-
attr_reader :attached_serial
|
95
|
-
|
96
95
|
# Initialize a new Channel object
|
97
96
|
#
|
98
97
|
# @param client [Ably::Rest::Client]
|
@@ -112,6 +111,7 @@ module Ably
|
|
112
111
|
@state = STATE(state_machine.current_state)
|
113
112
|
@manager = ChannelManager.new(self, client.connection)
|
114
113
|
@push = PushChannel.new(self)
|
114
|
+
@properties = ChannelProperties.new(self)
|
115
115
|
|
116
116
|
setup_event_handlers
|
117
117
|
setup_presence
|
@@ -292,7 +292,7 @@ module Ably
|
|
292
292
|
error = Ably::Exceptions::InvalidRequest.new('option :until_attach is invalid as the channel is not attached' )
|
293
293
|
return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
|
294
294
|
end
|
295
|
-
options[:from_serial] =
|
295
|
+
options[:from_serial] = properties.attach_serial
|
296
296
|
end
|
297
297
|
|
298
298
|
async_wrap(callback) do
|
@@ -319,11 +319,6 @@ module Ably
|
|
319
319
|
@error_reason = nil
|
320
320
|
end
|
321
321
|
|
322
|
-
# @api private
|
323
|
-
def set_attached_serial(serial)
|
324
|
-
@attached_serial = serial
|
325
|
-
end
|
326
|
-
|
327
322
|
# @api private
|
328
323
|
def update_options(channel_options)
|
329
324
|
@options = channel_options.clone.freeze
|
@@ -372,3 +367,4 @@ end
|
|
372
367
|
require 'ably/realtime/channel/channel_manager'
|
373
368
|
require 'ably/realtime/channel/channel_state_machine'
|
374
369
|
require 'ably/realtime/channel/push_channel'
|
370
|
+
require 'ably/realtime/channel/channel_properties'
|
@@ -37,7 +37,7 @@ module Ably::Realtime
|
|
37
37
|
# library, such as returning to attached whne detach has failed
|
38
38
|
if attached_protocol_message
|
39
39
|
update_presence_sync_state_following_attached attached_protocol_message
|
40
|
-
channel.
|
40
|
+
channel.properties.set_attach_serial(attached_protocol_message.channel_serial)
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
@@ -76,7 +76,7 @@ module Ably::Realtime
|
|
76
76
|
update_presence_sync_state_following_attached protocol_message
|
77
77
|
end
|
78
78
|
|
79
|
-
channel.
|
79
|
+
channel.properties.set_attach_serial(protocol_message.channel_serial)
|
80
80
|
end
|
81
81
|
|
82
82
|
# Handle DETACED messages, see #RTL13 for server-initated detaches
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Ably::Realtime
|
2
|
+
class Channel
|
3
|
+
# Represents properties of a channel and its state
|
4
|
+
class ChannelProperties
|
5
|
+
# {Ably::Realtime::Channel} this object associated with
|
6
|
+
# @return [Ably::Realtime::Channel]
|
7
|
+
attr_reader :channel
|
8
|
+
|
9
|
+
# Contains the last channelSerial received in an ATTACHED ProtocolMesage for the channel, see RTL15a
|
10
|
+
#
|
11
|
+
# @return [String]
|
12
|
+
attr_reader :attach_serial
|
13
|
+
|
14
|
+
def initialize(channel)
|
15
|
+
@channel = channel
|
16
|
+
end
|
17
|
+
|
18
|
+
# @api private
|
19
|
+
def set_attach_serial(attach_serial)
|
20
|
+
@attach_serial = attach_serial
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/ably/realtime/client.rb
CHANGED
@@ -65,11 +65,16 @@ module Ably
|
|
65
65
|
# @return [String,Nil]
|
66
66
|
attr_reader :recover
|
67
67
|
|
68
|
+
# Additional parameters to be sent in the querystring when initiating a realtime connection
|
69
|
+
# @return [Hash]
|
70
|
+
attr_reader :transport_params
|
71
|
+
|
68
72
|
def_delegators :auth, :client_id, :auth_options
|
69
73
|
def_delegators :@rest_client, :encoders
|
70
74
|
def_delegators :@rest_client, :use_tls?, :protocol, :protocol_binary?
|
71
75
|
def_delegators :@rest_client, :environment, :custom_host, :custom_port, :custom_tls_port
|
72
76
|
def_delegators :@rest_client, :log_level
|
77
|
+
def_delegators :@rest_client, :options
|
73
78
|
|
74
79
|
# Creates a {Ably::Realtime::Client Realtime Client} and configures the {Ably::Auth} object for the connection.
|
75
80
|
#
|
@@ -82,6 +87,7 @@ module Ably
|
|
82
87
|
# @option options [Boolean] :echo_messages If false, prevents messages originating from this connection being echoed back on the same connection
|
83
88
|
# @option options [String] :recover When a recover option is specified a connection inherits the state of a previous connection that may have existed under a different instance of the Realtime library, please refer to the API documentation for further information on connection state recovery
|
84
89
|
# @option options [Boolean] :auto_connect By default as soon as the client library is instantiated it will connect to Ably. You can optionally set this to false and explicitly connect.
|
90
|
+
# @option options [Hash] :transport_params Additional parameters to be sent in the querystring when initiating a realtime connection. Keys are Strings, values are Stringifiable(a value must respond to #to_s)
|
85
91
|
#
|
86
92
|
# @option options [Integer] :channel_retry_timeout (15 seconds). When a channel becomes SUSPENDED, after this delay in seconds, the channel will automatically attempt to reattach if the connection is CONNECTED
|
87
93
|
# @option options [Integer] :disconnected_retry_timeout (15 seconds). When the connection enters the DISCONNECTED state, after this delay in seconds, if the state is still DISCONNECTED, the client library will attempt to reconnect automatically
|
@@ -109,6 +115,9 @@ module Ably
|
|
109
115
|
end
|
110
116
|
end
|
111
117
|
|
118
|
+
@transport_params = options.delete(:transport_params).to_h.each_with_object({}) do |(key, value), acc|
|
119
|
+
acc[key.to_s] = value.to_s
|
120
|
+
end
|
112
121
|
@rest_client = Ably::Rest::Client.new(options.merge(realtime_client: self))
|
113
122
|
@echo_messages = rest_client.options.fetch(:echo_messages, true) == false ? false : true
|
114
123
|
@queue_messages = rest_client.options.fetch(:queue_messages, true) == false ? false : true
|
@@ -431,10 +431,10 @@ module Ably
|
|
431
431
|
client.auth.auth_params.tap do |auth_deferrable|
|
432
432
|
auth_deferrable.callback do |auth_params|
|
433
433
|
url_params = auth_params.merge(
|
434
|
-
format
|
435
|
-
echo
|
436
|
-
v
|
437
|
-
lib
|
434
|
+
'format' => client.protocol,
|
435
|
+
'echo' => client.echo_messages,
|
436
|
+
'v' => Ably::PROTOCOL_VERSION,
|
437
|
+
'lib' => client.rest_client.lib_version_id,
|
438
438
|
)
|
439
439
|
|
440
440
|
# Use native websocket heartbeats if possible, but allow Ably protocol heartbeats
|
@@ -445,6 +445,7 @@ module Ably
|
|
445
445
|
end
|
446
446
|
|
447
447
|
url_params['clientId'] = client.auth.client_id if client.auth.has_client_id?
|
448
|
+
url_params.merge!(client.transport_params)
|
448
449
|
|
449
450
|
if connection_resumable?
|
450
451
|
url_params.merge! resume: key, connection_serial: serial
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
1
3
|
module Ably::Realtime
|
2
4
|
class Connection
|
3
5
|
# EventMachine WebSocket transport
|
@@ -16,10 +18,13 @@ module Ably::Realtime
|
|
16
18
|
)
|
17
19
|
include Ably::Modules::StateEmitter
|
18
20
|
|
21
|
+
attr_reader :host
|
22
|
+
|
19
23
|
def initialize(connection, url)
|
20
24
|
@connection = connection
|
21
25
|
@state = STATE.Initialized
|
22
26
|
@url = url
|
27
|
+
@host = URI.parse(url).hostname
|
23
28
|
|
24
29
|
setup_event_handlers
|
25
30
|
end
|
@@ -49,7 +54,7 @@ module Ably::Realtime
|
|
49
54
|
# Required {http://www.rubydoc.info/github/eventmachine/eventmachine/EventMachine/Connection EventMachine::Connection} interface
|
50
55
|
def connection_completed
|
51
56
|
change_state STATE.Connected
|
52
|
-
start_tls if client.use_tls?
|
57
|
+
start_tls(tls_opts) if client.use_tls?
|
53
58
|
driver.start
|
54
59
|
end
|
55
60
|
|
@@ -77,6 +82,51 @@ module Ably::Realtime
|
|
77
82
|
send_data(data)
|
78
83
|
end
|
79
84
|
|
85
|
+
# TLS verification support, original implementation by Mislav Marohnić:
|
86
|
+
#
|
87
|
+
# https://github.com/lostisland/faraday/commit/63cf47c95b573539f047c729bd9ad67560bc83ff
|
88
|
+
def ssl_verify_peer(cert_string)
|
89
|
+
cert = nil
|
90
|
+
begin
|
91
|
+
cert = OpenSSL::X509::Certificate.new(cert_string)
|
92
|
+
rescue OpenSSL::X509::CertificateError => e
|
93
|
+
disconnect_with_reason "Websocket host '#{host}' returned an invalid TLS certificate: #{e.message}"
|
94
|
+
return false
|
95
|
+
end
|
96
|
+
|
97
|
+
@last_seen_cert = cert
|
98
|
+
|
99
|
+
if certificate_store.verify(@last_seen_cert)
|
100
|
+
begin
|
101
|
+
certificate_store.add_cert(@last_seen_cert)
|
102
|
+
rescue OpenSSL::X509::StoreError => e
|
103
|
+
unless e.message == 'cert already in hash table'
|
104
|
+
disconnect_with_reason "Websocket host '#{host}' returned an invalid TLS certificate: #{e.message}"
|
105
|
+
return false
|
106
|
+
end
|
107
|
+
end
|
108
|
+
true
|
109
|
+
else
|
110
|
+
disconnect_with_reason "Websocket host '#{host}' returned an invalid TLS certificate"
|
111
|
+
false
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def ssl_handshake_completed
|
116
|
+
unless OpenSSL::SSL.verify_certificate_identity(@last_seen_cert, host)
|
117
|
+
disconnect_with_reason "Websocket host '#{host}' returned an invalid TLS certificate"
|
118
|
+
false
|
119
|
+
else
|
120
|
+
true
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def certificate_store
|
125
|
+
@certificate_store ||= OpenSSL::X509::Store.new.tap do |store|
|
126
|
+
store.set_default_paths
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
80
130
|
# True if socket connection is ready to be released
|
81
131
|
# i.e. it is not currently connecting or connected
|
82
132
|
def ready_for_release?
|
@@ -106,6 +156,12 @@ module Ably::Realtime
|
|
106
156
|
@connection
|
107
157
|
end
|
108
158
|
|
159
|
+
def disconnect_with_reason(reason)
|
160
|
+
client.logger.error { "WebsocketTransport: Disconnecting due to error: #{reason}" }
|
161
|
+
@reason_closed = reason
|
162
|
+
disconnect
|
163
|
+
end
|
164
|
+
|
109
165
|
def reason_closed
|
110
166
|
@reason_closed
|
111
167
|
end
|
@@ -214,6 +270,16 @@ module Ably::Realtime
|
|
214
270
|
end
|
215
271
|
)
|
216
272
|
end
|
273
|
+
|
274
|
+
# TLS options to pass to EventMachine::Connection#start_tls
|
275
|
+
#
|
276
|
+
# See https://www.rubydoc.info/github/eventmachine/eventmachine/EventMachine/Connection#start_tls-instance_method
|
277
|
+
def tls_opts
|
278
|
+
{
|
279
|
+
sni_hostname: host,
|
280
|
+
verify_peer: true,
|
281
|
+
}
|
282
|
+
end
|
217
283
|
end
|
218
284
|
end
|
219
285
|
end
|
@@ -278,28 +278,14 @@ module Ably::Realtime
|
|
278
278
|
|
279
279
|
# Return the presence messages history for the channel
|
280
280
|
#
|
281
|
-
# Once attached to a channel, you can retrieve presence message history on the channel before the
|
282
|
-
# channel was attached with the option <tt>until_attach: true</tt>. This is very useful for
|
283
|
-
# developers who wish to capture new presence events as well as retrieve historical presence state with
|
284
|
-
# the guarantee that no presence history has been missed.
|
285
|
-
#
|
286
281
|
# @param (see Ably::Rest::Presence#history)
|
287
282
|
# @option options (see Ably::Rest::Presence#history)
|
288
|
-
# @option options [Boolean] :until_attach When true, request for history will be limited only to messages published before the associated channel was attached. The associated channel must be attached.
|
289
283
|
#
|
290
284
|
# @yield [Ably::Models::PaginatedResult<Ably::Models::PresenceMessage>] First {Ably::Models::PaginatedResult page} of {Ably::Models::PresenceMessage} objects accessible with {Ably::Models::PaginatedResult#items #items}.
|
291
285
|
#
|
292
286
|
# @return [Ably::Util::SafeDeferrable]
|
293
287
|
#
|
294
288
|
def history(options = {}, &callback)
|
295
|
-
if options.delete(:until_attach)
|
296
|
-
unless channel.attached?
|
297
|
-
error = Ably::Exceptions::InvalidRequest.new('option :until_attach is invalid as the channel is not attached')
|
298
|
-
return Ably::Util::SafeDeferrable.new_and_fail_immediately(logger, error)
|
299
|
-
end
|
300
|
-
options[:from_serial] = channel.attached_serial
|
301
|
-
end
|
302
|
-
|
303
289
|
async_wrap(callback) do
|
304
290
|
rest_presence.history(options.merge(async_blocking_operations: true))
|
305
291
|
end
|
data/lib/ably/rest/channel.rb
CHANGED
@@ -40,21 +40,20 @@ module Ably
|
|
40
40
|
@push = PushChannel.new(self)
|
41
41
|
end
|
42
42
|
|
43
|
-
# Publish one or more messages to the channel.
|
44
|
-
#
|
45
|
-
# @param
|
46
|
-
# @param
|
47
|
-
# @param attributes [Hash, nil] Optional additional message attributes such as :extras, :id, :client_id or :connection_id, applied when name attribute is nil or a string
|
43
|
+
# Publish one or more messages to the channel. Three overloaded forms
|
44
|
+
# @param name [String, Array<Ably::Models::Message|Hash>, Ably::Models::Message, 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, or a single Ably::Model::Message object
|
45
|
+
# @param data [String, ByteArray, Hash, nil] The message payload unless an Array of [Ably::Model::Message] objects passed in the first argument, in which case an optional hash of query parameters
|
46
|
+
# @param attributes [Hash, nil] Optional additional message attributes such as :extras, :id, :client_id or :connection_id, applied when name attribute is nil or a string (Deprecated, will be removed in 2.0 in favour of constructing a Message object)
|
48
47
|
# @return [Boolean] true if the message was published, otherwise false
|
49
48
|
#
|
50
49
|
# @example
|
51
|
-
# # Publish a single message
|
50
|
+
# # Publish a single message with (name, data) form
|
52
51
|
# channel.publish 'click', { x: 1, y: 2 }
|
53
52
|
#
|
54
53
|
# # Publish an array of message Hashes
|
55
54
|
# messages = [
|
56
|
-
# { name: 'click', { x: 1, y: 2 } },
|
57
|
-
# { name: 'click', { x: 2, y: 3 } }
|
55
|
+
# { name: 'click', data: { x: 1, y: 2 } },
|
56
|
+
# { name: 'click', data: { x: 2, y: 3 } }
|
58
57
|
# ]
|
59
58
|
# channel.publish messages
|
60
59
|
#
|
@@ -65,17 +64,25 @@ module Ably
|
|
65
64
|
# ]
|
66
65
|
# channel.publish messages
|
67
66
|
#
|
68
|
-
|
69
|
-
|
70
|
-
|
67
|
+
# # Publish a single Ably::Models::Message object, with a query params
|
68
|
+
# # specifying quickAck: true
|
69
|
+
# message = Ably::Models::Message(name: 'click', { x: 1, y: 2 })
|
70
|
+
# channel.publish message, quickAck: 'true'
|
71
|
+
#
|
72
|
+
def publish(first, second = nil, third = {})
|
73
|
+
messages, qs_params = if first.kind_of?(Enumerable)
|
74
|
+
# ([Message], qs_params) form
|
75
|
+
[first, second]
|
76
|
+
elsif first.kind_of?(Ably::Models::Message)
|
77
|
+
# (Message, qs_params) form
|
78
|
+
[[first], second]
|
71
79
|
else
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
[{ name: name, data: data }.merge(attributes)]
|
80
|
+
# (name, data, attributes) form
|
81
|
+
first = ensure_utf_8(:name, first, allow_nil: true)
|
82
|
+
ensure_supported_payload second
|
83
|
+
# RSL1h - attributes as an extra method parameter is extra-spec but need to
|
84
|
+
# keep it for backcompat until version 2
|
85
|
+
[[{ name: first, data: second }.merge(third)], nil]
|
79
86
|
end
|
80
87
|
|
81
88
|
payload = messages.each_with_index.map do |message, index|
|
@@ -103,7 +110,8 @@ module Ably
|
|
103
110
|
end
|
104
111
|
end
|
105
112
|
|
106
|
-
|
113
|
+
options = qs_params ? { qs_params: qs_params } : {}
|
114
|
+
response = client.post("#{base_path}/publish", payload.length == 1 ? payload.first : payload, options)
|
107
115
|
|
108
116
|
[201, 204].include?(response.status)
|
109
117
|
end
|