ably 1.1.8 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -0
  3. data/README.md +1 -1
  4. data/UPDATING.md +30 -0
  5. data/lib/ably/auth.rb +3 -3
  6. data/lib/ably/models/channel_options.rb +97 -0
  7. data/lib/ably/models/idiomatic_ruby_wrapper.rb +4 -0
  8. data/lib/ably/models/message.rb +4 -4
  9. data/lib/ably/models/protocol_message.rb +17 -5
  10. data/lib/ably/models/token_details.rb +7 -2
  11. data/lib/ably/modules/channels_collection.rb +22 -2
  12. data/lib/ably/modules/conversions.rb +34 -0
  13. data/lib/ably/realtime/channel/channel_manager.rb +16 -4
  14. data/lib/ably/realtime/channel/channel_state_machine.rb +5 -0
  15. data/lib/ably/realtime/channel.rb +54 -24
  16. data/lib/ably/realtime/channels.rb +1 -1
  17. data/lib/ably/rest/channel.rb +26 -34
  18. data/lib/ably/rest/client.rb +4 -1
  19. data/lib/ably/util/crypto.rb +1 -1
  20. data/lib/ably/version.rb +2 -2
  21. data/spec/acceptance/realtime/channel_spec.rb +247 -21
  22. data/spec/acceptance/realtime/channels_spec.rb +59 -7
  23. data/spec/acceptance/realtime/connection_spec.rb +1 -1
  24. data/spec/acceptance/realtime/message_spec.rb +77 -0
  25. data/spec/acceptance/rest/auth_spec.rb +18 -0
  26. data/spec/acceptance/rest/channel_spec.rb +1 -1
  27. data/spec/acceptance/rest/channels_spec.rb +22 -5
  28. data/spec/acceptance/rest/client_spec.rb +2 -2
  29. data/spec/acceptance/rest/message_spec.rb +61 -3
  30. data/spec/lib/unit/models/channel_options_spec.rb +52 -0
  31. data/spec/unit/models/message_spec.rb +14 -0
  32. data/spec/unit/models/protocol_message_spec.rb +53 -7
  33. data/spec/unit/models/token_details_spec.rb +14 -0
  34. data/spec/unit/realtime/channels_spec.rb +52 -14
  35. data/spec/unit/rest/channels_spec.rb +81 -14
  36. metadata +21 -3
@@ -28,21 +28,20 @@ module Ably
28
28
  #
29
29
  # @param client [Ably::Rest::Client]
30
30
  # @param name [String] The name of the channel
31
- # @param channel_options [Hash] Channel options, currently reserved for Encryption options
32
- # @option channel_options [Hash,Ably::Models::CipherParams] :cipher A hash of options or a {Ably::Models::CipherParams} to configure the encryption. *:key* is required, all other options are optional. See {Ably::Util::Crypto#initialize} for a list of +:cipher+ options
31
+ # @param channel_options [Hash, Ably::Models::ChannelOptions] A hash of options or a {Ably::Models::ChannelOptions}
33
32
  #
34
33
  def initialize(client, name, channel_options = {})
35
34
  name = (ensure_utf_8 :name, name)
36
35
 
37
- update_options channel_options
36
+ @options = Ably::Models::ChannelOptions(channel_options)
38
37
  @client = client
39
38
  @name = name
40
39
  @push = PushChannel.new(self)
41
40
  end
42
41
 
43
- # Publish one or more messages to the channel. Three overloaded forms
42
+ # Publish one or more messages to the channel. Five overloaded forms
44
43
  # @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
44
+ # @param data [String, Array, 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
45
  # @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)
47
46
  # @return [Boolean] true if the message was published, otherwise false
48
47
  #
@@ -50,42 +49,33 @@ module Ably
50
49
  # # Publish a single message with (name, data) form
51
50
  # channel.publish 'click', { x: 1, y: 2 }
52
51
  #
53
- # # Publish an array of message Hashes
52
+ # # Publish a single message with single Hash form
53
+ # message = { name: 'click', data: { x: 1, y: 2 } }
54
+ # channel.publish message
55
+ #
56
+ # # Publish an array of message Hashes form
54
57
  # messages = [
