ably 0.8.1 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/README.md +11 -11
- data/SPEC.md +87 -87
- data/ably.gemspec +1 -1
- data/lib/ably/exceptions.rb +4 -1
- data/lib/ably/models/{paginated_resource.rb → paginated_result.rb} +8 -8
- data/lib/ably/models/presence_message.rb +1 -1
- data/lib/ably/modules/conversions.rb +15 -0
- data/lib/ably/modules/encodeable.rb +2 -2
- data/lib/ably/modules/event_emitter.rb +8 -8
- data/lib/ably/modules/safe_deferrable.rb +15 -3
- data/lib/ably/modules/state_emitter.rb +2 -2
- data/lib/ably/modules/state_machine.rb +1 -1
- data/lib/ably/realtime/channel.rb +2 -4
- data/lib/ably/realtime/channel/channel_manager.rb +1 -1
- data/lib/ably/realtime/client.rb +4 -4
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +2 -2
- data/lib/ably/realtime/connection.rb +1 -1
- data/lib/ably/realtime/connection/connection_manager.rb +3 -3
- data/lib/ably/realtime/connection/connection_state_machine.rb +1 -1
- data/lib/ably/realtime/connection/websocket_transport.rb +2 -2
- data/lib/ably/realtime/presence.rb +23 -5
- data/lib/ably/realtime/presence/members_map.rb +16 -13
- data/lib/ably/rest/channel.rb +3 -2
- data/lib/ably/rest/client.rb +2 -2
- data/lib/ably/rest/presence.rb +4 -4
- data/lib/ably/util/pub_sub.rb +1 -1
- data/lib/ably/util/safe_deferrable.rb +1 -1
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/channel_history_spec.rb +1 -1
- data/spec/acceptance/realtime/channel_spec.rb +5 -15
- data/spec/acceptance/realtime/connection_failures_spec.rb +2 -2
- data/spec/acceptance/realtime/connection_spec.rb +16 -14
- data/spec/acceptance/realtime/message_spec.rb +91 -8
- data/spec/acceptance/realtime/presence_spec.rb +190 -71
- data/spec/acceptance/realtime/stats_spec.rb +2 -2
- data/spec/acceptance/rest/channel_spec.rb +1 -1
- data/spec/acceptance/rest/encoders_spec.rb +1 -1
- data/spec/acceptance/rest/message_spec.rb +73 -1
- data/spec/acceptance/rest/presence_spec.rb +4 -2
- data/spec/unit/models/{paginated_resource_spec.rb → paginated_result_spec.rb} +18 -18
- data/spec/unit/modules/event_emitter_spec.rb +27 -27
- data/spec/unit/modules/state_emitter_spec.rb +1 -1
- data/spec/unit/realtime/channel_spec.rb +2 -2
- data/spec/unit/realtime/connection_spec.rb +3 -3
- data/spec/unit/realtime/presence_spec.rb +2 -2
- metadata +44 -44
data/lib/ably/rest/channel.rb
CHANGED
@@ -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::
|
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::
|
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
|
data/lib/ably/rest/client.rb
CHANGED
@@ -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::
|
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::
|
171
|
+
Ably::Models::PaginatedResult.new(response, url, self, paginated_options)
|
172
172
|
end
|
173
173
|
|
174
174
|
# Retrieve the Ably service time
|
data/lib/ably/rest/presence.rb
CHANGED
@@ -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::
|
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::
|
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::
|
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::
|
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
|
data/lib/ably/util/pub_sub.rb
CHANGED
@@ -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
|
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
@@ -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::
|
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 :
|
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(
|
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 '
|
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 '
|
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 '
|
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 '
|
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 :
|
30
|
+
context 'with :auto_connect option set to false' do
|
31
31
|
let(:client) do
|
32
|
-
Ably::Realtime::Client.new(default_options.merge(
|
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(:
|
204
|
+
let(:events_emitted) { [] }
|
205
205
|
let(:test_expectation) do
|
206
206
|
Proc.new do
|
207
|
-
expect(
|
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
|
-
|
216
|
-
test_expectation.call if
|
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
|
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
|
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
|
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.
|
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
|
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
|
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 '
|
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 '
|
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 '
|
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 '
|
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
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
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 '
|
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
|
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
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
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
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
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
|
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
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
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
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
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
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
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
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
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
|
-
|
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
|