ably 0.8.1 → 0.8.2

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.
Files changed (47) hide show
  1. checksums.yaml +5 -13
  2. data/README.md +11 -11
  3. data/SPEC.md +87 -87
  4. data/ably.gemspec +1 -1
  5. data/lib/ably/exceptions.rb +4 -1
  6. data/lib/ably/models/{paginated_resource.rb → paginated_result.rb} +8 -8
  7. data/lib/ably/models/presence_message.rb +1 -1
  8. data/lib/ably/modules/conversions.rb +15 -0
  9. data/lib/ably/modules/encodeable.rb +2 -2
  10. data/lib/ably/modules/event_emitter.rb +8 -8
  11. data/lib/ably/modules/safe_deferrable.rb +15 -3
  12. data/lib/ably/modules/state_emitter.rb +2 -2
  13. data/lib/ably/modules/state_machine.rb +1 -1
  14. data/lib/ably/realtime/channel.rb +2 -4
  15. data/lib/ably/realtime/channel/channel_manager.rb +1 -1
  16. data/lib/ably/realtime/client.rb +4 -4
  17. data/lib/ably/realtime/client/incoming_message_dispatcher.rb +2 -2
  18. data/lib/ably/realtime/connection.rb +1 -1
  19. data/lib/ably/realtime/connection/connection_manager.rb +3 -3
  20. data/lib/ably/realtime/connection/connection_state_machine.rb +1 -1
  21. data/lib/ably/realtime/connection/websocket_transport.rb +2 -2
  22. data/lib/ably/realtime/presence.rb +23 -5
  23. data/lib/ably/realtime/presence/members_map.rb +16 -13
  24. data/lib/ably/rest/channel.rb +3 -2
  25. data/lib/ably/rest/client.rb +2 -2
  26. data/lib/ably/rest/presence.rb +4 -4
  27. data/lib/ably/util/pub_sub.rb +1 -1
  28. data/lib/ably/util/safe_deferrable.rb +1 -1
  29. data/lib/ably/version.rb +1 -1
  30. data/spec/acceptance/realtime/channel_history_spec.rb +1 -1
  31. data/spec/acceptance/realtime/channel_spec.rb +5 -15
  32. data/spec/acceptance/realtime/connection_failures_spec.rb +2 -2
  33. data/spec/acceptance/realtime/connection_spec.rb +16 -14
  34. data/spec/acceptance/realtime/message_spec.rb +91 -8
  35. data/spec/acceptance/realtime/presence_spec.rb +190 -71
  36. data/spec/acceptance/realtime/stats_spec.rb +2 -2
  37. data/spec/acceptance/rest/channel_spec.rb +1 -1
  38. data/spec/acceptance/rest/encoders_spec.rb +1 -1
  39. data/spec/acceptance/rest/message_spec.rb +73 -1
  40. data/spec/acceptance/rest/presence_spec.rb +4 -2
  41. data/spec/unit/models/{paginated_resource_spec.rb → paginated_result_spec.rb} +18 -18
  42. data/spec/unit/modules/event_emitter_spec.rb +27 -27
  43. data/spec/unit/modules/state_emitter_spec.rb +1 -1
  44. data/spec/unit/realtime/channel_spec.rb +2 -2
  45. data/spec/unit/realtime/connection_spec.rb +3 -3
  46. data/spec/unit/realtime/presence_spec.rb +2 -2
  47. metadata +44 -44
@@ -37,6 +37,7 @@ module Ably
37
37
  # @return [Boolean] true if the message was published, otherwise false
38
38
  def publish(name, data)
39
39
  ensure_utf_8 :name, name
40
+ ensure_supported_payload data
40
41
 