55
58
  # { name: 'click', data: { x: 1, y: 2 } },
56
59
  # { name: 'click', data: { x: 2, y: 3 } }
57
60
  # ]
58
61
  # channel.publish messages
59
62
  #
60
- # # Publish an array of Ably::Models::Message objects
63
+ # # Publish an array of Ably::Models::Message objects form
61
64
  # messages = [
62
- # Ably::Models::Message(name: 'click', { x: 1, y: 2 })
63
- # Ably::Models::Message(name: 'click', { x: 2, y: 3 })
65
+ # Ably::Models::Message(name: 'click', data: { x: 1, y: 2 })
66
+ # Ably::Models::Message(name: 'click', data: { x: 2, y: 3 })
64
67
  # ]
65
68
  # channel.publish messages
66
69
  #
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'
70
+ # # Publish a single Ably::Models::Message object form
71
+ # message = Ably::Models::Message(name: 'click', data: { x: 1, y: 2 })
72
+ # channel.publish message
71
73
  #
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]
79
- else
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]
86
- end
74
+ def publish(name, data = nil, attributes = {})
75
+ qs_params = nil
76
+ qs_params = data if name.kind_of?(Enumerable) || name.kind_of?(Ably::Models::Message)
87
77
 
88
- messages.map! { |message| Ably::Models::Message(message.dup) }
78
+ messages = build_messages(name, data, attributes) # (RSL1a, RSL1b)
89
79
 
90
80
  if messages.sum(&:size) > (max_message_size = client.max_message_size || Ably::Rest::Client::MAX_MESSAGE_SIZE)
91
81
  raise Ably::Exceptions::MaxMessageSizeExceeded.new("Maximum message size exceeded #{max_message_size} bytes.")
@@ -163,14 +153,16 @@ module Ably
163
153
  @presence ||= Presence.new(client, self)
164
154
  end
165
155
 
166
- # @api private
167
- def update_options(channel_options)
168
- @options = channel_options.clone.freeze
156
+ # Sets or updates the stored channel options. (#RSL7)
157
+ # @param channel_options [Hash, Ably::Models::ChannelOptions] A hash of options or a {Ably::Models::ChannelOptions}
158
+ # @return [Ably::Models::ChannelOptions]
159
+ def set_options(channel_options)
160
+ @options = Ably::Models::ChannelOptions(channel_options)
169
161
  end
170
- alias set_options update_options # (RSL7)
171
- alias options= update_options
162
+ alias options= set_options
172
163
 
173
164
  private
165
+
174
166
  def base_path
175
167
  "/channels/#{URI.encode_www_form_component(name)}"
176
168
  end
@@ -199,10 +199,13 @@ module Ably
199
199
  @custom_tls_port = options.delete(:tls_port)
200
200
  @add_request_ids = options.delete(:add_request_ids)
201
201
  @log_retries_as_info = options.delete(:log_retries_as_info)
202
- @idempotent_rest_publishing = options.delete(:idempotent_rest_publishing) || Ably.major_minor_version_numeric > 1.1
203
202
  @max_message_size = options.delete(:max_message_size) || MAX_MESSAGE_SIZE
204
203
  @max_frame_size = options.delete(:max_frame_size) || MAX_FRAME_SIZE
205
204
 
205
+ if (@idempotent_rest_publishing = options.delete(:idempotent_rest_publishing)).nil?
206
+ @idempotent_rest_publishing = Ably::PROTOCOL_VERSION.to_f > 1.1
207
+ end
208
+
206
209
  if options[:fallback_hosts_use_default] && options[:fallback_hosts]
207
210
  raise ArgumentError, "fallback_hosts_use_default cannot be set to try when fallback_hosts is also provided"
208
211
  end
@@ -30,7 +30,7 @@ module Ably::Util
30
30
  # crypto.decrypt(decrypted) # => 'secret text'
31
31
  #
32
32
  def initialize(params)
