ably 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/.ruby-version.old +1 -0
- data/.travis.yml +0 -2
- data/Rakefile +22 -4
- data/SPEC.md +1676 -0
- data/ably.gemspec +1 -1
- data/lib/ably.rb +0 -8
- data/lib/ably/auth.rb +54 -46
- data/lib/ably/exceptions.rb +19 -5
- data/lib/ably/logger.rb +1 -1
- data/lib/ably/models/error_info.rb +1 -1
- data/lib/ably/models/idiomatic_ruby_wrapper.rb +11 -9
- data/lib/ably/models/message.rb +15 -12
- data/lib/ably/models/message_encoders/base.rb +6 -5
- data/lib/ably/models/message_encoders/base64.rb +1 -0
- data/lib/ably/models/message_encoders/cipher.rb +6 -3
- data/lib/ably/models/message_encoders/json.rb +1 -0
- data/lib/ably/models/message_encoders/utf8.rb +2 -9
- data/lib/ably/models/nil_logger.rb +20 -0
- data/lib/ably/models/paginated_resource.rb +5 -2
- data/lib/ably/models/presence_message.rb +21 -12
- data/lib/ably/models/protocol_message.rb +22 -6
- data/lib/ably/modules/ably.rb +11 -0
- data/lib/ably/modules/async_wrapper.rb +2 -0
- data/lib/ably/modules/conversions.rb +23 -3
- data/lib/ably/modules/encodeable.rb +2 -1
- data/lib/ably/modules/enum.rb +2 -0
- data/lib/ably/modules/event_emitter.rb +7 -1
- data/lib/ably/modules/event_machine_helpers.rb +2 -0
- data/lib/ably/modules/http_helpers.rb +2 -0
- data/lib/ably/modules/model_common.rb +12 -2
- data/lib/ably/modules/state_emitter.rb +76 -0
- data/lib/ably/modules/state_machine.rb +53 -0
- data/lib/ably/modules/statesman_monkey_patch.rb +33 -0
- data/lib/ably/modules/uses_state_machine.rb +74 -0
- data/lib/ably/realtime.rb +4 -2
- data/lib/ably/realtime/channel.rb +51 -58
- data/lib/ably/realtime/channel/channel_manager.rb +91 -0
- data/lib/ably/realtime/channel/channel_state_machine.rb +68 -0
- data/lib/ably/realtime/client.rb +70 -26
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +31 -13
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +1 -1
- data/lib/ably/realtime/connection.rb +135 -92
- data/lib/ably/realtime/connection/connection_manager.rb +216 -33
- data/lib/ably/realtime/connection/connection_state_machine.rb +30 -73
- data/lib/ably/realtime/models/nil_channel.rb +10 -1
- data/lib/ably/realtime/presence.rb +336 -92
- data/lib/ably/rest.rb +2 -2
- data/lib/ably/rest/channel.rb +13 -4
- data/lib/ably/rest/client.rb +138 -38
- data/lib/ably/rest/middleware/logger.rb +24 -3
- data/lib/ably/rest/presence.rb +12 -7
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/channel_history_spec.rb +101 -85
- data/spec/acceptance/realtime/channel_spec.rb +461 -120
- data/spec/acceptance/realtime/client_spec.rb +119 -0
- data/spec/acceptance/realtime/connection_failures_spec.rb +499 -0
- data/spec/acceptance/realtime/connection_spec.rb +571 -97
- data/spec/acceptance/realtime/message_spec.rb +347 -333
- data/spec/acceptance/realtime/presence_history_spec.rb +35 -40
- data/spec/acceptance/realtime/presence_spec.rb +769 -239
- data/spec/acceptance/realtime/stats_spec.rb +14 -22
- data/spec/acceptance/realtime/time_spec.rb +16 -20
- data/spec/acceptance/rest/auth_spec.rb +425 -364
- data/spec/acceptance/rest/base_spec.rb +108 -176
- data/spec/acceptance/rest/channel_spec.rb +89 -89
- data/spec/acceptance/rest/channels_spec.rb +30 -32
- data/spec/acceptance/rest/client_spec.rb +273 -0
- data/spec/acceptance/rest/encoders_spec.rb +185 -0
- data/spec/acceptance/rest/message_spec.rb +186 -163
- data/spec/acceptance/rest/presence_spec.rb +150 -111
- data/spec/acceptance/rest/stats_spec.rb +45 -40
- data/spec/acceptance/rest/time_spec.rb +8 -10
- data/spec/rspec_config.rb +10 -1
- data/spec/shared/client_initializer_behaviour.rb +212 -0
- data/spec/{support/model_helper.rb → shared/model_behaviour.rb} +6 -6
- data/spec/{support/protocol_msgbus_helper.rb → shared/protocol_msgbus_behaviour.rb} +1 -1
- data/spec/spec_helper.rb +9 -0
- data/spec/support/api_helper.rb +11 -0
- data/spec/support/event_machine_helper.rb +101 -3
- data/spec/support/markdown_spec_formatter.rb +90 -0
- data/spec/support/private_api_formatter.rb +36 -0
- data/spec/support/protocol_helper.rb +32 -0
- data/spec/support/random_helper.rb +15 -0
- data/spec/support/test_app.rb +4 -0
- data/spec/unit/auth_spec.rb +68 -0
- data/spec/unit/logger_spec.rb +77 -66
- data/spec/unit/models/error_info_spec.rb +1 -1
- data/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +2 -3
- data/spec/unit/models/message_encoders/base64_spec.rb +2 -2
- data/spec/unit/models/message_encoders/cipher_spec.rb +2 -2
- data/spec/unit/models/message_encoders/utf8_spec.rb +2 -46
- data/spec/unit/models/message_spec.rb +160 -15
- data/spec/unit/models/paginated_resource_spec.rb +29 -27
- data/spec/unit/models/presence_message_spec.rb +163 -20
- data/spec/unit/models/protocol_message_spec.rb +43 -8
- data/spec/unit/modules/async_wrapper_spec.rb +2 -3
- data/spec/unit/modules/conversions_spec.rb +1 -1
- data/spec/unit/modules/enum_spec.rb +2 -3
- data/spec/unit/modules/event_emitter_spec.rb +62 -5
- data/spec/unit/modules/state_emitter_spec.rb +283 -0
- data/spec/unit/realtime/channel_spec.rb +107 -2
- data/spec/unit/realtime/channels_spec.rb +1 -0
- data/spec/unit/realtime/client_spec.rb +8 -48
- data/spec/unit/realtime/connection_spec.rb +3 -3
- data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +2 -2
- data/spec/unit/realtime/presence_spec.rb +13 -4
- data/spec/unit/realtime/realtime_spec.rb +0 -11
- data/spec/unit/realtime/websocket_transport_spec.rb +2 -2
- data/spec/unit/rest/channel_spec.rb +109 -0
- data/spec/unit/rest/channels_spec.rb +4 -3
- data/spec/unit/rest/client_spec.rb +30 -125
- data/spec/unit/rest/rest_spec.rb +10 -0
- data/spec/unit/util/crypto_spec.rb +10 -5
- data/spec/unit/util/pub_sub_spec.rb +5 -5
- metadata +44 -12
- data/spec/integration/modules/state_emitter_spec.rb +0 -80
- data/spec/integration/rest/auth.rb +0 -9
@@ -1,5 +1,6 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'spec_helper'
|
2
|
-
require '
|
3
|
+
require 'shared/model_behaviour'
|
3
4
|
|
4
5
|
describe Ably::Models::ProtocolMessage do
|
5
6
|
include Ably::Modules::Conversions
|
@@ -16,7 +17,7 @@ describe Ably::Models::ProtocolMessage do
|
|
16
17
|
let(:model_args) { [] }
|
17
18
|
end
|
18
19
|
|
19
|
-
context 'initializer action coercion' do
|
20
|
+
context 'initializer action coercion', :api_private do
|
20
21
|
it 'ignores actions that are Integers' do
|
21
22
|
protocol_message = subject.new(action: 14)
|
22
23
|
expect(protocol_message.hash[:action]).to eql(14)
|
@@ -38,9 +39,9 @@ describe Ably::Models::ProtocolMessage do
|
|
38
39
|
end
|
39
40
|
|
40
41
|
context 'attributes' do
|
41
|
-
let(:unique_value) {
|
42
|
+
let(:unique_value) { random_str }
|
42
43
|
|
43
|
-
context 'Java naming' do
|
44
|
+
context 'Java naming', :api_private do
|
44
45
|
let(:protocol_message) { new_protocol_message(channelSerial: unique_value) }
|
45
46
|
|
46
47
|
it 'converts the attribute to ruby symbol naming convention' do
|
@@ -48,7 +49,7 @@ describe Ably::Models::ProtocolMessage do
|
|
48
49
|
end
|
49
50
|
end
|
50
51
|
|
51
|
-
context '#action' do
|
52
|
+
context '#action', :api_private do
|
52
53
|
let(:protocol_message) { new_protocol_message(action: 14) }
|
53
54
|
|
54
55
|
it 'returns an Enum that behaves like a symbol' do
|
@@ -70,7 +71,7 @@ describe Ably::Models::ProtocolMessage do
|
|
70
71
|
|
71
72
|
context '#timestamp' do
|
72
73
|
let(:protocol_message) { new_protocol_message(timestamp: as_since_epoch(Time.now)) }
|
73
|
-
it 'retrieves attribute :timestamp' do
|
74
|
+
it 'retrieves attribute :timestamp as Time object' do
|
74
75
|
expect(protocol_message.timestamp).to be_a(Time)
|
75
76
|
expect(protocol_message.timestamp.to_i).to be_within(1).of(Time.now.to_i)
|
76
77
|
end
|
@@ -133,6 +134,40 @@ describe Ably::Models::ProtocolMessage do
|
|
133
134
|
end
|
134
135
|
end
|
135
136
|
|
137
|
+
context '#flags' do
|
138
|
+
context 'when nil' do
|
139
|
+
let(:protocol_message) { new_protocol_message({}) }
|
140
|
+
|
141
|
+
it 'is zero' do
|
142
|
+
expect(protocol_message.flags).to eql(0)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context 'when numeric' do
|
147
|
+
let(:protocol_message) { new_protocol_message(flags: '25') }
|
148
|
+
|
149
|
+
it 'is an Integer' do
|
150
|
+
expect(protocol_message.flags).to eql(25)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
context 'when has_presence' do
|
155
|
+
let(:protocol_message) { new_protocol_message(flags: 1) }
|
156
|
+
|
157
|
+
it '#has_presence_flag? is true' do
|
158
|
+
expect(protocol_message.has_presence_flag?).to be_truthy
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context 'when has another future flag' do
|
163
|
+
let(:protocol_message) { new_protocol_message(flags: 2) }
|
164
|
+
|
165
|
+
it '#has_presence_flag? is false' do
|
166
|
+
expect(protocol_message.has_presence_flag?).to be_falsey
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
136
171
|
context '#has_connection_serial?' do
|
137
172
|
context 'without connection_serial' do
|
138
173
|
let(:protocol_message) { new_protocol_message({}) }
|
@@ -231,7 +266,7 @@ describe Ably::Models::ProtocolMessage do
|
|
231
266
|
end
|
232
267
|
end
|
233
268
|
|
234
|
-
context '#to_json' do
|
269
|
+
context '#to_json', :api_private do
|
235
270
|
let(:json_object) { JSON.parse(model.to_json) }
|
236
271
|
let(:message) { { 'name' => 'event', 'clientId' => 'joe', 'timestamp' => as_since_epoch(Time.now) } }
|
237
272
|
let(:attached_action) { Ably::Models::ProtocolMessage::ACTION.Attached }
|
@@ -266,7 +301,7 @@ describe Ably::Models::ProtocolMessage do
|
|
266
301
|
end
|
267
302
|
end
|
268
303
|
|
269
|
-
context '#to_msgpack' do
|
304
|
+
context '#to_msgpack', :api_private do
|
270
305
|
let(:model) { new_protocol_message({ :connectionSerial => 'unique', messages: [message] }) }
|
271
306
|
let(:message) { { 'name' => 'event', 'clientId' => 'joe', 'timestamp' => as_since_epoch(Time.now) } }
|
272
307
|
let(:packed) { model.to_msgpack }
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'securerandom'
|
3
2
|
|
4
|
-
describe Ably::Modules::AsyncWrapper do
|
3
|
+
describe Ably::Modules::AsyncWrapper, :api_private do
|
5
4
|
include RSpec::EventMachine
|
6
5
|
|
7
6
|
let(:class_with_module) do
|
@@ -18,7 +17,7 @@ describe Ably::Modules::AsyncWrapper do
|
|
18
17
|
end
|
19
18
|
end
|
20
19
|
let(:subject) { class_with_module.new }
|
21
|
-
let(:result) {
|
20
|
+
let(:result) { random_str }
|
22
21
|
let(:sleep_time) { 0.1 }
|
23
22
|
|
24
23
|
before do
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'securerandom'
|
3
2
|
|
4
|
-
describe Ably::Modules::Enum do
|
3
|
+
describe Ably::Modules::Enum, :api_private do
|
5
4
|
class ExampleClassWithEnum
|
6
5
|
extend Ably::Modules::Enum
|
7
6
|
ENUMEXAMPLE = ruby_enum('ENUMEXAMPLE', :value_zero, 'value_1', :value_snake_case_2, :SentenceCase)
|
@@ -92,7 +91,7 @@ describe Ably::Modules::Enum do
|
|
92
91
|
end
|
93
92
|
|
94
93
|
context '#[]' do
|
95
|
-
let(:argument) {
|
94
|
+
let(:argument) { random_str }
|
96
95
|
before do
|
97
96
|
expect(subject).to receive(:get).with(argument).once.and_return(true)
|
98
97
|
end
|
@@ -14,8 +14,8 @@ describe Ably::Modules::EventEmitter do
|
|
14
14
|
|
15
15
|
subject { klass.new }
|
16
16
|
|
17
|
-
context 'event fan out' do
|
18
|
-
|
17
|
+
context '#trigger event fan out' do
|
18
|
+
it 'should emit an event for any number of subscribers' do
|
19
19
|
2.times do
|
20
20
|
subject.on(:message) { |msg| obj.received_message msg }
|
21
21
|
end
|
@@ -33,7 +33,7 @@ describe Ably::Modules::EventEmitter do
|
|
33
33
|
subject.trigger 'valid', msg
|
34
34
|
end
|
35
35
|
|
36
|
-
context 'with coercion' do
|
36
|
+
context 'with coercion', :api_private do
|
37
37
|
let(:options) do
|
38
38
|
{ coerce_into: Proc.new { |event| String(event) } }
|
39
39
|
end
|
@@ -46,7 +46,7 @@ describe Ably::Modules::EventEmitter do
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
context 'without coercion' do
|
49
|
+
context 'without coercion', :api_private do
|
50
50
|
it 'only matches event names on type matches' do
|
51
51
|
subject.on('valid') { |msg| obj.received_message msg }
|
52
52
|
|
@@ -55,7 +55,7 @@ describe Ably::Modules::EventEmitter do
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
context 'subscribe to multiple events' do
|
58
|
+
context '#on subscribe to multiple events' do
|
59
59
|
it 'with the same block' do
|
60
60
|
subject.on(:click, :hover) { |msg| obj.received_message msg }
|
61
61
|
|
@@ -65,6 +65,63 @@ describe Ably::Modules::EventEmitter do
|
|
65
65
|
subject.trigger :hover, msg
|
66
66
|
end
|
67
67
|
end
|
68
|
+
|
69
|
+
context 'event callback changes within the callback block' do
|
70
|
+
context 'when new event callbacks are added' do
|
71
|
+
before do
|
72
|
+
2.times do
|
73
|
+
subject.on(:message) do |msg|
|
74
|
+
obj.received_message msg
|
75
|
+
subject.on(:message) do |msg|
|
76
|
+
obj.received_message_from_new_callbacks msg
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
allow(obj).to receive(:received_message)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'is unaffected and processes the prior event callbacks once' do
|
84
|
+
expect(obj).to receive(:received_message).with(msg).twice
|
85
|
+
expect(obj).to_not receive(:received_message_from_new_callbacks).with(msg)
|
86
|
+
subject.trigger :message, msg
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'adds them for the next emitted event' do
|
90
|
+
expect(obj).to receive(:received_message_from_new_callbacks).with(msg).twice
|
91
|
+
|
92
|
+
# New callbacks are added in this trigger
|
93
|
+
subject.trigger :message, msg
|
94
|
+
|
95
|
+
# New callbacks are now called with second event emitted
|
96
|
+
subject.trigger :message, msg
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'when callbacks are removed' do
|
101
|
+
before do
|
102
|
+
2.times do
|
103
|
+
subject.once(:message) do |msg|
|
104
|
+
obj.received_message msg
|
105
|
+
subject.off
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'is unaffected and processes the prior event callbacks once' do
|
111
|
+
expect(obj).to receive(:received_message).with(msg).twice
|
112
|
+
subject.trigger :message, msg
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'removes them for the next emitted event' do
|
116
|
+
expect(obj).to receive(:received_message).with(msg).twice
|
117
|
+
|
118
|
+
# Callbacks are removed in this trigger
|
119
|
+
subject.trigger :message, msg
|
120
|
+
# No callbacks should exist now
|
121
|
+
subject.trigger :message, msg
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
68
125
|
end
|
69
126
|
|
70
127
|
context '#once' do
|
@@ -0,0 +1,283 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ably::Modules::StateEmitter do
|
4
|
+
class ExampleStateWithEventEmitter
|
5
|
+
include Ably::Modules::EventEmitter
|
6
|
+
extend Ably::Modules::Enum
|
7
|
+
|
8
|
+
STATE = ruby_enum('STATE',
|
9
|
+
:initializing,
|
10
|
+
:connecting,
|
11
|
+
:connected,
|
12
|
+
:disconnected
|
13
|
+
)
|
14
|
+
include Ably::Modules::StateEmitter
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@state = :initializing
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:initial_state) { :initializing }
|
22
|
+
|
23
|
+
subject { ExampleStateWithEventEmitter.new }
|
24
|
+
|
25
|
+
specify '#state returns current state' do
|
26
|
+
expect(subject.state).to eq(:initializing)
|
27
|
+
end
|
28
|
+
|
29
|
+
specify '#state= sets current state' do
|
30
|
+
expect { subject.state = :connecting }.to change { subject.state }.to(:connecting)
|
31
|
+
end
|
32
|
+
|
33
|
+
specify '#change_state sets current state' do
|
34
|
+
expect { subject.change_state :connecting }.to change { subject.state }.to(:connecting)
|
35
|
+
end
|
36
|
+
|
37
|
+
context '#change_state with arguments' do
|
38
|
+
let(:args) { [5,3,1] }
|
39
|
+
let(:callback_status) { { called: false } }
|
40
|
+
|
41
|
+
it 'passes the arguments through to the triggered callback' do
|
42
|
+
subject.on(:connecting) do |*callback_args|
|
43
|
+
expect(callback_args).to eql(args)
|
44
|
+
callback_status[:called] = true
|
45
|
+
end
|
46
|
+
expect { subject.change_state :connecting, *args }.to change { subject.state }.to(:connecting)
|
47
|
+
expect(callback_status).to eql(called: true)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context '#state?' do
|
52
|
+
it 'returns true if state matches' do
|
53
|
+
expect(subject.state?(initial_state)).to eql(true)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'returns false if state does not match' do
|
57
|
+
expect(subject.state?(:connecting)).to eql(false)
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'and convenience predicates for states' do
|
61
|
+
it 'returns true for #initializing? if state matches' do
|
62
|
+
expect(subject.initializing?).to eql(true)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'returns false for #connecting? if state does not match' do
|
66
|
+
expect(subject.connecting?).to eql(false)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context '#state STATE coercion', :api_private do
|
72
|
+
it 'allows valid STATE values' do
|
73
|
+
expect { subject.state = :connected }.to_not raise_error
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'prevents invalid STATE values' do
|
77
|
+
expect { subject.state = :invalid }.to raise_error KeyError
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context '#once_or_if', :api_private do
|
82
|
+
context 'without :else option block' do
|
83
|
+
let(:block_calls) { [] }
|
84
|
+
let(:block) do
|
85
|
+
proc do
|
86
|
+
block_calls << Time.now
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'calls the block if in the provided state' do
|
91
|
+
subject.once_or_if initial_state, &block
|
92
|
+
expect(block_calls.count).to eql(1)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'calls the block when the state is reached' do
|
96
|
+
subject.once_or_if :connected, &block
|
97
|
+
expect(block_calls.count).to eql(0)
|
98
|
+
|
99
|
+
subject.change_state :connected
|
100
|
+
expect(block_calls.count).to eql(1)
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'calls the block only once' do
|
104
|
+
subject.once_or_if :connected, &block
|
105
|
+
3.times do
|
106
|
+
subject.change_state :connected
|
107
|
+
subject.change_state :connecting
|
108
|
+
end
|
109
|
+
expect(block_calls.count).to eql(1)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'with an array of targets' do
|
114
|
+
let(:block_calls) { [] }
|
115
|
+
let(:block) do
|
116
|
+
proc do
|
117
|
+
block_calls << Time.now
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'calls the block if in the provided state' do
|
122
|
+
subject.once_or_if [initial_state, :connecting], &block
|
123
|
+
expect(block_calls.count).to eql(1)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'calls the block when one of the states is reached' do
|
127
|
+
subject.once_or_if [:connecting, :connected], &block
|
128
|
+
expect(block_calls.count).to eql(0)
|
129
|
+
|
130
|
+
subject.change_state :connected
|
131
|
+
expect(block_calls.count).to eql(1)
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'calls the block only once' do
|
135
|
+
subject.once_or_if [:connecting, :connected], &block
|
136
|
+
expect(block_calls.count).to eql(0)
|
137
|
+
|
138
|
+
3.times do
|
139
|
+
subject.change_state :connected
|
140
|
+
subject.change_state :connecting
|
141
|
+
end
|
142
|
+
expect(block_calls.count).to eql(1)
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'does not remove all blocks on success' do
|
146
|
+
allow(subject).to receive(:off) do |&block|
|
147
|
+
raise 'Should not receive a nil block' if block.nil?
|
148
|
+
end
|
149
|
+
|
150
|
+
subject.once_or_if(:connected) { }
|
151
|
+
subject.change_state :connected
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
context 'with :else option block', :api_private do
|
156
|
+
let(:success_calls) { [] }
|
157
|
+
let(:success_block) do
|
158
|
+
proc do
|
159
|
+
success_calls << Time.now
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
let(:failure_calls) { [] }
|
164
|
+
let(:failure_block) do
|
165
|
+
proc do |*args|
|
166
|
+
failure_calls << args
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
let(:target_state) { :connected }
|
171
|
+
|
172
|
+
before do
|
173
|
+
subject.once_or_if target_state, else: failure_block, &success_block
|
174
|
+
end
|
175
|
+
|
176
|
+
context 'blocks' do
|
177
|
+
specify 'are not called if the state does not change' do
|
178
|
+
subject.change_state initial_state
|
179
|
+
expect(success_calls.count).to eql(0)
|
180
|
+
expect(failure_calls.count).to eql(0)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
context 'success block' do
|
185
|
+
it 'is called once target_state is reached' do
|
186
|
+
subject.change_state target_state
|
187
|
+
expect(success_calls.count).to eql(1)
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'is never called again once target_state is reached' do
|
191
|
+
subject.change_state target_state
|
192
|
+
subject.change_state :connecting
|
193
|
+
subject.change_state target_state
|
194
|
+
expect(success_calls.count).to eql(1)
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'is never called after failure block was called' do
|
198
|
+
subject.change_state :connecting
|
199
|
+
subject.change_state target_state
|
200
|
+
expect(success_calls.count).to eql(0)
|
201
|
+
expect(failure_calls.count).to eql(1)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
context 'failure block' do
|
206
|
+
it 'is called once a state other than target_state is reached' do
|
207
|
+
subject.change_state :connecting
|
208
|
+
expect(failure_calls.count).to eql(1)
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'is never called again once the block has been called previously' do
|
212
|
+
subject.change_state :connecting
|
213
|
+
subject.change_state target_state
|
214
|
+
subject.change_state :connecting
|
215
|
+
expect(failure_calls.count).to eql(1)
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'is never called after success block was called' do
|
219
|
+
subject.change_state target_state
|
220
|
+
subject.change_state :connecting
|
221
|
+
expect(failure_calls.count).to eql(0)
|
222
|
+
expect(success_calls.count).to eql(1)
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'has arguments from the error state' do
|
226
|
+
subject.change_state :disconnected, 1, 2
|
227
|
+
expect(failure_calls.count).to eql(1)
|
228
|
+
expect(failure_calls.first).to contain_exactly(1, 2)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
context 'state change arguments' do
|
234
|
+
let(:arguments) { [1,2,3] }
|
235
|
+
|
236
|
+
specify 'are passed to success blocks' do
|
237
|
+
subject.once_or_if(:connected) do |*arguments|
|
238
|
+
expect(arguments).to eql(arguments)
|
239
|
+
end
|
240
|
+
subject.change_state :connected, *arguments
|
241
|
+
end
|
242
|
+
|
243
|
+
specify 'are passed to else blocks' do
|
244
|
+
else_block = proc { |arguments| expect(arguments).to eql(arguments) }
|
245
|
+
subject.once_or_if(:connected, else: else_block) do
|
246
|
+
raise 'Success should not be called'
|
247
|
+
end
|
248
|
+
subject.change_state :connecting, *arguments
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
context '#once_state_changed', :api_private do
|
254
|
+
let(:block_calls) { [] }
|
255
|
+
let(:block) do
|
256
|
+
proc do |*args|
|
257
|
+
block_calls << args
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
it 'is not called if the state does not change' do
|
262
|
+
subject.once_state_changed &block
|
263
|
+
subject.change_state initial_state
|
264
|
+
expect(block_calls.count).to eql(0)
|
265
|
+
end
|
266
|
+
|
267
|
+
it 'calls the block for any state change once' do
|
268
|
+
subject.once_state_changed &block
|
269
|
+
3.times do
|
270
|
+
subject.change_state :connected
|
271
|
+
subject.change_state :connecting
|
272
|
+
end
|
273
|
+
expect(block_calls.count).to eql(1)
|
274
|
+
end
|
275
|
+
|
276
|
+
it 'emits arguments to the block' do
|
277
|
+
subject.once_state_changed &block
|
278
|
+
subject.change_state :connected, 1, 2
|
279
|
+
expect(block_calls.count).to eql(1)
|
280
|
+
expect(block_calls.first).to contain_exactly(1, 2)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|