41
42
  payload = {
42
43
  name: name,
@@ -60,7 +61,7 @@ module Ably
60
61
  # @option options [Symbol] :direction +:forwards+ or +:backwards+, defaults to +:backwards+
61
62
  # @option options [Integer] :limit Maximum number of messages to retrieve up to 1,000, defaults to 100
62
63
  #
63
- # @return [Ably::Models::PaginatedResource<Ably::Models::Message>] First {Ably::Models::PaginatedResource page} of {Ably::Models::Message} objects accessible with {Ably::Models::PaginatedResource#items #items}.
64
+ # @return [Ably::Models::PaginatedResult<Ably::Models::Message>] First {Ably::Models::PaginatedResult page} of {Ably::Models::Message} objects accessible with {Ably::Models::PaginatedResult#items #items}.
64
65
  #
65
66
  def history(options = {})
66
67
  url = "#{base_path}/messages"
@@ -78,7 +79,7 @@ module Ably
78
79
 
79
80
  response = client.get(url, options)
80
81
 
81
- Ably::Models::PaginatedResource.new(response, url, client, paginated_options) do |message|
82
+ Ably::Models::PaginatedResult.new(response, url, client, paginated_options) do |message|
82
83
  message.tap do |message|
83
84
  decode_message message
84
85
  end
@@ -150,7 +150,7 @@ module Ably
150
150
  # @option options [Integer] :limit Maximum number of messages to retrieve up to 1,000, defaults to 100
151
151
  # @option options [Symbol] :unit `:minute`, `:hour`, `:day` or `:month`. Defaults to `:minute`
152
152
  #
153
- # @return [Ably::Models::PaginatedResource<Ably::Models::Stats>] An Array of Stats
153
+ # @return [Ably::Models::PaginatedResult<Ably::Models::Stats>] An Array of Stats
154
154
  #
155
155
  def stats(options = {})
156
156
  options = {
@@ -168,7 +168,7 @@ module Ably
168
168
  url = '/stats'
169
169
  response = get(url, options)
170
170
 
171
- Ably::Models::PaginatedResource.new(response, url, self, paginated_options)
171
+ Ably::Models::PaginatedResult.new(response, url, self, paginated_options)
172
172
  end
173
173
 
174
174
  # Retrieve the Ably service time
@@ -25,7 +25,7 @@ module Ably
25
25
  # @param [Hash] options the options for the set of members present
26
26
  # @option options [Integer] :limit Maximum number of members to retrieve up to 1,000, defaults to 100
27
27
  #
28
- # @return [Ably::Models::PaginatedResource<Ably::Models::PresenceMessage>] First {Ably::Models::PaginatedResource page} of {Ably::Models::PresenceMessage} objects accessible with {Ably::Models::PaginatedResource#items #items}.
28
+ # @return [Ably::Models::PaginatedResult<Ably::Models::PresenceMessage>] First {Ably::Models::PaginatedResult page} of {Ably::Models::PresenceMessage} objects accessible with {Ably::Models::PaginatedResult#items #items}.
29
29
  #
30
30
  def get(options = {})
31
31
  options = options = {
@@ -39,7 +39,7 @@ module Ably
39
39
 
40
40
  response = client.get(base_path, options)
41
41
 
42
- Ably::Models::PaginatedResource.new(response, base_path, client, paginated_options) do |presence_message|
42
+ Ably::Models::PaginatedResult.new(response, base_path, client, paginated_options) do |presence_message|
43
43
  presence_message.tap do |presence_message|
44
44
  decode_message presence_message
45
45
  end
@@ -54,7 +54,7 @@ module Ably
54
54
  # @option options [Symbol] :direction +:forwards+ or +:backwards+, defaults to +:backwards+
55
55
  # @option options [Integer] :limit Maximum number of messages to retrieve up to 1,000, defaults to 100
56
56
  #
57
- # @return [Ably::Models::PaginatedResource<Ably::Models::PresenceMessage>] First {Ably::Models::PaginatedResource page} of {Ably::Models::PresenceMessage} objects accessible with {Ably::Models::PaginatedResource#items #items}.
57
+ # @return [Ably::Models::PaginatedResult<Ably::Models::PresenceMessage>] First {Ably::Models::PaginatedResult page} of {Ably::Models::PresenceMessage} objects accessible with {Ably::Models::PaginatedResult#items #items}.
58
58
  #
59
59
  def history(options = {})
60
60
  url = "#{base_path}/history"
@@ -72,7 +72,7 @@ module Ably
72
72
 
73
73
  response = client.get(url, options)
74
74
 
75
- Ably::Models::PaginatedResource.new(response, url, client, paginated_options) do |presence_message|
75
+ Ably::Models::PaginatedResult.new(response, url, client, paginated_options) do |presence_message|
76
76
  presence_message.tap do |presence_message|
77
77
  decode_message presence_message
78
78
  end
@@ -37,7 +37,7 @@ module Ably::Util
37
37
  configure_event_emitter options
38
38
 
39
39
  alias_method :subscribe, :unsafe_on
40
- alias_method :publish, :trigger
40
+ alias_method :publish, :emit
41
41
  alias_method :unsubscribe, :off
42
42
  end
43
43
  end
@@ -1,6 +1,6 @@
1
1
  module Ably::Util
2
2
  # SafeDeferrable class provides a Deferrable that is safe to use for for public interfaces
3
- # of this client library. Any exceptions raised in the success or failure callbacks is
3
+ # of this client library. Any exceptions raised in the success or failure callbacks are
4
4
  # caught and logged to the provided logger.
5
5
  #
6
6
  # An exception in a callback provided by a developer should not break this client library
data/lib/ably/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Ably
2
- VERSION = '0.8.1'
2
+ VERSION = '0.8.2'
3
3
  end
@@ -24,7 +24,7 @@ describe Ably::Realtime::Channel, '#history', :event_machine do
24
24
  expect(history).to be_a(Ably::Util::SafeDeferrable)
25
25
  history.callback do |page|
26
26
  expect(page.items.count).to eql(1)
27
- expect(page).to be_a(Ably::Models::PaginatedResource)
27
+ expect(page).to be_a(Ably::Models::PaginatedResult)
28
28
  stop_reactor
29
29
  end
30
30
  end
@@ -13,9 +13,9 @@ describe Ably::Realtime::Channel, :event_machine do
13
13
  let(:messages) { [] }
14
14
 
15
15
  describe 'initialization' do
16
- context 'with :connect_automatically option set to false on connection' do
16
+ context 'with :auto_connect option set to false on connection' do
17
17
  let(:client) do
18
- Ably::Realtime::Client.new(default_options.merge(connect_automatically: false))
18
+ Ably::Realtime::Client.new(default_options.merge(auto_connect: false))
19
19
  end
20
20
 
21
21
  it 'remains initialized when accessing a channel' do
@@ -32,16 +32,6 @@ describe Ably::Realtime::Channel, :event_machine do
32
32
  stop_reactor
33
33
  end
34
34
  end
35
-
36
- it 'opens a connection implicitly when accessing #presence' do
37
- client.channel('test').tap do |channel|
38
- channel.on(:attached) do
39
- expect(client.connection).to be_connected
40
- stop_reactor
41
- end
42
- channel.presence
43
- end
44
- end
45
35
  end
46
36
  end
47
37
 
@@ -162,7 +152,7 @@ describe Ably::Realtime::Channel, :event_machine do
162
152
  end
163
153
  let(:restricted_channel) { restricted_client.channel("cannot_subscribe") }
164
154
 
165
- it 'triggers failed event' do
155
+ it 'emits failed event' do
166
156
  restricted_channel.attach
167
157
  restricted_channel.on(:failed) do |error|
168
158
  expect(restricted_channel.state).to eq(:failed)
@@ -179,7 +169,7 @@ describe Ably::Realtime::Channel, :event_machine do
179
169
  end
180
170
  end
181
171
 
182
- it 'triggers an error event' do
172
+ it 'emits an error event' do
183
173
  restricted_channel.attach
184
174
  restricted_channel.on(:error) do |error|
185
175
  expect(restricted_channel.state).to eq(:failed)
@@ -458,7 +448,7 @@ describe Ably::Realtime::Channel, :event_machine do
458
448
  end
459
449
  end
460
450
 
461
- it 'triggers an error event on the channel' do
451
+ it 'emits an error event on the channel' do
462
452
  channel.attach do
463
453
  channel.on(:error) do |error|
464
454
  expect(error).to eql(connection_error)
@@ -319,7 +319,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
319
319
  disconnected: { retry_every: retry_every, max_time_in_state: 60 })
320
320
  end
321
321
 
322
- it "retries every CONNECT_RETRY_CONFIG[:disconnected][:retry_every] seconds" do
322
+ it "retries every #{Ably::Realtime::Connection::ConnectionManager::CONNECT_RETRY_CONFIG[:disconnected][:retry_every]} seconds" do
323
323
  fail_if_suspended_or_failed
324
324
 
325
325
  stubbed_first_attempt = false
@@ -415,7 +415,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
415
415
  end
416
416
  end
417
417
 
418
- it 'triggers the resume callback', api_private: true do
418
+ it 'executes the resume callback', api_private: true do
419
419
  channel.attach do
420
420
  connection.transport.close_connection_after_writing
421
421
  connection.on_resume do
@@ -27,9 +27,9 @@ describe Ably::Realtime::Connection, :event_machine do
27
27
  end
28
28
  end
29
29
 
30
- context 'with :connect_automatically option set to false' do
30
+ context 'with :auto_connect option set to false' do
31
31
  let(:client) do
32
- Ably::Realtime::Client.new(default_options.merge(connect_automatically: false))
32
+ Ably::Realtime::Client.new(default_options.merge(auto_connect: false))
33
33
  end
34
34
 
35
35
  it 'does not connect automatically' do
@@ -201,10 +201,10 @@ describe Ably::Realtime::Connection, :event_machine do
201
201
 
202
202
  context 'initialization state changes' do
203
203
  let(:phases) { [:connecting, :connected] }
204
- let(:events_triggered) { [] }
204
+ let(:events_emitted) { [] }
205
205
  let(:test_expectation) do
206
206
  Proc.new do
207
- expect(events_triggered).to eq(phases)
207
+ expect(events_emitted).to eq(phases)
208
208
  stop_reactor
209
209
  end
210
210
  end
@@ -212,20 +212,20 @@ describe Ably::Realtime::Connection, :event_machine do
212
212
  def expect_ordered_phases
213
213
  phases.each do |phase|
214
214
  connection.on(phase) do
215
- events_triggered << phase
216
- test_expectation.call if events_triggered.length == phases.length
215
+ events_emitted << phase
216
+ test_expectation.call if events_emitted.length == phases.length
217
217
  end
218
218
  end
219
219
  end
220
220
 
221
221
  context 'with implicit #connect' do
222
- it 'are triggered in order' do
222
+ it 'are emitted in order' do
223
223
  expect_ordered_phases
224
224
  end
225
225
  end
226
226
 
227
227
  context 'with explicit #connect' do
228
- it 'are triggered in order' do
228
+ it 'are emitted in order' do
229
229
  expect_ordered_phases
230
230
  connection.connect
231
231
  end
@@ -542,10 +542,10 @@ describe Ably::Realtime::Connection, :event_machine do
542
542
  let(:states) { Hash.new }
543
543
  let(:client_options) { default_options.merge(log_level: :none) }
544
544
 
545
- it 'is composed of connection id and serial that is kept up to date with each message ACK received' do
545
+ it 'is composed of connection key and serial that is kept up to date with each message ACK received' do
546
546
  connection.on(:connected) do
547
547
  expected_serial = -1
548
- expect(connection.id).to_not be_nil
548
+ expect(connection.key).to_not be_nil
549
549
  expect(connection.serial).to eql(expected_serial)
550
550
 
551
551
  client.channel('test').attach do |channel|
@@ -556,6 +556,8 @@ describe Ably::Realtime::Connection, :event_machine do
556
556
  channel.publish('event', 'data') do
557
557
  expected_serial += 1 # attach message received
558
558
  expect(connection.serial).to eql(expected_serial)
559
+
560
+ expect(connection.recovery_key).to eql("#{connection.key}:#{connection.serial}")
559
561
  stop_reactor
560
562
  end
561
563
  end
@@ -621,7 +623,7 @@ describe Ably::Realtime::Connection, :event_machine do
621
623
  end
622
624
  end
623
625
 
624
- it 'does not trigger a resume callback', api_private: true do
626
+ it 'does not call a resume callback', api_private: true do
625
627
  connection.once(:connected) do
626
628
  connection.transition_state_machine! :failed
627
629
  end
@@ -629,7 +631,7 @@ describe Ably::Realtime::Connection, :event_machine do
629
631
  connection.once(:failed) do
630
632
  recover_client = Ably::Realtime::Client.new(default_options.merge(recover: client.connection.recovery_key))
631
633
  recover_client.connection.on_resume do
632
- raise 'Should not trigger resume callback'
634
+ raise 'Should not call the resume callback'
633
635
  end
634
636
  recover_client.connection.on(:connected) do
635
637
  EventMachine.add_timer(0.5) { stop_reactor }
@@ -676,7 +678,7 @@ describe Ably::Realtime::Connection, :event_machine do
676
678
  context 'with invalid formatted value sent to server' do
677
679
  let(:client_options) { default_options.merge(recover: 'not-a-valid-connection-key:1', log_level: :none) }
678
680
 
679
- it 'triggers a fatal error on the connection object, sets the #error_reason and disconnects' do
681
+ it 'emits a fatal error on the connection object, sets the #error_reason and disconnects' do
680
682
  connection.once(:error) do |error|
681
683
  expect(connection.state).to eq(:failed)
682
684
  expect(error.message).to match(/Invalid connection key/)
@@ -691,7 +693,7 @@ describe Ably::Realtime::Connection, :event_machine do
691
693
  context 'with expired (missing) value sent to server' do
692
694
  let(:client_options) { default_options.merge(recover: '0123456789abcdef:0', log_level: :fatal) }
693
695
 
694
- it 'triggers an error on the connection object, sets the #error_reason, yet will connect anyway' do
696
+ it 'emits an error on the connection object, sets the #error_reason, yet will connect anyway' do
695
697
  connection.once(:error) do |error|
696
698
  expect(connection.state).to eq(:connected)
697
699
  expect(error.message).to match(/Invalid connection key/i)
@@ -32,6 +32,87 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
32
32
  end
33
33
  end
34
34
 
35
+ context 'with supported data payload content type' do
36
+ def publish_and_check_data(data)
37
+ channel.attach
38
+ channel.publish 'event', data
39
+ channel.subscribe do |message|
40
+ expect(message.data).to eql(data)
41
+ stop_reactor
42
+ end
43
+ end
44
+
45
+ context 'JSON Object (Hash)' do
46
+ let(:data) { { 'Hash' => 'true' } }
47
+
48
+ it 'is encoded and decoded to the same hash' do
49
+ publish_and_check_data data
50
+ end
51
+ end
52
+
53
+ context 'JSON Array' do
54
+ let(:data) { [ nil, true, false, 55, 'string', { 'Hash' => true }, ['array'] ] }
55
+
56
+ it 'is encoded and decoded to the same Array' do
57
+ publish_and_check_data data
58
+ end
59
+ end
60
+
61
+ context 'String' do
62
+ let(:data) { random_str }
63
+
64
+ it 'is encoded and decoded to the same Array' do
65
+ publish_and_check_data data
66
+ end
67
+ end
68
+
69
+ context 'Binary' do
70
+ let(:data) { Base64.encode64(random_str) }
71
+
72
+ it 'is encoded and decoded to the same Array' do
73
+ publish_and_check_data data
74
+ end
75
+ end
76
+ end
77
+
78
+ context 'with unsupported data payload content type' do
79
+ context 'Integer' do
80
+ let(:data) { 1 }
81
+
82
+ it 'is raises an UnsupportedDataTypeError 40011 exception' do
83
+ expect { channel.publish 'event', data }.to raise_error(Ably::Exceptions::UnsupportedDataTypeError)
84
+ stop_reactor
85
+ end
86
+ end
87
+
88
+ context 'Float' do
89
+ let(:data) { 1.1 }
90
+
91
+ it 'is raises an UnsupportedDataTypeError 40011 exception' do
92
+ expect { channel.publish 'event', data }.to raise_error(Ably::Exceptions::UnsupportedDataTypeError)
93
+ stop_reactor
94
+ end
95
+ end
96
+
97
+ context 'Boolean' do
98
+ let(:data) { true }
99
+
100
+ it 'is raises an UnsupportedDataTypeError 40011 exception' do
101
+ expect { channel.publish 'event', data }.to raise_error(Ably::Exceptions::UnsupportedDataTypeError)
102
+ stop_reactor
103
+ end
104
+ end
105
+
106
+ context 'False' do
107
+ let(:data) { false }
108
+
109
+ it 'is raises an UnsupportedDataTypeError 40011 exception' do
110
+ expect { channel.publish 'event', data }.to raise_error(Ably::Exceptions::UnsupportedDataTypeError)
111
+ stop_reactor
112
+ end
113
+ end
114
+ end
115
+
35
116
  context 'with ASCII_8BIT message name' do
36
117
  let(:message_name) { random_str.encode(Encoding::ASCII_8BIT) }
37
118
  it 'is converted into UTF_8' do
@@ -410,7 +491,7 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
410
491
  end
411
492
  end
412
493
 
413
- it 'triggers a Cipher error on the channel' do
494
+ it 'emits a Cipher error on the channel' do
414
495
  unencrypted_channel_client2.attach do
415
496
  encrypted_channel_client1.publish 'example', payload
416
497
  unencrypted_channel_client2.on(:error) do |error|
@@ -441,7 +522,7 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
441
522
  end
442
523
  end
443
524
 
444
- it 'triggers a Cipher error on the channel' do
525
+ it 'emits a Cipher error on the channel' do
445
526
  encrypted_channel_client2.attach do
446
527
  encrypted_channel_client1.publish 'example', payload
447
528
  encrypted_channel_client2.on(:error) do |error|
@@ -464,15 +545,17 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
464
545
  let(:payload) { MessagePack.pack({ 'key' => random_str }) }
465
546
 
466
547
  it 'delivers the message but still encrypted with the cipher details in the #encoding attribute' do
467
- encrypted_channel_client1.publish 'example', payload
468
- encrypted_channel_client2.subscribe do |message|
469
- expect(message.data).to_not eql(payload)
470
- expect(message.encoding).to match(/^cipher\+aes-256-cbc/)
471
- stop_reactor
548
+ encrypted_channel_client2.attach do
549
+ encrypted_channel_client1.publish 'example', payload
550
+ encrypted_channel_client2.subscribe do |message|
551
+ expect(message.data).to_not eql(payload)
552
+ expect(message.encoding).to match(/^cipher\+aes-256-cbc/)
553
+ stop_reactor
554
+ end
472
555
  end
473
556
  end
474
557
 
475
- it 'triggers a Cipher error on the channel' do
558
+ it 'emits a Cipher error on the channel' do
476
559
  encrypted_channel_client2.attach do
477
560
  encrypted_channel_client1.publish 'example', payload
478
561
  encrypted_channel_client2.on(:error) do |error|
@@ -53,6 +53,107 @@ describe Ably::Realtime::Presence, :event_machine do
53
53
  end
54
54
  end
55
55
 
56
+ context 'with supported data payload content type' do
57
+ def register_presence_and_check_data(method_name, data)
58
+ if method_name.to_s.match(/_client/)
59
+ presence_client_one.public_send(method_name, 'client_id', data: data)
60
+ else
61
+ presence_client_one.public_send(method_name, data: data)
62
+ end
63
+
64
+ presence_client_one.subscribe do |presence_message|
65
+ expect(presence_message.data).to eql(data)
66
+ stop_reactor
67
+ end
68
+ end
69
+
70
+ context 'JSON Object (Hash)' do
71
+ let(:data) { { 'Hash' => 'true' } }
72
+
73
+ it 'is encoded and decoded to the same hash' do
74
+ setup_test(method_name, args, options) do
75
+ register_presence_and_check_data method_name, data
76
+ end
77
+ end
78
+ end
79
+
80
+ context 'JSON Array' do
81
+ let(:data) { [ nil, true, false, 55, 'string', { 'Hash' => true }, ['array'] ] }
82
+
83
+ it 'is encoded and decoded to the same Array' do
84
+ setup_test(method_name, args, options) do
85
+ register_presence_and_check_data method_name, data
86
+ end
87
+ end
88
+ end
89
+
90
+ context 'String' do
91
+ let(:data) { random_str }
92
+
93
+ it 'is encoded and decoded to the same Array' do
94
+ setup_test(method_name, args, options) do
95
+ register_presence_and_check_data method_name, data
96
+ end
97
+ end
98
+ end
99
+
100
+ context 'Binary' do
101
+ let(:data) { Base64.encode64(random_str) }
102
+
103
+ it 'is encoded and decoded to the same Array' do
104
+ setup_test(method_name, args, options) do
105
+ register_presence_and_check_data method_name, data
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ context 'with unsupported data payload content type' do
112
+ def presence_action(method_name, data)
113
+ if method_name.to_s.match(/_client/)
114
+ presence_client_one.public_send(method_name, 'client_id', data: data)
115
+ else
116
+ presence_client_one.public_send(method_name, data: data)
117
+ end
118
+ end
119
+
120
+ context 'Integer' do
121
+ let(:data) { 1 }
122
+
123
+ it 'raises an UnsupportedDataTypeError 40011 exception' do
124
+ expect { presence_action(method_name, data) }.to raise_error(Ably::Exceptions::UnsupportedDataTypeError)
125
+ stop_reactor
126
+ end
127
+ end
128
+
129
+ context 'Float' do
130
+ let(:data) { 1.1 }
131
+
132
+ it 'raises an UnsupportedDataTypeError 40011 exception' do
133
+ expect { presence_action(method_name, data) }.to raise_error(Ably::Exceptions::UnsupportedDataTypeError)
134
+ stop_reactor
135
+ end
136
+ end
137
+
138
+ context 'Boolean' do
139
+ let(:data) { true }
140
+
141
+ it 'raises an UnsupportedDataTypeError 40011 exception' do
142
+ expect { presence_action(method_name, data) }.to raise_error(Ably::Exceptions::UnsupportedDataTypeError)
143
+ stop_reactor
144
+ end
145
+ end
146
+
147
+ context 'False' do
148
+ let(:data) { false }
149
+
150
+ it 'raises an UnsupportedDataTypeError 40011 exception' do
151
+ expect { presence_action(method_name, data) }.to raise_error(Ably::Exceptions::UnsupportedDataTypeError)
152
+ stop_reactor
153
+ end
154
+ end
155
+ end
156
+
56
157
  it 'returns a SafeDeferrable that catches exceptions in callbacks and logs them' do
57
158
  setup_test(method_name, args, options) do
58
159
  expect(presence_client_one.public_send(method_name, args)).to be_a(Ably::Util::SafeDeferrable)
@@ -153,13 +254,15 @@ describe Ably::Realtime::Presence, :event_machine do
153
254
  stop_reactor
154
255
  end
155
256
 
156
- it 'will trigger an :in_sync event when synchronisation is complete' do
257
+ it 'will emit an :in_sync event when synchronisation is complete' do
157
258
  presence_client_one.enter
158
259
  presence_client_two.enter
159
260
 
160
261
  presence_anonymous_client.members.once(:in_sync) do
161
262
  stop_reactor
162
263
  end
264
+
265
+ channel_anonymous_client.attach
163
266
  end
164
267
 
165
268
  context 'before server sync complete' do
@@ -180,6 +283,8 @@ describe Ably::Realtime::Presence, :event_machine do
180
283
  expect(member_ids.uniq.count).to eql(2)
181
284
  stop_reactor
182
285
  end
286
+
287
+ channel_anonymous_client.attach
183
288
  end
184
289
  end
185
290
  end
@@ -200,7 +305,7 @@ describe Ably::Realtime::Presence, :event_machine do
200
305
  presence_client_one.enter do
201
306
  channel_anonymous_client.attach do
202
307
  expect(channel_anonymous_client.presence).to_not be_sync_complete
203
- channel_anonymous_client.presence.get do
308
+ channel_anonymous_client.presence.get(wait_for_sync: true) do
204
309
  expect(channel_anonymous_client.presence).to be_sync_complete
205
310
  stop_reactor
206
311
  end
@@ -319,7 +424,7 @@ describe Ably::Realtime::Presence, :event_machine do
319
424
  end
320
425
  end
321
426
 
322
- it 'does not emit :present after the :leave event has been emitted, and that member is not included in the list of members via #get' do
427
+ it 'does not emit :present after the :leave event has been emitted, and that member is not included in the list of members via #get with :wait_for_sync' do
323
428
  left_client = 10
324
429
  left_client_id = "client:#{left_client}"
325
430
 
@@ -341,7 +446,7 @@ describe Ably::Realtime::Presence, :event_machine do
341
446
  member_left_emitted = true
342
447
  end
343
448
 
344
- presence_anonymous_client.get do |members|
449
+ presence_anonymous_client.get(wait_for_sync: true) do |members|
345
450
  expect(members.count).to eql(enter_expected_count - 1)
346
451
  expect(member_left_emitted).to eql(true)
347
452
  expect(members.map(&:client_id)).to_not include(left_client_id)
@@ -365,43 +470,40 @@ describe Ably::Realtime::Presence, :event_machine do
365
470
  end
366
471
 
367
472
  context '#get' do
368
- it 'waits until sync is complete', event_machine: 15 do
369
- enter_expected_count.times do |index|
370
- presence_client_one.enter_client("client:#{index}") do |message|
371
- entered << message
372
- next unless entered.count == enter_expected_count
373
-
374
- presence_anonymous_client.get do |members|
375
- expect(members.map(&:client_id).uniq.count).to eql(enter_expected_count)
376
- expect(members.count).to eql(enter_expected_count)
377
- stop_reactor
473
+ context 'with :wait_for_sync option set to true' do
474
+ it 'waits until sync is complete', event_machine: 15 do
475
+ enter_expected_count.times do |index|
476
+ presence_client_one.enter_client("client:#{index}") do |message|
477
+ entered << message
478
+ next unless entered.count == enter_expected_count
479
+
480
+ presence_anonymous_client.get(wait_for_sync: true) do |members|
481
+ expect(members.map(&:client_id).uniq.count).to eql(enter_expected_count)
482
+ expect(members.count).to eql(enter_expected_count)
483
+ stop_reactor
484
+ end
378
485
  end
379
486
  end
380
487
  end
381
488
  end
382
- end
383
- end
384
- end
385
- end
386
489
 
387
- context 'automatic attachment of channel on access to presence object' do
388
- it 'is implicit if presence state is initialized' do
389
- channel_client_one.presence
390
- channel_client_one.on(:attached) do
391
- expect(channel_client_one.state).to eq(:attached)
392
- stop_reactor
393
- end
394
- end
395
-
396
- it 'is disabled if presence state is not initialized' do
397
- channel_client_one.attach do
398
- channel_client_one.detach do
399
- expect(channel_client_one.state).to eq(:detached)
400
-
401
- channel_client_one.presence # access the presence object
402
- EventMachine.add_timer(1) do
403
- expect(channel_client_one.state).to eq(:detached)
404
- stop_reactor
490
+ context 'by default' do
491
+ it 'it does not wait for sync', event_machine: 15 do
492
+ enter_expected_count.times do |index|
493
+ presence_client_one.enter_client("client:#{index}") do |message|
494
+ entered << message
495
+ next unless entered.count == enter_expected_count
496
+
497
+ channel_anonymous_client.attach do
498
+ presence_anonymous_client.get do |members|
499
+ expect(presence_anonymous_client.members).to_not be_in_sync
500
+ expect(members.count).to eql(0)
501
+ stop_reactor
502
+ end
503
+ end
504
+ end
505
+ end
506
+ end
405
507
  end
406
508
  end
407
509
  end
@@ -571,7 +673,7 @@ describe Ably::Realtime::Presence, :event_machine do
571
673
  end
572
674
 
573
675
  context 'when set to nil' do
574
- it 'emits the previously defined value as a convenience' do
676
+ it 'emits a nil value for the data attribute when leaving' do
575
677
  presence_client_one.enter data: enter_data do
576
678
  presence_client_one.leave data: nil
577
679
  end
@@ -595,6 +697,22 @@ describe Ably::Realtime::Presence, :event_machine do
595
697
  end
596
698
  end
597
699
  end
700
+
701
+ context 'and sync is complete' do
702
+ it 'does not cache members that have left' do
703
+ presence_client_one.enter data: enter_data do
704
+ expect(presence_client_one.members).to be_in_sync
705
+ expect(presence_client_one.members.send(:members).count).to eql(1)
706
+ presence_client_one.leave data: data
707
+ end
708
+
709
+ presence_client_one.subscribe(:leave) do |presence_message|
710
+ expect(presence_message.data).to eql(data)
711
+ expect(presence_client_one.members.send(:members).count).to eql(0)
712
+ stop_reactor
713
+ end
714
+ end
715
+ end
598
716
  end
599
717
 
600
718
  it 'raises an exception if not entered' do
@@ -911,43 +1029,45 @@ describe Ably::Realtime::Presence, :event_machine do
911
1029
  )
912
1030
  end
913
1031
 
914
- it 'fails if the connection fails' do
915
- when_all(*connect_members_deferrables) do
916
- channel_client_two.attach do
917
- client_two.connection.transport.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
918
- if protocol_message.action == :sync
919
- sync_pages_received << protocol_message
920
- force_connection_failure client_two if sync_pages_received.count == 1
1032
+ context 'when :wait_for_sync is true' do
1033
+ it 'fails if the connection fails' do
1034
+ when_all(*connect_members_deferrables) do
1035
+ channel_client_two.attach do
1036
+ client_two.connection.transport.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
1037
+ if protocol_message.action == :sync
1038
+ sync_pages_received << protocol_message
1039
+ force_connection_failure client_two if sync_pages_received.count == 1
1040
+ end
921
1041
  end
922
1042
  end
923
- end
924
1043
 
925
- presence_client_two.get.tap do |deferrable|
926
- deferrable.callback { raise 'Get should not succeed' }
927
- deferrable.errback do |error|
928
- stop_reactor
1044
+ presence_client_two.get(wait_for_sync: true).tap do |deferrable|
1045
+ deferrable.callback { raise 'Get should not succeed' }
1046
+ deferrable.errback do |error|
1047
+ stop_reactor
1048
+ end
929
1049
  end
930
1050
  end
931
1051
  end
932
- end
933
1052
 
934
- it 'fails if the channel is detached' do
935
- when_all(*connect_members_deferrables) do
936
- channel_client_two.attach do
937
- client_two.connection.transport.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
938
- if protocol_message.action == :sync
939
- # prevent any more SYNC messages coming through
940
- client_two.connection.transport.__incoming_protocol_msgbus__.unsubscribe
941
- channel_client_two.change_state :detaching
942
- channel_client_two.change_state :detached
1053
+ it 'fails if the channel is detached' do
1054
+ when_all(*connect_members_deferrables) do
1055
+ channel_client_two.attach do
1056
+ client_two.connection.transport.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
1057
+ if protocol_message.action == :sync
1058
+ # prevent any more SYNC messages coming through
1059
+ client_two.connection.transport.__incoming_protocol_msgbus__.unsubscribe
1060
+ channel_client_two.change_state :detaching
1061
+ channel_client_two.change_state :detached
1062
+ end
943
1063
  end
944
1064
  end
945
- end
946
1065
 
947
- presence_client_two.get.tap do |deferrable|
948
- deferrable.callback { raise 'Get should not succeed' }
949
- deferrable.errback do |error|
950
- stop_reactor
1066
+ presence_client_two.get(wait_for_sync: true).tap do |deferrable|
1067
+ deferrable.callback { raise 'Get should not succeed' }
1068
+ deferrable.errback do |error|
1069
+ stop_reactor
1070
+ end
951
1071
  end
952
1072
  end
953
1073
  end
@@ -1059,12 +1179,12 @@ describe Ably::Realtime::Presence, :event_machine do
1059
1179
  end
1060
1180
 
1061
1181
  wait_until(proc { clients_entered[:client_one] + clients_entered[:client_two] == total_members * 2 }) do
1062
- presence_anonymous_client.get do |anonymous_members|
1182
+ presence_anonymous_client.get(wait_for_sync: true) do |anonymous_members|
1063
1183
  expect(anonymous_members.count).to eq(total_members)
1064
1184
  expect(anonymous_members.map(&:client_id).uniq.count).to eq(total_members)
1065
1185
 
1066
- presence_client_one.get do |client_one_members|
1067
- presence_client_two.get do |client_two_members|
1186
+ presence_client_one.get(wait_for_sync: true) do |client_one_members|
1187
+ presence_client_two.get(wait_for_sync: true) do |client_two_members|
1068
1188
  expect(client_one_members.count).to eq(total_members)
1069
1189
  expect(client_one_members.count).to eq(client_two_members.count)
1070
1190
  stop_reactor
@@ -1319,7 +1439,7 @@ describe Ably::Realtime::Presence, :event_machine do
1319
1439
 
1320
1440
  specify 'expect :left event with client data from enter event' do
1321
1441
  presence_client_one.subscribe(:leave) do |message|
1322
- presence_client_one.get do |members|
1442
+ presence_client_one.get(wait_for_sync: true) do |members|
1323
1443
  expect(members.count).to eq(0)
1324
1444
  expect(message.data).to eql(data_payload)
1325
1445
  stop_reactor
@@ -1335,8 +1455,7 @@ describe Ably::Realtime::Presence, :event_machine do
1335
1455
  let(:members_count) { 400 }
1336
1456
  let(:sync_pages_received) { [] }
1337
1457
 
1338
- # Will re-enable once https://github.com/ably/realtime/issues/91 is resolved
1339
- skip 'resumes the SYNC operation', em_timeout: 15 do
1458
+ it 'resumes the SYNC operation', em_timeout: 15 do
1340
1459
  when_all(*members_count.times.map do |index|
1341
1460
  presence_client_one.enter_client("client:#{index}")
1342
1461
  end) do
@@ -1349,7 +1468,7 @@ describe Ably::Realtime::Presence, :event_machine do
1349
1468
  end
1350
1469
  end
1351
1470
 
1352
- presence_client_two.get do |members|
1471
+ presence_client_two.get(wait_for_sync: true) do |members|
1353
1472
  expect(members.count).to eql(members_count)
1354
1473
  expect(members.map(&:member_key).uniq.count).to eql(members_count)
1355
1474
  stop_reactor