33
- @fixed_iv = params.delete(:fixed_iv) if params.kind_of?(Hash)
33
+ @fixed_iv = params[:fixed_iv]
34
34
  @cipher_params = Ably::Models::CipherParams(params)
35
35
  end
36
36
 
data/lib/ably/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Ably
2
- VERSION = '1.1.8'
3
- PROTOCOL_VERSION = '1.1'
2
+ VERSION = '1.2.0'
3
+ PROTOCOL_VERSION = '1.2'
4
4
 
5
5
  # @api private
6
6
  def self.major_minor_version_numeric
@@ -117,6 +117,94 @@ describe Ably::Realtime::Channel, :event_machine do
117
117
  end
118
118
  end
119
119
 
120
+ context 'context when channel options contain modes' do
121
+ before do
122
+ channel.options = { modes: %i[publish] }
123
+ end
124
+
125
+ it 'sends an ATTACH with options as flags (#RTL4l)' do
126
+ connection.once(:connected) do
127
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
128
+ next if protocol_message.action != :attach
129
+
130
+ expect(protocol_message.has_attach_publish_flag?).to eq(true)
131
+ stop_reactor
132
+ end
133
+
134
+ channel.attach
135
+ end
136
+ end
137
+
138
+ context 'when channel is reattaching' do
139
+ it 'sends ATTACH_RESUME flag along with other modes (RTL4j)' do
140
+ channel.attach do
141
+ channel.on(:suspended) do
142
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
143
+ next if protocol_message.action != :attach
144
+
145
+ expect(protocol_message.has_attach_resume_flag?).to eq(true)
146
+ expect(protocol_message.has_attach_publish_flag?).to eq(true)
147
+ stop_reactor
148
+ end
149
+
150
+ client.connection.connect
151
+ end
152
+ client.connection.transition_state_machine :suspended
153
+ end
154
+ end
155
+ end
156
+ end
157
+
158
+ context 'context when channel options contain params' do
159
+ let(:params) do
160
+ { foo: 'foo', bar: 'bar'}
161
+ end
162
+
163
+ before do
164
+ channel.options = { params: params }
165
+ end
166
+
167
+ it 'sends an ATTACH with params (#RTL4k)' do
168
+ connection.once(:connected) do
169
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
170
+ next if protocol_message.action != :attach
171
+
172
+ expect(protocol_message.params).to eq(params)
173
+ stop_reactor
174
+ end
175
+
176
+ channel.attach
177
+ end
178
+ end
179
+ end
180
+
181
+ context 'when received attached' do
182
+ it 'decodes flags and sets it as modes on channel options (#RTL4m)'do
183
+ channel.on(:attached) do
184
+ expect(channel.options.modes.map(&:to_sym)).to eq(%i[subscribe])
185
+ stop_reactor
186
+ end
187
+
188
+ channel.transition_state_machine(:attaching)
189
+ attached_message = Ably::Models::ProtocolMessage.new(action: 11, channel: channel_name, flags: 262144)
190
+ client.connection.__incoming_protocol_msgbus__.publish :protocol_message, attached_message
191
+ end
192
+
193
+ it 'set params as channel options params (#RTL4k1)' do
194
+ params = { param: :something }
195
+
196
+ channel.on(:attached) do
197
+ expect(channel.params).to eq(channel.options.params)
198
+ expect(channel.params).to eq(params)
199
+ stop_reactor
200
+ end
201
+
202
+ channel.transition_state_machine(:attaching)
203
+ attached_message = Ably::Models::ProtocolMessage.new(action: 11, channel: channel_name, params: params)
204
+ client.connection.__incoming_protocol_msgbus__.publish :protocol_message, attached_message
205
+ end
206
+ end
207
+
120
208
  it 'implicitly attaches the channel (#RTL7c)' do
121
209
  expect(channel).to be_initialized
122
210
  channel.subscribe { |message| }
@@ -368,6 +456,42 @@ describe Ably::Realtime::Channel, :event_machine do
368
456
  end
369
457
  end
370
458
  end
