ably 0.7.2 → 0.7.4
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +1 -1
- data/README.md +107 -24
- data/SPEC.md +531 -398
- data/lib/ably/auth.rb +23 -15
- data/lib/ably/exceptions.rb +9 -0
- data/lib/ably/models/message.rb +17 -9
- data/lib/ably/models/paginated_resource.rb +12 -8
- data/lib/ably/models/presence_message.rb +18 -10
- data/lib/ably/models/protocol_message.rb +15 -4
- data/lib/ably/modules/async_wrapper.rb +4 -3
- data/lib/ably/modules/event_emitter.rb +31 -2
- data/lib/ably/modules/message_emitter.rb +77 -0
- data/lib/ably/modules/safe_deferrable.rb +71 -0
- data/lib/ably/modules/safe_yield.rb +41 -0
- data/lib/ably/modules/state_emitter.rb +28 -8
- data/lib/ably/realtime.rb +0 -5
- data/lib/ably/realtime/channel.rb +24 -29
- data/lib/ably/realtime/channel/channel_manager.rb +54 -11
- data/lib/ably/realtime/channel/channel_state_machine.rb +21 -6
- data/lib/ably/realtime/client.rb +7 -2
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +29 -26
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +4 -4
- data/lib/ably/realtime/connection.rb +41 -9
- data/lib/ably/realtime/connection/connection_manager.rb +72 -24
- data/lib/ably/realtime/connection/connection_state_machine.rb +26 -4
- data/lib/ably/realtime/connection/websocket_transport.rb +19 -6
- data/lib/ably/realtime/presence.rb +74 -208
- data/lib/ably/realtime/presence/members_map.rb +264 -0
- data/lib/ably/realtime/presence/presence_manager.rb +59 -0
- data/lib/ably/realtime/presence/presence_state_machine.rb +64 -0
- data/lib/ably/rest/channel.rb +1 -1
- data/lib/ably/rest/client.rb +6 -2
- data/lib/ably/rest/presence.rb +1 -1
- data/lib/ably/util/pub_sub.rb +3 -1
- data/lib/ably/util/safe_deferrable.rb +18 -0
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/channel_history_spec.rb +2 -2
- data/spec/acceptance/realtime/channel_spec.rb +28 -6
- data/spec/acceptance/realtime/connection_failures_spec.rb +116 -46
- data/spec/acceptance/realtime/connection_spec.rb +55 -10
- data/spec/acceptance/realtime/message_spec.rb +32 -0
- data/spec/acceptance/realtime/presence_spec.rb +456 -96
- data/spec/acceptance/realtime/stats_spec.rb +2 -2
- data/spec/acceptance/realtime/time_spec.rb +2 -2
- data/spec/acceptance/rest/auth_spec.rb +75 -7
- data/spec/shared/client_initializer_behaviour.rb +8 -0
- data/spec/shared/safe_deferrable_behaviour.rb +71 -0
- data/spec/support/api_helper.rb +1 -1
- data/spec/support/event_machine_helper.rb +1 -1
- data/spec/support/test_app.rb +13 -7
- data/spec/unit/models/message_spec.rb +15 -14
- data/spec/unit/models/paginated_resource_spec.rb +4 -4
- data/spec/unit/models/presence_message_spec.rb +17 -17
- data/spec/unit/models/stat_spec.rb +4 -4
- data/spec/unit/modules/async_wrapper_spec.rb +28 -9
- data/spec/unit/modules/event_emitter_spec.rb +50 -0
- data/spec/unit/modules/state_emitter_spec.rb +76 -2
- data/spec/unit/realtime/channel_spec.rb +51 -20
- data/spec/unit/realtime/channels_spec.rb +3 -3
- data/spec/unit/realtime/connection_spec.rb +30 -0
- data/spec/unit/realtime/presence_spec.rb +52 -26
- data/spec/unit/realtime/safe_deferrable_spec.rb +12 -0
- metadata +85 -39
- checksums.yaml +0 -7
- data/.ruby-version.old +0 -1
@@ -28,7 +28,7 @@ describe Ably::Models::PresenceMessage do
|
|
28
28
|
end
|
29
29
|
|
30
30
|
context 'with a protocol message with a different connectionId' do
|
31
|
-
let(:model) { subject.new({ 'connectionId' => model_connection_id }, protocol_message) }
|
31
|
+
let(:model) { subject.new({ 'connectionId' => model_connection_id }, protocol_message: protocol_message) }
|
32
32
|
|
33
33
|
it 'uses the model value' do
|
34
34
|
expect(model.connection_id).to eql(model_connection_id)
|
@@ -46,7 +46,7 @@ describe Ably::Models::PresenceMessage do
|
|
46
46
|
end
|
47
47
|
|
48
48
|
context 'with a protocol message with a connectionId' do
|
49
|
-
let(:model) { subject.new({ }, protocol_message) }
|
49
|
+
let(:model) { subject.new({ }, protocol_message: protocol_message) }
|
50
50
|
|
51
51
|
it 'uses the model value' do
|
52
52
|
expect(model.connection_id).to eql(protocol_connection_id)
|
@@ -63,8 +63,8 @@ describe Ably::Models::PresenceMessage do
|
|
63
63
|
end
|
64
64
|
|
65
65
|
context 'with the same client id across multiple connections' do
|
66
|
-
let(:connection_1) { subject.new({ client_id: 'same', connection_id: 'unique' }, protocol_message) }
|
67
|
-
let(:connection_2) { subject.new({ client_id: 'same', connection_id: 'different' }, protocol_message) }
|
66
|
+
let(:connection_1) { subject.new({ client_id: 'same', connection_id: 'unique' }, protocol_message: protocol_message) }
|
67
|
+
let(:connection_2) { subject.new({ client_id: 'same', connection_id: 'different' }, protocol_message: protocol_message) }
|
68
68
|
|
69
69
|
it 'is unique' do
|
70
70
|
expect(connection_1.member_key).to_not eql(connection_2.member_key)
|
@@ -72,8 +72,8 @@ describe Ably::Models::PresenceMessage do
|
|
72
72
|
end
|
73
73
|
|
74
74
|
context 'with a single connection and different client_ids' do
|
75
|
-
let(:client_1) { subject.new({ client_id: 'unique', connection_id: 'same' }, protocol_message) }
|
76
|
-
let(:client_2) { subject.new({ client_id: 'different', connection_id: 'same' }, protocol_message) }
|
75
|
+
let(:client_1) { subject.new({ client_id: 'unique', connection_id: 'same' }, protocol_message: protocol_message) }
|
76
|
+
let(:client_2) { subject.new({ client_id: 'different', connection_id: 'same' }, protocol_message: protocol_message) }
|
77
77
|
|
78
78
|
it 'is unique' do
|
79
79
|
expect(client_1.member_key).to_not eql(client_2.member_key)
|
@@ -82,7 +82,7 @@ describe Ably::Models::PresenceMessage do
|
|
82
82
|
end
|
83
83
|
|
84
84
|
context '#timestamp' do
|
85
|
-
let(:model) { subject.new({}, protocol_message) }
|
85
|
+
let(:model) { subject.new({}, protocol_message: protocol_message) }
|
86
86
|
it 'retrieves attribute :timestamp as a Time object from ProtocolMessage' do
|
87
87
|
expect(model.timestamp).to be_a(Time)
|
88
88
|
expect(model.timestamp.to_i).to be_within(1).of(Time.now.to_i)
|
@@ -90,7 +90,7 @@ describe Ably::Models::PresenceMessage do
|
|
90
90
|
end
|
91
91
|
|
92
92
|
context 'Java naming', :api_private do
|
93
|
-
let(:model) { subject.new({ clientId: 'joe' }, protocol_message) }
|
93
|
+
let(:model) { subject.new({ clientId: 'joe' }, protocol_message: protocol_message) }
|
94
94
|
|
95
95
|
it 'converts the attribute to ruby symbol naming convention' do
|
96
96
|
expect(model.client_id).to eql('joe')
|
@@ -99,7 +99,7 @@ describe Ably::Models::PresenceMessage do
|
|
99
99
|
|
100
100
|
context 'with action', :api_private do
|
101
101
|
context 'absent' do
|
102
|
-
let(:model) { subject.new({ action: 0 }, protocol_message) }
|
102
|
+
let(:model) { subject.new({ action: 0 }, protocol_message: protocol_message) }
|
103
103
|
|
104
104
|
it 'provides action as an Enum' do
|
105
105
|
expect(model.action).to eq(:absent)
|
@@ -107,7 +107,7 @@ describe Ably::Models::PresenceMessage do
|
|
107
107
|
end
|
108
108
|
|
109
109
|
context 'enter' do
|
110
|
-
let(:model) { subject.new({ action: 2 }, protocol_message) }
|
110
|
+
let(:model) { subject.new({ action: 2 }, protocol_message: protocol_message) }
|
111
111
|
|
112
112
|
it 'provides action as an Enum' do
|
113
113
|
expect(model.action).to eq(:enter)
|
@@ -116,7 +116,7 @@ describe Ably::Models::PresenceMessage do
|
|
116
116
|
end
|
117
117
|
|
118
118
|
context 'without action', :api_private do
|
119
|
-
let(:model) { subject.new({}, protocol_message) }
|
119
|
+
let(:model) { subject.new({}, protocol_message: protocol_message) }
|
120
120
|
|
121
121
|
it 'raises an exception when accessed' do
|
122
122
|
expect { model.action }.to raise_error KeyError
|
@@ -129,7 +129,7 @@ describe Ably::Models::PresenceMessage do
|
|
129
129
|
let(:encoded_value) { value.encode(encoding) }
|
130
130
|
let(:value) { random_str }
|
131
131
|
let(:options) { { attribute.to_sym => encoded_value } }
|
132
|
-
let(:model) { subject.new(options, protocol_message) }
|
132
|
+
let(:model) { subject.new(options, protocol_message: protocol_message) }
|
133
133
|
let(:model_attribute) { model.public_send(attribute) }
|
134
134
|
|
135
135
|
context 'as UTF_8 string' do
|
@@ -191,7 +191,7 @@ describe Ably::Models::PresenceMessage do
|
|
191
191
|
let(:json_object) { JSON.parse(model.to_json) }
|
192
192
|
|
193
193
|
context 'with valid data' do
|
194
|
-
let(:model) { subject.new({ action: 'enter', clientId: 'joe' }, protocol_message) }
|
194
|
+
let(:model) { subject.new({ action: 'enter', clientId: 'joe' }, protocol_message: protocol_message) }
|
195
195
|
|
196
196
|
it 'converts the attribute back to Java mixedCase notation using string keys' do
|
197
197
|
expect(json_object["clientId"]).to eql('joe')
|
@@ -199,7 +199,7 @@ describe Ably::Models::PresenceMessage do
|
|
199
199
|
end
|
200
200
|
|
201
201
|
context 'with invalid data' do
|
202
|
-
let(:model) { subject.new({ clientId: 'joe' }, protocol_message) }
|
202
|
+
let(:model) { subject.new({ clientId: 'joe' }, protocol_message: protocol_message) }
|
203
203
|
|
204
204
|
it 'raises an exception' do
|
205
205
|
expect { model.to_json }.to raise_error KeyError, /cannot generate a valid Hash/
|
@@ -208,7 +208,7 @@ describe Ably::Models::PresenceMessage do
|
|
208
208
|
|
209
209
|
context 'with binary data' do
|
210
210
|
let(:data) { MessagePack.pack(random_str(32)) }
|
211
|
-
let(:model) { subject.new({ action: 'enter', data: data }, protocol_message) }
|
211
|
+
let(:model) { subject.new({ action: 'enter', data: data }, protocol_message: protocol_message) }
|
212
212
|
|
213
213
|
it 'encodes as Base64 so that it can be converted to UTF-8 automatically by JSON#dump' do
|
214
214
|
expect(json_object["data"]).to eql(::Base64.encode64(data))
|
@@ -319,7 +319,7 @@ describe Ably::Models::PresenceMessage do
|
|
319
319
|
end
|
320
320
|
|
321
321
|
context 'with ProtocolMessage' do
|
322
|
-
subject { Ably::Models.PresenceMessage(json, protocol_message) }
|
322
|
+
subject { Ably::Models.PresenceMessage(json, protocol_message: protocol_message) }
|
323
323
|
|
324
324
|
it 'returns a PresenceMessage object' do
|
325
325
|
expect(subject).to be_a(Ably::Models::PresenceMessage)
|
@@ -363,7 +363,7 @@ describe Ably::Models::PresenceMessage do
|
|
363
363
|
end
|
364
364
|
|
365
365
|
context 'with ProtocolMessage' do
|
366
|
-
subject { Ably::Models.PresenceMessage(message, protocol_message) }
|
366
|
+
subject { Ably::Models.PresenceMessage(message, protocol_message: protocol_message) }
|
367
367
|
|
368
368
|
it 'returns a PresenceMessage object' do
|
369
369
|
expect(subject).to be_a(Ably::Models::PresenceMessage)
|
@@ -64,22 +64,22 @@ describe Ably::Models::Stat do
|
|
64
64
|
|
65
65
|
describe '#from_interval_id' do
|
66
66
|
it 'converts a month interval_id 2014-02 into a Time object in UTC 0' do
|
67
|
-
expect(subject.from_interval_id('2014-02')).to eql(Time.
|
67
|
+
expect(subject.from_interval_id('2014-02')).to eql(Time.gm(2014, 2))
|
68
68
|
expect(subject.from_interval_id('2014-02').utc_offset).to eql(0)
|
69
69
|
end
|
70
70
|
|
71
71
|
it 'converts a day interval_id 2014-02-03 into a Time object in UTC 0' do
|
72
|
-
expect(subject.from_interval_id('2014-02-03')).to eql(Time.
|
72
|
+
expect(subject.from_interval_id('2014-02-03')).to eql(Time.gm(2014, 2, 3))
|
73
73
|
expect(subject.from_interval_id('2014-02-03').utc_offset).to eql(0)
|
74
74
|
end
|
75
75
|
|
76
76
|
it 'converts an hour interval_id 2014-02-03:05 into a Time object in UTC 0' do
|
77
|
-
expect(subject.from_interval_id('2014-02-03:05')).to eql(Time.
|
77
|
+
expect(subject.from_interval_id('2014-02-03:05')).to eql(Time.gm(2014, 2, 3, 5))
|
78
78
|
expect(subject.from_interval_id('2014-02-03:05').utc_offset).to eql(0)
|
79
79
|
end
|
80
80
|
|
81
81
|
it 'converts a minute interval_id 2014-02-03:05:06 into a Time object in UTC 0' do
|
82
|
-
expect(subject.from_interval_id('2014-02-03:05:06')).to eql(Time.
|
82
|
+
expect(subject.from_interval_id('2014-02-03:05:06')).to eql(Time.gm(2014, 2, 3, 5, 6))
|
83
83
|
expect(subject.from_interval_id('2014-02-03:05:06').utc_offset).to eql(0)
|
84
84
|
end
|
85
85
|
|
@@ -14,6 +14,10 @@ describe Ably::Modules::AsyncWrapper, :api_private do
|
|
14
14
|
def block=(block)
|
15
15
|
@block = block
|
16
16
|
end
|
17
|
+
|
18
|
+
def logger
|
19
|
+
true
|
20
|
+
end
|
17
21
|
end
|
18
22
|
end
|
19
23
|
let(:subject) { class_with_module.new }
|
@@ -33,7 +37,15 @@ describe Ably::Modules::AsyncWrapper, :api_private do
|
|
33
37
|
end
|
34
38
|
end
|
35
39
|
|
36
|
-
it '
|
40
|
+
it 'returns a SafeDeferrable that catches and logs exceptions in the provided callbacks' do
|
41
|
+
run_reactor do
|
42
|
+
deferrable = subject.operation
|
43
|
+
expect(deferrable).to be_a(Ably::Util::SafeDeferrable)
|
44
|
+
stop_reactor
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'calls the provided block with result when provided' do
|
37
49
|
run_reactor do
|
38
50
|
subject.operation do |result|
|
39
51
|
expect(result).to eql(result)
|
@@ -42,10 +54,20 @@ describe Ably::Modules::AsyncWrapper, :api_private do
|
|
42
54
|
end
|
43
55
|
end
|
44
56
|
|
45
|
-
it '
|
57
|
+
it 'catches exceptions in the provided block and logs them to logger' do
|
58
|
+
run_reactor do
|
59
|
+
subject.operation do |result|
|
60
|
+
raise 'Intentional exception'
|
61
|
+
end
|
62
|
+
expect(subject.logger).to receive(:error).with(/Intentional exception/) do
|
63
|
+
stop_reactor
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'returns a SafeDeferrable that calls the callback block' do
|
46
69
|
run_reactor do
|
47
70
|
deferrable = subject.operation
|
48
|
-
expect(deferrable).to be_a(EventMachine::Deferrable)
|
49
71
|
deferrable.callback do |result|
|
50
72
|
expect(result).to eql(result)
|
51
73
|
stop_reactor
|
@@ -56,7 +78,6 @@ describe Ably::Modules::AsyncWrapper, :api_private do
|
|
56
78
|
it 'does not call the errback' do
|
57
79
|
run_reactor do
|
58
80
|
deferrable = subject.operation
|
59
|
-
expect(deferrable).to be_a(EventMachine::Deferrable)
|
60
81
|
deferrable.callback do |result|
|
61
82
|
expect(result).to eql(result)
|
62
83
|
EventMachine.add_timer(sleep_time * 2) { stop_reactor }
|
@@ -88,10 +109,9 @@ describe Ably::Modules::AsyncWrapper, :api_private do
|
|
88
109
|
end
|
89
110
|
end
|
90
111
|
|
91
|
-
it 'calls the errback block of the
|
112
|
+
it 'calls the errback block of the SafeDeferrable' do
|
92
113
|
run_reactor do
|
93
114
|
deferrable = subject.operation
|
94
|
-
expect(deferrable).to be_a(EventMachine::Deferrable)
|
95
115
|
deferrable.errback do |error|
|
96
116
|
expect(error).to be_a(RuntimeError)
|
97
117
|
expect(error.message).to match(/Intentional/)
|
@@ -100,7 +120,7 @@ describe Ably::Modules::AsyncWrapper, :api_private do
|
|
100
120
|
end
|
101
121
|
end
|
102
122
|
|
103
|
-
it 'does not call the
|
123
|
+
it 'does not call the provided block' do
|
104
124
|
run_reactor do
|
105
125
|
subject.operation do |result|
|
106
126
|
raise 'Callback should not have been called'
|
@@ -109,10 +129,9 @@ describe Ably::Modules::AsyncWrapper, :api_private do
|
|
109
129
|
end
|
110
130
|
end
|
111
131
|
|
112
|
-
it 'does not call the callback block of the
|
132
|
+
it 'does not call the callback block of the SafeDeferrable' do
|
113
133
|
run_reactor do
|
114
134
|
deferrable = subject.operation
|
115
|
-
expect(deferrable).to be_a(EventMachine::Deferrable)
|
116
135
|
deferrable.callback do |result|
|
117
136
|
raise 'Callback should not have been called'
|
118
137
|
end
|
@@ -7,6 +7,7 @@ describe Ably::Modules::EventEmitter do
|
|
7
7
|
Class.new do
|
8
8
|
include Ably::Modules::EventEmitter
|
9
9
|
configure_event_emitter callback_opts
|
10
|
+
def logger; end
|
10
11
|
end
|
11
12
|
end
|
12
13
|
let(:obj) { double('example') }
|
@@ -124,6 +125,35 @@ describe Ably::Modules::EventEmitter do
|
|
124
125
|
end
|
125
126
|
end
|
126
127
|
|
128
|
+
context '#on' do
|
129
|
+
it 'calls the block every time an event is emitted only' do
|
130
|
+
block_called = 0
|
131
|
+
subject.on('event') { block_called += 1 }
|
132
|
+
3.times { subject.trigger 'event', 'data' }
|
133
|
+
expect(block_called).to eql(3)
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'catches exceptions in the provided block, logs the error and continues' do
|
137
|
+
expect(subject.logger).to receive(:error).with(/Intentional exception/)
|
138
|
+
subject.on(:event) { raise 'Intentional exception' }
|
139
|
+
subject.trigger :event
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context '#unsafe_on', api_private: true do
|
144
|
+
it 'calls the block every time an event is emitted only' do
|
145
|
+
block_called = 0
|
146
|
+
subject.unsafe_on('event') { block_called += 1 }
|
147
|
+
3.times { subject.trigger 'event', 'data' }
|
148
|
+
expect(block_called).to eql(3)
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'does not catch exceptions in provided blocks' do
|
152
|
+
subject.unsafe_on(:event) { raise 'Intentional exception' }
|
153
|
+
expect { subject.trigger :event }.to raise_error(/Intentional exception/)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
127
157
|
context '#once' do
|
128
158
|
it 'calls the block the first time an event is emitted only' do
|
129
159
|
block_called = 0
|
@@ -139,6 +169,26 @@ describe Ably::Modules::EventEmitter do
|
|
139
169
|
3.times { subject.trigger 'event', 'data' }
|
140
170
|
expect(block_called).to eql(4)
|
141
171
|
end
|
172
|
+
|
173
|
+
it 'catches exceptions in the provided block, logs the error and continues' do
|
174
|
+
expect(subject.logger).to receive(:error).with(/Intentional exception/)
|
175
|
+
subject.once(:event) { raise 'Intentional exception' }
|
176
|
+
subject.trigger :event
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
context '#unsafe_once' do
|
181
|
+
it 'calls the block the first time an event is emitted only' do
|
182
|
+
block_called = 0
|
183
|
+
subject.unsafe_once('event') { block_called += 1 }
|
184
|
+
3.times { subject.trigger 'event', 'data' }
|
185
|
+
expect(block_called).to eql(1)
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'does not catch exceptions in provided blocks' do
|
189
|
+
subject.unsafe_once(:event) { raise 'Intentional exception' }
|
190
|
+
expect { subject.trigger :event }.to raise_error(/Intentional exception/)
|
191
|
+
end
|
142
192
|
end
|
143
193
|
|
144
194
|
context '#off' do
|
@@ -13,14 +13,17 @@ describe Ably::Modules::StateEmitter do
|
|
13
13
|
)
|
14
14
|
include Ably::Modules::StateEmitter
|
15
15
|
|
16
|
-
def initialize
|
16
|
+
def initialize(logger)
|
17
17
|
@state = :initializing
|
18
|
+
@logger = logger
|
18
19
|
end
|
20
|
+
|
21
|
+
attr_reader :logger
|
19
22
|
end
|
20
23
|
|
21
24
|
let(:initial_state) { :initializing }
|
22
25
|
|
23
|
-
subject { ExampleStateWithEventEmitter.new }
|
26
|
+
subject { ExampleStateWithEventEmitter.new(double('Logger').as_null_object) }
|
24
27
|
|
25
28
|
specify '#state returns current state' do
|
26
29
|
expect(subject.state).to eq(:initializing)
|
@@ -248,6 +251,64 @@ describe Ably::Modules::StateEmitter do
|
|
248
251
|
subject.change_state :connecting, *arguments
|
249
252
|
end
|
250
253
|
end
|
254
|
+
|
255
|
+
context 'with blocks that raise exceptions' do
|
256
|
+
let(:success_block) do
|
257
|
+
proc { raise 'Success exception' }
|
258
|
+
end
|
259
|
+
|
260
|
+
let(:failure_block) do
|
261
|
+
proc { raise 'Failure exception' }
|
262
|
+
end
|
263
|
+
|
264
|
+
let(:target_state) { :connected }
|
265
|
+
|
266
|
+
before do
|
267
|
+
subject.once_or_if target_state, else: failure_block, &success_block
|
268
|
+
end
|
269
|
+
|
270
|
+
context 'success block' do
|
271
|
+
it 'catches exceptions in the provided block, logs the error and continues' do
|
272
|
+
expect(subject.logger).to receive(:error).with(/Success exception/)
|
273
|
+
subject.change_state target_state
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
context 'failure block' do
|
278
|
+
it 'catches exceptions in the provided block, logs the error and continues' do
|
279
|
+
expect(subject.logger).to receive(:error).with(/Failure exception/)
|
280
|
+
subject.change_state :connecting
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
context '#unsafe_once_or_if', :api_private do
|
287
|
+
let(:target_state) { :connected }
|
288
|
+
|
289
|
+
let(:success_block) do
|
290
|
+
proc { raise 'Success exception' }
|
291
|
+
end
|
292
|
+
|
293
|
+
let(:failure_block) do
|
294
|
+
proc { raise 'Failure exception' }
|
295
|
+
end
|
296
|
+
|
297
|
+
before do
|
298
|
+
subject.unsafe_once_or_if target_state, else: failure_block, &success_block
|
299
|
+
end
|
300
|
+
|
301
|
+
context 'success block' do
|
302
|
+
it 'catches exceptions in the provided block, logs the error and continues' do
|
303
|
+
expect { subject.change_state target_state }.to raise_error(/Success exception/)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
context 'failure block' do
|
308
|
+
it 'catches exceptions in the provided block, logs the error and continues' do
|
309
|
+
expect { subject.change_state :connecting }.to raise_error(/Failure exception/)
|
310
|
+
end
|
311
|
+
end
|
251
312
|
end
|
252
313
|
|
253
314
|
context '#once_state_changed', :api_private do
|
@@ -279,5 +340,18 @@ describe Ably::Modules::StateEmitter do
|
|
279
340
|
expect(block_calls.count).to eql(1)
|
280
341
|
expect(block_calls.first).to contain_exactly(1, 2)
|
281
342
|
end
|
343
|
+
|
344
|
+
it 'catches exceptions in the provided block, logs the error and continues' do
|
345
|
+
subject.once_state_changed { raise 'Intentional exception' }
|
346
|
+
expect(subject.logger).to receive(:error).with(/Intentional exception/)
|
347
|
+
subject.change_state :connected
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
context '#unsafe_once_state_changed', :api_private do
|
352
|
+
it 'does not catch exceptions in the provided block' do
|
353
|
+
subject.unsafe_once_state_changed { raise 'Intentional exception' }
|
354
|
+
expect { subject.change_state :connected }.to raise_error(/Intentional exception/)
|
355
|
+
end
|
282
356
|
end
|
283
357
|
end
|
@@ -133,7 +133,7 @@ describe Ably::Realtime::Channel do
|
|
133
133
|
Ably::Models::Message.new({
|
134
134
|
'name' => 'test',
|
135
135
|
'data' => 'payload'
|
136
|
-
}, instance_double('Ably::Models::ProtocolMessage'))
|
136
|
+
}, protocol_message: instance_double('Ably::Models::ProtocolMessage'))
|
137
137
|
end
|
138
138
|
let(:msgbus) { subject.__incoming_msgbus__ }
|
139
139
|
|
@@ -152,20 +152,45 @@ describe Ably::Realtime::Channel do
|
|
152
152
|
|
153
153
|
context 'subscriptions' do
|
154
154
|
let(:message_history) { Hash.new { |hash, key| hash[key] = 0 } }
|
155
|
-
let(:
|
156
|
-
let(:
|
155
|
+
let(:click_event) { 'click' }
|
156
|
+
let(:click_message) { instance_double('Ably::Models::Message', name: click_event, encode: nil, decode: nil) }
|
157
|
+
let(:focus_event) { 'focus' }
|
158
|
+
let(:focus_message) { instance_double('Ably::Models::Message', name: focus_event, encode: nil, decode: nil) }
|
159
|
+
let(:blur_message) { instance_double('Ably::Models::Message', name: 'blur', encode: nil, decode: nil) }
|
157
160
|
|
158
161
|
context '#subscribe' do
|
159
|
-
specify '
|
162
|
+
specify 'without a block raises an invalid ArgumentError' do
|
163
|
+
expect { subject.subscribe }.to raise_error ArgumentError
|
164
|
+
end
|
165
|
+
|
166
|
+
specify 'with no event name specified subscribes the provided block to all events' do
|
160
167
|
subject.subscribe { |message| message_history[:received] += 1}
|
161
|
-
subject.__incoming_msgbus__.publish(:message,
|
168
|
+
subject.__incoming_msgbus__.publish(:message, click_message)
|
169
|
+
expect(message_history[:received]).to eql(1)
|
170
|
+
end
|
171
|
+
|
172
|
+
specify 'with a single event name subscribes that block to matching events' do
|
173
|
+
subject.subscribe(click_event) { |message| message_history[:received] += 1 }
|
174
|
+
subject.subscribe('non_match_move') { |message| message_history[:received] += 1 }
|
175
|
+
subject.__incoming_msgbus__.publish(:message, click_message)
|
176
|
+
expect(message_history[:received]).to eql(1)
|
177
|
+
end
|
178
|
+
|
179
|
+
specify 'with a multiple event name arguments subscribes that block to all of those event names' do
|
180
|
+
subject.subscribe(focus_event, click_event) { |message| message_history[:received] += 1 }
|
181
|
+
subject.__incoming_msgbus__.publish(:message, click_message)
|
162
182
|
expect(message_history[:received]).to eql(1)
|
183
|
+
subject.__incoming_msgbus__.publish(:message, focus_message)
|
184
|
+
expect(message_history[:received]).to eql(2)
|
185
|
+
|
186
|
+
# Blur does not match subscribed focus & click events
|
187
|
+
subject.__incoming_msgbus__.publish(:message, blur_message)
|
188
|
+
expect(message_history[:received]).to eql(2)
|
163
189
|
end
|
164
190
|
|
165
|
-
specify 'to
|
166
|
-
subject.subscribe(
|
167
|
-
subject.
|
168
|
-
subject.__incoming_msgbus__.publish(:message, message)
|
191
|
+
specify 'with a multiple duplicate event name arguments subscribes that block to all of those unique event names once' do
|
192
|
+
subject.subscribe(click_event, click_event) { |message| message_history[:received] += 1 }
|
193
|
+
subject.__incoming_msgbus__.publish(:message, click_message)
|
169
194
|
expect(message_history[:received]).to eql(1)
|
170
195
|
end
|
171
196
|
end
|
@@ -175,30 +200,36 @@ describe Ably::Realtime::Channel do
|
|
175
200
|
Proc.new { |message| message_history[:received] += 1 }
|
176
201
|
end
|
177
202
|
before do
|
178
|
-
subject.subscribe(
|
203
|
+
subject.subscribe(click_event, &callback)
|
179
204
|
end
|
180
205
|
|
181
|
-
specify '
|
206
|
+
specify 'with no event name specified unsubscribes that block from all events' do
|
182
207
|
subject.unsubscribe &callback
|
183
|
-
subject.__incoming_msgbus__.publish(:message,
|
208
|
+
subject.__incoming_msgbus__.publish(:message, click_message)
|
209
|
+
expect(message_history[:received]).to eql(0)
|
210
|
+
end
|
211
|
+
|
212
|
+
specify 'with a single event name argument unsubscribes the provided block with the matching event name' do
|
213
|
+
subject.unsubscribe click_event, &callback
|
214
|
+
subject.__incoming_msgbus__.publish(:message, click_message)
|
184
215
|
expect(message_history[:received]).to eql(0)
|
185
216
|
end
|
186
217
|
|
187
|
-
specify '
|
188
|
-
subject.unsubscribe
|
189
|
-
subject.__incoming_msgbus__.publish(:message,
|
218
|
+
specify 'with multiple event name arguments unsubscribes each of those matching event names with the provided block' do
|
219
|
+
subject.unsubscribe focus_event, click_event, &callback
|
220
|
+
subject.__incoming_msgbus__.publish(:message, click_message)
|
190
221
|
expect(message_history[:received]).to eql(0)
|
191
222
|
end
|
192
223
|
|
193
|
-
specify '
|
224
|
+
specify 'with a non-matching event name argument has no effect' do
|
194
225
|
subject.unsubscribe 'move', &callback
|
195
|
-
subject.__incoming_msgbus__.publish(:message,
|
226
|
+
subject.__incoming_msgbus__.publish(:message, click_message)
|
196
227
|
expect(message_history[:received]).to eql(1)
|
197
228
|
end
|
198
229
|
|
199
|
-
specify 'all
|
200
|
-
subject.unsubscribe
|
201
|
-
subject.__incoming_msgbus__.publish(:message,
|
230
|
+
specify 'with no block argument unsubscribes all blocks for the event name argument' do
|
231
|
+
subject.unsubscribe click_event
|
232
|
+
subject.__incoming_msgbus__.publish(:message, click_message)
|
202
233
|
expect(message_history[:received]).to eql(0)
|
203
234
|
end
|
204
235
|
end
|