ably-rest 0.7.1 → 0.7.3

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 (148) hide show
  1. checksums.yaml +13 -5
  2. data/.gitmodules +1 -1
  3. data/.rspec +1 -0
  4. data/.travis.yml +7 -3
  5. data/SPEC.md +495 -419
  6. data/ably-rest.gemspec +19 -5
  7. data/lib/ably-rest.rb +9 -1
  8. data/lib/submodules/ably-ruby/.gitignore +6 -0
  9. data/lib/submodules/ably-ruby/.rspec +1 -0
  10. data/lib/submodules/ably-ruby/.ruby-version.old +1 -0
  11. data/lib/submodules/ably-ruby/.travis.yml +10 -0
  12. data/lib/submodules/ably-ruby/Gemfile +4 -0
  13. data/lib/submodules/ably-ruby/LICENSE.txt +22 -0
  14. data/lib/submodules/ably-ruby/README.md +122 -0
  15. data/lib/submodules/ably-ruby/Rakefile +34 -0
  16. data/lib/submodules/ably-ruby/SPEC.md +1794 -0
  17. data/lib/submodules/ably-ruby/ably.gemspec +36 -0
  18. data/lib/submodules/ably-ruby/lib/ably.rb +12 -0
  19. data/lib/submodules/ably-ruby/lib/ably/auth.rb +438 -0
  20. data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +69 -0
  21. data/lib/submodules/ably-ruby/lib/ably/logger.rb +102 -0
  22. data/lib/submodules/ably-ruby/lib/ably/models/error_info.rb +37 -0
  23. data/lib/submodules/ably-ruby/lib/ably/models/idiomatic_ruby_wrapper.rb +223 -0
  24. data/lib/submodules/ably-ruby/lib/ably/models/message.rb +132 -0
  25. data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/base.rb +108 -0
  26. data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/base64.rb +40 -0
  27. data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/cipher.rb +83 -0
  28. data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/json.rb +34 -0
  29. data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/utf8.rb +26 -0
  30. data/lib/submodules/ably-ruby/lib/ably/models/nil_logger.rb +20 -0
  31. data/lib/submodules/ably-ruby/lib/ably/models/paginated_resource.rb +173 -0
  32. data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +147 -0
  33. data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +210 -0
  34. data/lib/submodules/ably-ruby/lib/ably/models/stat.rb +161 -0
  35. data/lib/submodules/ably-ruby/lib/ably/models/token.rb +74 -0
  36. data/lib/submodules/ably-ruby/lib/ably/modules/ably.rb +15 -0
  37. data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +62 -0
  38. data/lib/submodules/ably-ruby/lib/ably/modules/channels_collection.rb +69 -0
  39. data/lib/submodules/ably-ruby/lib/ably/modules/conversions.rb +100 -0
  40. data/lib/submodules/ably-ruby/lib/ably/modules/encodeable.rb +69 -0
  41. data/lib/submodules/ably-ruby/lib/ably/modules/enum.rb +202 -0
  42. data/lib/submodules/ably-ruby/lib/ably/modules/event_emitter.rb +128 -0
  43. data/lib/submodules/ably-ruby/lib/ably/modules/event_machine_helpers.rb +26 -0
  44. data/lib/submodules/ably-ruby/lib/ably/modules/http_helpers.rb +41 -0
  45. data/lib/submodules/ably-ruby/lib/ably/modules/message_pack.rb +14 -0
  46. data/lib/submodules/ably-ruby/lib/ably/modules/model_common.rb +41 -0
  47. data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +153 -0
  48. data/lib/submodules/ably-ruby/lib/ably/modules/state_machine.rb +57 -0
  49. data/lib/submodules/ably-ruby/lib/ably/modules/statesman_monkey_patch.rb +33 -0
  50. data/lib/submodules/ably-ruby/lib/ably/modules/uses_state_machine.rb +74 -0
  51. data/lib/submodules/ably-ruby/lib/ably/realtime.rb +64 -0
  52. data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +298 -0
  53. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +92 -0
  54. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +69 -0
  55. data/lib/submodules/ably-ruby/lib/ably/realtime/channels.rb +50 -0
  56. data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +184 -0
  57. data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +184 -0
  58. data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +70 -0
  59. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +445 -0
  60. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +368 -0
  61. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_state_machine.rb +91 -0
  62. data/lib/submodules/ably-ruby/lib/ably/realtime/connection/websocket_transport.rb +188 -0
  63. data/lib/submodules/ably-ruby/lib/ably/realtime/models/nil_channel.rb +30 -0
  64. data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +564 -0
  65. data/lib/submodules/ably-ruby/lib/ably/rest.rb +43 -0
  66. data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +104 -0
  67. data/lib/submodules/ably-ruby/lib/ably/rest/channels.rb +44 -0
  68. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +396 -0
  69. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/encoder.rb +49 -0
  70. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/exceptions.rb +41 -0
  71. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/external_exceptions.rb +24 -0
  72. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +17 -0
  73. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/logger.rb +58 -0
  74. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/parse_json.rb +27 -0
  75. data/lib/submodules/ably-ruby/lib/ably/rest/middleware/parse_message_pack.rb +27 -0
  76. data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +92 -0
  77. data/lib/submodules/ably-ruby/lib/ably/util/crypto.rb +105 -0
  78. data/lib/submodules/ably-ruby/lib/ably/util/pub_sub.rb +43 -0
  79. data/lib/submodules/ably-ruby/lib/ably/version.rb +3 -0
  80. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +154 -0
  81. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +558 -0
  82. data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +119 -0
  83. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +575 -0
  84. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +785 -0
  85. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +457 -0
  86. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +55 -0
  87. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +1001 -0
  88. data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +23 -0
  89. data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +27 -0
  90. data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +564 -0
  91. data/lib/submodules/ably-ruby/spec/acceptance/rest/base_spec.rb +165 -0
  92. data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +134 -0
  93. data/lib/submodules/ably-ruby/spec/acceptance/rest/channels_spec.rb +41 -0
  94. data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +273 -0
  95. data/lib/submodules/ably-ruby/spec/acceptance/rest/encoders_spec.rb +185 -0
  96. data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +247 -0
  97. data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +292 -0
  98. data/lib/submodules/ably-ruby/spec/acceptance/rest/stats_spec.rb +172 -0
  99. data/lib/submodules/ably-ruby/spec/acceptance/rest/time_spec.rb +15 -0
  100. data/lib/submodules/ably-ruby/spec/resources/crypto-data-128.json +56 -0
  101. data/lib/submodules/ably-ruby/spec/resources/crypto-data-256.json +56 -0
  102. data/lib/submodules/ably-ruby/spec/rspec_config.rb +57 -0
  103. data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +212 -0
  104. data/lib/submodules/ably-ruby/spec/shared/model_behaviour.rb +86 -0
  105. data/lib/submodules/ably-ruby/spec/shared/protocol_msgbus_behaviour.rb +36 -0
  106. data/lib/submodules/ably-ruby/spec/spec_helper.rb +20 -0
  107. data/lib/submodules/ably-ruby/spec/support/api_helper.rb +60 -0
  108. data/lib/submodules/ably-ruby/spec/support/event_machine_helper.rb +104 -0
  109. data/lib/submodules/ably-ruby/spec/support/markdown_spec_formatter.rb +118 -0
  110. data/lib/submodules/ably-ruby/spec/support/private_api_formatter.rb +36 -0
  111. data/lib/submodules/ably-ruby/spec/support/protocol_helper.rb +32 -0
  112. data/lib/submodules/ably-ruby/spec/support/random_helper.rb +15 -0
  113. data/lib/submodules/ably-ruby/spec/support/rest_testapp_before_retry.rb +15 -0
  114. data/lib/submodules/ably-ruby/spec/support/test_app.rb +113 -0
  115. data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +68 -0
  116. data/lib/submodules/ably-ruby/spec/unit/logger_spec.rb +146 -0
  117. data/lib/submodules/ably-ruby/spec/unit/models/error_info_spec.rb +18 -0
  118. data/lib/submodules/ably-ruby/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +349 -0
  119. data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/base64_spec.rb +181 -0
  120. data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/cipher_spec.rb +260 -0
  121. data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/json_spec.rb +135 -0
  122. data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/utf8_spec.rb +56 -0
  123. data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +389 -0
  124. data/lib/submodules/ably-ruby/spec/unit/models/paginated_resource_spec.rb +288 -0
  125. data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +386 -0
  126. data/lib/submodules/ably-ruby/spec/unit/models/protocol_message_spec.rb +315 -0
  127. data/lib/submodules/ably-ruby/spec/unit/models/stat_spec.rb +113 -0
  128. data/lib/submodules/ably-ruby/spec/unit/models/token_spec.rb +86 -0
  129. data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +124 -0
  130. data/lib/submodules/ably-ruby/spec/unit/modules/conversions_spec.rb +72 -0
  131. data/lib/submodules/ably-ruby/spec/unit/modules/enum_spec.rb +272 -0
  132. data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +184 -0
  133. data/lib/submodules/ably-ruby/spec/unit/modules/state_emitter_spec.rb +283 -0
  134. data/lib/submodules/ably-ruby/spec/unit/realtime/channel_spec.rb +206 -0
  135. data/lib/submodules/ably-ruby/spec/unit/realtime/channels_spec.rb +81 -0
  136. data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +30 -0
  137. data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +33 -0
  138. data/lib/submodules/ably-ruby/spec/unit/realtime/incoming_message_dispatcher_spec.rb +36 -0
  139. data/lib/submodules/ably-ruby/spec/unit/realtime/presence_spec.rb +111 -0
  140. data/lib/submodules/ably-ruby/spec/unit/realtime/realtime_spec.rb +9 -0
  141. data/lib/submodules/ably-ruby/spec/unit/realtime/websocket_transport_spec.rb +25 -0
  142. data/lib/submodules/ably-ruby/spec/unit/rest/channel_spec.rb +109 -0
  143. data/lib/submodules/ably-ruby/spec/unit/rest/channels_spec.rb +79 -0
  144. data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +53 -0
  145. data/lib/submodules/ably-ruby/spec/unit/rest/rest_spec.rb +10 -0
  146. data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +87 -0
  147. data/lib/submodules/ably-ruby/spec/unit/util/pub_sub_spec.rb +86 -0
  148. metadata +182 -27