459
+
460
+ describe 'clean attach (RTL4j)' do
461
+ context "when channel wasn't previously attached" do
462
+ it "doesn't send ATTACH_RESUME" do
463
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
464
+ next if protocol_message.action != :attach
465
+
466
+ expect(protocol_message.has_attach_resume_flag?).to eq(false)
467
+ stop_reactor
468
+ end
469
+
470
+ channel.attach
471
+ end
472
+ end
473
+
474
+ context "when channel was explicitly detached" do
475
+ it "doesn't send ATTACH_RESUME" do
476
+ channel.once(:detached) do
477
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
478
+ next if protocol_message.action != :attach
479
+
480
+ expect(protocol_message.has_attach_resume_flag?).to eq(false)
481
+ stop_reactor
482
+ end
483
+
484
+ channel.attach
485
+ end
486
+
487
+ channel.once(:attached) do
488
+ channel.detach
489
+ end
490
+
491
+ channel.attach
492
+ end
493
+ end
494
+ end
371
495
  end
372
496
 
373
497
  describe '#detach' do
@@ -1936,15 +2060,33 @@ describe Ably::Realtime::Channel, :event_machine do
1936
2060
  end
1937
2061
  end
1938
2062
 
1939
- it 'transitions state automatically to :attaching once the connection is re-established (#RTN15c3)' do
1940
- channel.attach do
1941
- channel.on(:suspended) do
1942
- client.connection.connect
1943
- channel.once(:attached) do
1944
- stop_reactor
2063
+ describe 'reattaching (#RTN15c3)' do
2064
+ it 'transitions state automatically to :attaching once the connection is re-established ' do
2065
+ channel.attach do
2066
+ channel.on(:suspended) do
2067
+ client.connection.connect
2068
+ channel.once(:attached) do
2069
+ stop_reactor
2070
+ end
1945
2071
  end
2072
+ client.connection.transition_state_machine :suspended
2073
+ end
2074
+ end
2075
+
2076
+ it 'sends ATTACH_RESUME flag when reattaching (RTL4j)' do
2077
+ channel.attach do
2078
+ channel.on(:suspended) do
2079
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
2080
+ next if protocol_message.action != :attach
2081
+
2082
+ expect(protocol_message.has_attach_resume_flag?).to eq(true)
2083
+ stop_reactor
2084
+ end
2085
+
2086
+ client.connection.connect
2087
+ end
2088
+ client.connection.transition_state_machine :suspended
1946
2089
  end
1947
- client.connection.transition_state_machine :suspended
1948
2090
  end
1949
2091
  end
1950
2092
  end
@@ -2134,6 +2276,68 @@ describe Ably::Realtime::Channel, :event_machine do
2134
2276
  end
2135
2277
  end
2136
2278
 
2279
+ describe '#set_options (#RTL16a)' do
2280
+ let(:modes) { %i[subscribe] }
2281
+ let(:channel_options) do
2282
+ { modes: modes }
2283
+ end
2284
+
2285
+ def self.build_flags(flags)
2286
+ flags.map { |flag| Ably::Models::ProtocolMessage::ATTACH_FLAGS_MAPPING[flag] }.reduce(:|)
2287
+ end
2288
+
2289
+ shared_examples 'an update that sends ATTACH message' do |state, flags|
2290
+ it 'sends an ATTACH message on options change' do
2291
+ attach_sent = nil
2292
+
2293
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
2294
+ if protocol_message.action == :attach && protocol_message.flags.nonzero?
2295
+ attach_sent = true
2296
+ expect(protocol_message.flags).to eq(flags)
2297
+ end
2298
+ end
2299
+
2300
+ channel.once(state) do
2301
+ channel.options = channel_options
2302
+ end
2303
+
2304
+ channel.on(:attached) do
2305
+ client.connection.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
2306
+ next if protocol_message.action != :attached
2307
+
2308
+ expect(attach_sent).to eq(true)
2309
+ stop_reactor
2310
+ end
2311
+ end
2312
+
2313
+ channel.attach
2314
+ end
2315
+ end
2316
+
2317
+ context 'when channel is attaching' do
2318
+ it_behaves_like 'an update that sends ATTACH message', :attaching, build_flags(%i[subscribe])
2319
+ end
2320
+
2321
+ context 'when channel is attaching' do
2322
+ it_behaves_like 'an update that sends ATTACH message', :attached, build_flags(%i[resume subscribe])
2323
+ end
2324
+
2325
+ context 'when channel is initialized' do
2326
+ it "doesn't send ATTACH message" do
2327
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
2328
+ raise "Unexpected message" if protocol_message.action == :attach
2329
+ end
2330
+
2331
+ channel.options = channel_options
2332
+ expect(channel.options.modes.map(&:to_sym)).to eq(modes)
2333
+
2334
+ EventMachine.next_tick do
2335
+ stop_reactor
2336
+ end
2337
+ end
2338
+ end
2339
+ end
2340
+
2137
2341
  context 'channel state change' do
2138
2342
  it 'emits a ChannelStateChange object' do
2139
2343
  channel.on(:attached) do |channel_state_change|
@@ -2375,11 +2579,20 @@ describe Ably::Realtime::Channel, :event_machine do
2375
2579
  end
2376
2580
 
2377
2581
  context 'and channel is attached' do
2378
- it 'reattaches immediately (#RTL13a)' do
2379
- channel.attach do
2582
+ it 'reattaches immediately (#RTL13a) with ATTACH_RESUME flag(RTL4j)' do
2583
+ resume_flag = false
2584
+
2585
+ channel.once(:attached) do
2586
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
2587
+ next if protocol_message.action != :attach
2588
+
2589
+ resume_flag = protocol_message.has_attach_resume_flag?
2590
+ end
2591
+
2380
2592
  channel.once(:attaching) do |state_change|
2381
2593
  expect(state_change.reason.code).to eql(50505)
2382
2594
  channel.once(:attached) do
2595
+ expect(resume_flag).to eq(true)
2383
2596
  stop_reactor
2384
2597
  end
2385
2598
  end
@@ -2387,26 +2600,39 @@ describe Ably::Realtime::Channel, :event_machine do
2387
2600
  detach_message = Ably::Models::ProtocolMessage.new(action: detached_action, channel: channel_name, error: { code: 50505 })
2388
2601
  client.connection.__incoming_protocol_msgbus__.publish :protocol_message, detach_message
2389
2602
  end
2603
+
2604
+ channel.attach
2390
2605
  end
2391
2606
  end
2392
2607
 
2393
2608
  context 'and channel is suspended' do
2394
- it 'reattaches immediately (#RTL13a)' do
2395
- channel.attach do
2396
- channel.once(:suspended) do
2397
- channel.once(:attaching) do |state_change|
2398
- expect(state_change.reason.code).to eql(50505)
2399
- channel.once(:attached) do
2400
- stop_reactor
2401
- end
2402
- end
2609
+ it 'reattaches immediately (#RTL13a) with ATTACH_RESUME flag(RTL4j)' do
2610
+ resume_flag = false
2403
2611
 
2404
- detach_message = Ably::Models::ProtocolMessage.new(action: detached_action, channel: channel_name, error: { code: 50505 })
2405
- client.connection.__incoming_protocol_msgbus__.publish :protocol_message, detach_message
2612
+ channel.once(:attached) do
2613
+ channel.transition_state_machine! :suspended
2614
+ end
2615
+
2616
+ channel.once(:suspended) do
2617
+ client.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
2618
+ next if protocol_message.action != :attach
2619
+
2620
+ resume_flag = protocol_message.has_attach_resume_flag?
2406
2621
  end
2407
2622
 
2408
- channel.transition_state_machine! :suspended
2623
+ channel.once(:attaching) do |state_change|
2624
+ expect(state_change.reason.code).to eql(50505)
2625
+ channel.once(:attached) do
2626
+ expect(resume_flag).to eq(true)
2627
+ stop_reactor
2628
+ end
2629
+ end
2630
+
2631
+ detach_message = Ably::Models::ProtocolMessage.new(action: detached_action, channel: channel_name, error: { code: 50505 })
2632
+ client.connection.__incoming_protocol_msgbus__.publish :protocol_message, detach_message
2409
2633
  end