@@ -0,0 +1,184 @@
1
+ require 'spec_helper'
2
+
3
+ describe Ably::Modules::EventEmitter do
4
+ let(:options) { {} }
5
+ let(:klass) do
6
+ callback_opts = options
7
+ Class.new do
8
+ include Ably::Modules::EventEmitter
9
+ configure_event_emitter callback_opts
10
+ end
11
+ end
12
+ let(:obj) { double('example') }
13
+ let(:msg) { double('message') }
14
+
15
+ subject { klass.new }
16
+
17
+ context '#trigger event fan out' do
18
+ it 'should emit an event for any number of subscribers' do
19
+ 2.times do
20
+ subject.on(:message) { |msg| obj.received_message msg }
21
+ end
22
+
23
+ expect(obj).to receive(:received_message).with(msg).twice
24
+ subject.trigger :message, msg
25
+ end
26
+
27
+ it 'sends only messages to matching event names' do
28
+ subject.on(:valid) { |msg| obj.received_message msg }
29
+
30
+ expect(obj).to receive(:received_message).with(msg).once
31
+ subject.trigger :valid, msg
32
+ subject.trigger :ignored, msg
33
+ subject.trigger 'valid', msg
34
+ end
35
+
36
+ context 'with coercion', :api_private do
37
+ let(:options) do
38
+ { coerce_into: Proc.new { |event| String(event) } }
39
+ end
40
+
41
+ it 'calls the provided proc to coerce the event name' do
42
+ subject.on('valid') { |msg| obj.received_message msg }
43
+
44
+ expect(obj).to receive(:received_message).with(msg).once
45
+ subject.trigger :valid, msg
46
+ end
47
+ end
48
+
49
+ context 'without coercion', :api_private do
50
+ it 'only matches event names on type matches' do
51
+ subject.on('valid') { |msg| obj.received_message msg }
52
+
53
+ expect(obj).to_not receive(:received_message).with(msg)
54
+ subject.trigger :valid, msg
55
+ end
56
+ end
57
+
58
+ context '#on subscribe to multiple events' do
59
+ it 'with the same block' do
60
+ subject.on(:click, :hover) { |msg| obj.received_message msg }
61
+
62
+ expect(obj).to receive(:received_message).with(msg).twice
63
+
64
+ subject.trigger :click, msg
65
+ subject.trigger :hover, msg
66
+ end
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
125
+ end
126
+
127
+ context '#once' do
128
+ it 'calls the block the first time an event is emitted only' do
129
+ block_called = 0
130
+ subject.once('event') { block_called += 1 }
131
+ 3.times { subject.trigger 'event', 'data' }
132
+ expect(block_called).to eql(1)
133
+ end
134
+
135
+ it 'does not remove other blocks after it is called' do
136
+ block_called = 0
137
+ subject.once('event') { block_called += 1 }
138
+ subject.on('event') { block_called += 1 }
139
+ 3.times { subject.trigger 'event', 'data' }
140
+ expect(block_called).to eql(4)
141
+ end
142
+ end
143
+
144
+ context '#off' do
145
+ let(:callback) { Proc.new { |msg| obj.received_message msg } }
146
+
147
+ before do
148
+ subject.on(:message, &callback)
149
+ end
150
+
151
+ after do
152
+ subject.trigger :message, msg
153
+ end
154
+
155
+ context 'with event names as arguments' do
156
+ it 'deletes matching callbacks' do
157
+ expect(obj).to_not receive(:received_message).with(msg)
158
+ subject.off(:message, &callback)
159
+ end
160
+
161
+ it 'deletes all callbacks if not block given' do
162
+ expect(obj).to_not receive(:received_message).with(msg)
163
+ subject.off(:message)
164
+ end
165
+
166
+ it 'continues if the block does not exist' do
167
+ expect(obj).to receive(:received_message).with(msg)
168
+ subject.off(:message) { true }
169
+ end
170
+ end
171
+
172
+ context 'without any event names' do
173
+ it 'deletes all matching callbacks' do
174
+ expect(obj).to_not receive(:received_message).with(msg)
175
+ subject.off(&callback)
176
+ end
177
+
178
+ it 'deletes all callbacks if not block given' do
179
+ expect(obj).to_not receive(:received_message).with(msg)
180
+ subject.off
181
+ end
182
+ end
183
+ end
184
+ end
@@ -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