2634
+
2635
+ channel.attach
2410
2636
  end
2411
2637
 
2412
2638
  context 'when connection is no longer connected' do
@@ -10,17 +10,56 @@ describe Ably::Realtime::Channels, :event_machine do
10
10
  end
11
11
 
12
12
  it 'returns channel object and passes the provided options' do
13
- expect(channel_with_options.options).to eql(options)
13
+ expect(channel_options).to be_a(Ably::Models::ChannelOptions)
14
+ expect(channel_options.to_h).to eq(options)
14
15
  stop_reactor
15
16
  end
16
17
  end
17
18
 
18
19
  vary_by_protocol do
20
+ let(:client_options) do
21
+ { key: api_key, environment: environment, protocol: protocol }
22
+ end
19
23
  let(:client) do
20
- auto_close Ably::Realtime::Client.new(key: api_key, environment: environment, protocol: protocol)
24
+ auto_close Ably::Realtime::Client.new(client_options)
21
25
  end
22
26
  let(:channel_name) { random_str }
23
- let(:options) { { key: 'value' } }
27
+ let(:options) do
28
+ { params: { key: 'value' } }
29
+ end
30
+
31
+ subject(:channel_options) { channel_with_options.options }
32
+
33
+ context 'when channel supposed to trigger reattachment per RTL16a (#RTS3c1)' do
34
+ it 'will raise an error' do
35
+ channel = client.channels.get(channel_name, options)
36
+
37
+ channel.on(:attached) do
38
+ expect { client.channels.get(channel_name, { modes: [] }) }.to raise_error ArgumentError, /use Channel#set_options directly/
39
+ stop_reactor
40
+ end
41
+
42
+ channel.attach
43
+ end
44
+
45
+ context 'params keys are the same but values are different' do
46
+ let(:options) do
47
+ { params: { x: '1' } }
48
+ end
49
+
50
+ it 'will raise an error' do
51
+ channel = client.channels.get(channel_name, options)
52
+
53
+ channel.on(:attached) do
54
+ expect { client.channels.get(channel_name, { params: { x: '2' } }) }.to raise_error ArgumentError, /use Channel#set_options directly/
55
+
56
+ stop_reactor
57
+ end
58
+
59
+ channel.attach
60
+ end
61
+ end
62
+ end
24
63
 
25
64
  describe 'using shortcut method #channel on the client object' do
26
65
  let(:channel) { client.channel(channel_name) }
@@ -35,26 +74,39 @@ describe Ably::Realtime::Channels, :event_machine do
35
74
  end
36
75
 
37
76
  describe 'accessing an existing channel object with different options' do
77
+ let(:client_options) { super().merge(logger: custom_logger_object) }
78
+ let(:custom_logger_object) { TestLogger.new }
38
79
  let(:new_channel_options) { { encrypted: true } }
39
- let(:original_channel) { client.channels.get(channel_name, options) }
80
+ let!(:original_channel) { client.channels.get(channel_name, options) }
40
81
 
41
82
  it 'overrides the existing channel options and returns the channel object' do
42
- expect(original_channel.options).to_not include(:encrypted)
83
+ expect(original_channel.options.to_h).to_not include(:encrypted)
43
84
  new_channel = client.channels.get(channel_name, new_channel_options)
44
85
  expect(new_channel).to be_a(Ably::Realtime::Channel)
45
86
  expect(new_channel.options[:encrypted]).to eql(true)
46
87
  stop_reactor
47
88
  end
89
+
90
+ it 'shows deprecation warning' do
91
+ client.channels.get(channel_name, new_channel_options)
92
+
93
+ warning = custom_logger_object.logs.find do |severity, message|
94
+ message.match(/Using this method to update channel options is deprecated and may be removed/)
95
+ end
96
+
97
+ expect(warning).to_not be_nil
98
+ stop_reactor
99
+ end
48
100
  end
49
101
 
50
102
  describe 'accessing an existing channel object without specifying any channel options' do
51
103
  let(:original_channel) { client.channels.get(channel_name, options) }
52
104
 
53
105
  it 'returns the existing channel without modifying the channel options' do
54
- expect(original_channel.options).to eql(options)
106
+ expect(original_channel.options.to_h).to eq(options)
55
107
  new_channel = client.channels.get(channel_name)
56
108
  expect(new_channel).to be_a(Ably::Realtime::Channel)
57
- expect(original_channel.options).to eql(options)
109
+ expect(original_channel.options.to_h).to eq(options)
58
110
  stop_reactor
59
111
  end
60
112
  end
@@ -2083,7 +2083,7 @@ describe Ably::Realtime::Connection, :event_machine do
2083
2083
  it 'sends the protocol version param v (#G4, #RTN2f)' do
2084
2084
  expect(EventMachine).to receive(:connect) do |host, port, transport, object, url|
2085
2085
  uri = URI.parse(url)
2086
- expect(CGI::parse(uri.query)['v'][0]).to eql('1.1')
2086
+ expect(CGI::parse(uri.query)['v'][0]).to eql('1.2')
2087
2087
  stop_reactor
2088
2088
  end
2089
2089
  client
@@ -75,6 +75,83 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
75
75
  end
76
76
  end
77
77
 
78
+ context 'a single Message object (#RSL1a)' do
79
+ let(:name) { random_str }
80
+ let(:data) { random_str }
81
+ let(:message) { Ably::Models::Message.new(name: name, data: data) }
82
+
83
+ it 'publishes the message' do
84
+ channel.attach
85
+ channel.publish(message)
86
+ channel.subscribe do |msg|
87
+ expect(msg.name).to eq(message.name)
88
+ expect(msg.data).to eq(message.data)
89
+ stop_reactor
90
+ end
91
+ end
92
+ end
93
+
94
+ context 'an array of Message objects (#RSL1a)' do
95
+ let(:data) { random_str }
96
+ let(:message1) { Ably::Models::Message.new(name: random_str, data: data) }
97
+ let(:message2) { Ably::Models::Message.new(name: random_str, data: data) }
98
+ let(:message3) { Ably::Models::Message.new(name: random_str, data: data) }
99
+
100
+ it 'publishes three messages' do
101
+ channel.attach
102
+ channel.publish([message1, message2, message3])
103
+ counter = 0
104
+ channel.subscribe do |message|
105
+ counter += 1
106
+ expect(message.data).to eq(data)
107
+ expect(message.name).to eq(message1.name) if counter == 1
108
+ expect(message.name).to eq(message2.name) if counter == 2
109
+ if counter == 3
110
+ expect(message.name).to eq(message3.name)
111
+ stop_reactor
112
+ end
113
+ end
114
+ end
115
+ end
116
+
117
+ context 'an array of hashes (#RSL1a)' do
118
+ let(:data) { random_str }
119
+ let(:message1) { { name: random_str, data: data } }
120
+ let(:message2) { { name: random_str, data: data } }
121
+ let(:message3) { { name: random_str, data: data } }
122
+
123
+ it 'publishes three messages' do
124
+ channel.attach
125
+ channel.publish([message1, message2, message3])
126
+ counter = 0
127
+ channel.subscribe do |message|
128
+ counter += 1
129
+ expect(message.data).to eq(data)
130
+ expect(message.name).to eq(message1[:name]) if counter == 1
131
+ expect(message.name).to eq(message2[:name]) if counter == 2
132
+ if counter == 3
133
+ expect(message.name).to eq(message3[:name])
134
+ stop_reactor
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ context 'a name with data payload (#RSL1a, #RSL1b)' do
141
+ let(:name) { random_str }
142
+ let(:data) { random_str }
143
+
144
+ it 'publishes a message' do
145
+ channel.attach
146
+ channel.publish(name, data)
147
+ channel.subscribe do |message|
148
+ expect(message.name).to eql(name)
149
+ expect(message.data).to eq(data)
150
+ stop_reactor
151
+ end
152
+ end
153
+ end
154
+
78
155
  context 'with supported extra payload content type (#RTL6h, #RSL6a2)' do
79
156
  let(:channel) { client.channel("pushenabled:#{random_str}") }
80
157