ably 0.1.4 → 0.1.5
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/ably.gemspec +1 -0
- data/lib/ably/auth.rb +9 -13
- data/lib/ably/models/idiomatic_ruby_wrapper.rb +27 -39
- data/lib/ably/modules/conversions.rb +31 -10
- data/lib/ably/modules/enum.rb +201 -0
- data/lib/ably/modules/event_emitter.rb +81 -0
- data/lib/ably/modules/event_machine_helpers.rb +21 -0
- data/lib/ably/modules/http_helpers.rb +13 -0
- data/lib/ably/modules/state.rb +67 -0
- data/lib/ably/realtime.rb +6 -1
- data/lib/ably/realtime/channel.rb +117 -56
- data/lib/ably/realtime/client.rb +7 -50
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +116 -0
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +63 -0
- data/lib/ably/realtime/connection.rb +97 -14
- data/lib/ably/realtime/models/error_info.rb +3 -2
- data/lib/ably/realtime/models/message.rb +28 -3
- data/lib/ably/realtime/models/nil_channel.rb +21 -0
- data/lib/ably/realtime/models/protocol_message.rb +35 -27
- data/lib/ably/rest/client.rb +39 -23
- data/lib/ably/rest/middleware/external_exceptions.rb +1 -1
- data/lib/ably/rest/middleware/parse_json.rb +7 -2
- data/lib/ably/rest/middleware/parse_message_pack.rb +23 -0
- data/lib/ably/rest/models/paged_resource.rb +4 -4
- data/lib/ably/util/pub_sub.rb +32 -0
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/channel_spec.rb +1 -0
- data/spec/acceptance/realtime/message_spec.rb +136 -0
- data/spec/acceptance/rest/base_spec.rb +51 -1
- data/spec/acceptance/rest/presence_spec.rb +7 -2
- data/spec/integration/modules/state_spec.rb +66 -0
- data/spec/{unit → integration/rest}/auth.rb +0 -0
- data/spec/support/api_helper.rb +5 -2
- data/spec/support/protocol_msgbus_helper.rb +29 -0
- data/spec/support/test_app.rb +14 -3
- data/spec/unit/{conversions.rb → modules/conversions_spec.rb} +1 -1
- data/spec/unit/modules/enum_spec.rb +263 -0
- data/spec/unit/modules/event_emitter_spec.rb +81 -0
- data/spec/unit/modules/pub_sub_spec.rb +74 -0
- data/spec/unit/realtime/channel_spec.rb +27 -0
- data/spec/unit/realtime/client_spec.rb +8 -0
- data/spec/unit/realtime/connection_spec.rb +40 -0
- data/spec/unit/realtime/error_info_spec.rb +9 -1
- data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +36 -0
- data/spec/unit/realtime/message_spec.rb +2 -2
- data/spec/unit/realtime/protocol_message_spec.rb +78 -9
- data/spec/unit/rest/{rest_spec.rb → client_spec.rb} +0 -0
- data/spec/unit/rest/message_spec.rb +1 -1
- metadata +51 -9
- data/lib/ably/realtime/callbacks.rb +0 -15
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ably::Modules::State 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::State
|
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 '#state?' do
|
38
|
+
it 'returns true if state matches' do
|
39
|
+
expect(subject.state?(initial_state)).to eql(true)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'returns false if state does not match' do
|
43
|
+
expect(subject.state?(:connecting)).to eql(false)
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'and convenience predicates for states' do
|
47
|
+
it 'returns true if state matches' do
|
48
|
+
expect(subject.initializing?).to eql(true)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'returns false if state does not match' do
|
52
|
+
expect(subject.connecting?).to eql(false)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context '#state STATE coercion' do
|
58
|
+
it 'allows valid STATE values' do
|
59
|
+
expect { subject.state = :connected }.to_not raise_error
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'prevents invalid STATE values' do
|
63
|
+
expect { subject.state = :invalid }.to raise_error KeyError
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
File without changes
|
data/spec/support/api_helper.rb
CHANGED
@@ -17,6 +17,10 @@ module ApiHelper
|
|
17
17
|
TestApp.instance.api_key
|
18
18
|
end
|
19
19
|
|
20
|
+
def restricted_api_key
|
21
|
+
TestApp.instance.restricted_api_key
|
22
|
+
end
|
23
|
+
|
20
24
|
def environment
|
21
25
|
TestApp.instance.environment
|
22
26
|
end
|
@@ -31,11 +35,10 @@ RSpec.configure do |config|
|
|
31
35
|
|
32
36
|
config.before(:suite) do
|
33
37
|
WebMock.disable!
|
34
|
-
TestApp.instance
|
35
38
|
end
|
36
39
|
|
37
40
|
config.after(:suite) do
|
38
41
|
WebMock.disable!
|
39
|
-
TestApp.instance.delete
|
42
|
+
TestApp.instance.delete if TestApp.instance_variable_get('@singleton__instance__')
|
40
43
|
end
|
41
44
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
shared_examples 'a protocol message bus' do
|
2
|
+
describe '__protocol_msgbus__ PubSub' do
|
3
|
+
let(:message) { double(:message, name: 'name', channel: 'channel', messages: []) }
|
4
|
+
|
5
|
+
specify 'supports valid ProtocolMessage messages' do
|
6
|
+
received = 0
|
7
|
+
msgbus.subscribe(:message) { received += 1 }
|
8
|
+
expect { msgbus.publish(:message, message) }.to change { received }.to(1)
|
9
|
+
end
|
10
|
+
|
11
|
+
specify 'fail with unacceptable STATE event names' do
|
12
|
+
expect { msgbus.subscribe(:invalid) }.to raise_error KeyError
|
13
|
+
expect { msgbus.publish(:invalid) }.to raise_error KeyError
|
14
|
+
expect { msgbus.unsubscribe(:invalid) }.to raise_error KeyError
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
shared_examples 'an incoming protocol message bus' do
|
20
|
+
it_behaves_like 'a protocol message bus' do
|
21
|
+
let(:msgbus) { subject.__incoming_protocol_msgbus__ }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
shared_examples 'an outgoing protocol message bus' do
|
26
|
+
it_behaves_like 'a protocol message bus' do
|
27
|
+
let(:msgbus) { subject.__outgoing_protocol_msgbus__ }
|
28
|
+
end
|
29
|
+
end
|
data/spec/support/test_app.rb
CHANGED
@@ -3,7 +3,10 @@ require "singleton"
|
|
3
3
|
class TestApp
|
4
4
|
APP_SPEC = {
|
5
5
|
'keys' => [
|
6
|
-
{}
|
6
|
+
{},
|
7
|
+
{
|
8
|
+
'capability' => '{ "*":["subscribe"], "canpublish:*":["publish"], "canpublish:andpresence":["presence","publish"] }'
|
9
|
+
}
|
7
10
|
],
|
8
11
|
'namespaces' => [
|
9
12
|
{ 'id' => 'persisted', 'persisted' => true }
|
@@ -44,18 +47,26 @@ class TestApp
|
|
44
47
|
@attributes["keys"].first
|
45
48
|
end
|
46
49
|
|
50
|
+
def restricted_key
|
51
|
+
@attributes["keys"][1]
|
52
|
+
end
|
53
|
+
|
47
54
|
def key_id
|
48
|
-
"#{app_id}.#{key[
|
55
|
+
"#{app_id}.#{key['id']}"
|
49
56
|
end
|
50
57
|
|
51
58
|
def key_value
|
52
|
-
key[
|
59
|
+
key['value']
|
53
60
|
end
|
54
61
|
|
55
62
|
def api_key
|
56
63
|
"#{key_id}:#{key_value}"
|
57
64
|
end
|
58
65
|
|
66
|
+
def restricted_api_key
|
67
|
+
"#{app_id}.#{restricted_key['id']}:#{restricted_key['value']}"
|
68
|
+
end
|
69
|
+
|
59
70
|
def delete
|
60
71
|
url = "#{sandbox_client.endpoint}/apps/#{app_id}"
|
61
72
|
|
@@ -0,0 +1,263 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
describe Ably::Modules::Enum do
|
5
|
+
class ExampleClassWithEnum
|
6
|
+
extend Ably::Modules::Enum
|
7
|
+
ENUMEXAMPLE = ruby_enum('ENUMEXAMPLE', :value_zero, 'value_1', :value_snake_case_2, :SentenceCase)
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:enum) { ExampleClassWithEnum::ENUMEXAMPLE }
|
11
|
+
let(:enum_name) { 'ENUMEXAMPLE' }
|
12
|
+
|
13
|
+
context 'convertor method added to class' do
|
14
|
+
subject { ExampleClassWithEnum }
|
15
|
+
|
16
|
+
it 'converts symbols' do
|
17
|
+
expect(subject.ENUMEXAMPLE(:value_zero)).to eql(enum.get(:value_zero))
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'converts strings' do
|
21
|
+
expect(subject.ENUMEXAMPLE('ValueZero')).to eql(enum.get(:value_zero))
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'converts integer index' do
|
25
|
+
expect(subject.ENUMEXAMPLE(0)).to eql(enum.get(:value_zero))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'convertor method added to instance' do
|
30
|
+
subject { ExampleClassWithEnum.new }
|
31
|
+
|
32
|
+
it 'converts symbols' do
|
33
|
+
expect(subject.ENUMEXAMPLE(:value_zero)).to eql(enum.get(:value_zero))
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'converts strings' do
|
37
|
+
expect(subject.ENUMEXAMPLE('ValueZero')).to eql(enum.get(:value_zero))
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'converts integer index' do
|
41
|
+
expect(subject.ENUMEXAMPLE(0)).to eql(enum.get(:value_zero))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'defined Enum from Array class' do
|
46
|
+
subject { enum }
|
47
|
+
|
48
|
+
it 'provides a MixedCase const for each provided value' do
|
49
|
+
expect(subject.ValueZero).to be_a(subject)
|
50
|
+
expect(subject.ValueSnakeCase2).to be_a(subject)
|
51
|
+
end
|
52
|
+
|
53
|
+
context '#get' do
|
54
|
+
context 'by integer index' do
|
55
|
+
let(:return_val) { subject.get(0) }
|
56
|
+
it 'returns an enum' do
|
57
|
+
expect(return_val).to be_a(subject)
|
58
|
+
expect(return_val.to_sym).to eql(:value_zero)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'by string value' do
|
63
|
+
let(:return_val) { subject.get('value_zero') }
|
64
|
+
it 'returns an enum' do
|
65
|
+
expect(return_val).to be_a(subject)
|
66
|
+
expect(return_val.to_sym).to eql(:value_zero)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'by symbol' do
|
71
|
+
let(:return_val) { subject.get(:sentence_case) }
|
72
|
+
it 'returns an enum' do
|
73
|
+
expect(return_val).to be_a(subject)
|
74
|
+
expect(return_val.to_sym).to eql(:sentence_case)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'by enum' do
|
79
|
+
let(:return_val) { subject.get(subject.get(:sentence_case)) }
|
80
|
+
it 'returns an enum' do
|
81
|
+
expect(return_val).to be_a(subject)
|
82
|
+
expect(return_val.to_sym).to eql(:sentence_case)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'by invalid type' do
|
87
|
+
let(:return_val) { subject.get(Array.new) }
|
88
|
+
it 'raises an error' do
|
89
|
+
expect { return_val }.to raise_error KeyError
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context '#[]' do
|
95
|
+
let(:argument) { SecureRandom.hex }
|
96
|
+
before do
|
97
|
+
expect(subject).to receive(:get).with(argument).once.and_return(true)
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'is an alias for get' do
|
101
|
+
subject[argument]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'returns the provided Enum name' do
|
106
|
+
expect(subject.name).to eql(enum_name)
|
107
|
+
end
|
108
|
+
|
109
|
+
specify '#to_s returns the Enum name' do
|
110
|
+
expect("#{subject}").to eql(enum_name)
|
111
|
+
end
|
112
|
+
|
113
|
+
context '#size' do
|
114
|
+
it 'returns the number of enum items' do
|
115
|
+
expect(subject.size).to eql(4)
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'has alias #length' do
|
119
|
+
expect(subject.length).to eql(subject.size)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'freezes the Enum' do
|
124
|
+
expect(subject.send(:by_index)).to be_frozen
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'prevents modification' do
|
128
|
+
expect { subject.send(:define_values, :enum_id) }.to raise_error RuntimeError, /cannot be modified/
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'behaves like Enumerable' do
|
132
|
+
expect(subject.map(&:to_sym)).to eql([:value_zero, :value_1, :value_snake_case_2, :sentence_case])
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context 'defined Enum from Hash class' do
|
137
|
+
class ExampleClassWithEnumFromHash
|
138
|
+
extend Ably::Modules::Enum
|
139
|
+
ENUMEXAMPLE = ruby_enum('ENUMEXAMPLE',
|
140
|
+
value_one: 1,
|
141
|
+
value_five: 5
|
142
|
+
)
|
143
|
+
end
|
144
|
+
|
145
|
+
subject { ExampleClassWithEnumFromHash::ENUMEXAMPLE }
|
146
|
+
|
147
|
+
it 'provides a MixedCase const for each provided value' do
|
148
|
+
expect(subject.ValueOne).to be_a(subject)
|
149
|
+
expect(subject.ValueFive).to be_a(subject)
|
150
|
+
end
|
151
|
+
|
152
|
+
context '#get' do
|
153
|
+
context 'by integer index for 1' do
|
154
|
+
let(:return_val) { subject.get(1) }
|
155
|
+
it 'returns an enum' do
|
156
|
+
expect(return_val).to be_a(subject)
|
157
|
+
expect(return_val.to_sym).to eql(:value_one)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
context 'by integer index for 5' do
|
162
|
+
let(:return_val) { subject.get(5) }
|
163
|
+
it 'returns an enum' do
|
164
|
+
expect(return_val).to be_a(subject)
|
165
|
+
expect(return_val.to_sym).to eql(:value_five)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context 'by invalid integer index' do
|
170
|
+
let(:return_val) { subject.get(0) }
|
171
|
+
it 'raises an exception' do
|
172
|
+
expect { return_val }.to raise_error KeyError
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
context 'by string value' do
|
177
|
+
let(:return_val) { subject.get('value_five') }
|
178
|
+
it 'returns an enum' do
|
179
|
+
expect(return_val).to be_a(subject)
|
180
|
+
expect(return_val.to_sym).to eql(:value_five)
|
181
|
+
expect(return_val.to_i).to eql(5)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context 'defined Enum from another Enum' do
|
188
|
+
class ExampleBaseEnum
|
189
|
+
extend Ably::Modules::Enum
|
190
|
+
ENUMEXAMPLE = ruby_enum('ENUMEXAMPLE', :one, :second_enum)
|
191
|
+
end
|
192
|
+
|
193
|
+
class ExampleOtherEnum
|
194
|
+
extend Ably::Modules::Enum
|
195
|
+
ENUMEXAMPLE = ruby_enum('ENUMEXAMPLE', ExampleBaseEnum::ENUMEXAMPLE)
|
196
|
+
end
|
197
|
+
|
198
|
+
subject { ExampleOtherEnum::ENUMEXAMPLE }
|
199
|
+
|
200
|
+
it 'provides a MixedCase const for each provided value' do
|
201
|
+
expect(subject.One).to be_a(subject)
|
202
|
+
expect(subject.SecondEnum).to be_a(subject)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
context 'Enum instance' do
|
207
|
+
context '#==' do
|
208
|
+
subject { enum.get(:value_snake_case_2) }
|
209
|
+
|
210
|
+
it 'compares with a symbol' do
|
211
|
+
expect(subject).to eq(:value_Snake_Case_2)
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'compares with a string' do
|
215
|
+
expect(subject).to eq('ValueSnakeCase2')
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'compares with a integer index' do
|
219
|
+
expect(subject).to eq(subject.to_i)
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'compares with itself' do
|
223
|
+
expect(subject).to eq(subject)
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'compares with other Objects' do
|
227
|
+
expect(subject).to_not eq(Object.new)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context '#to_s' do
|
232
|
+
subject { enum.get(:value_zero) }
|
233
|
+
|
234
|
+
it 'returns ENUMNAME.CamelCase name' do
|
235
|
+
expect(subject.to_s).to eql("#{enum_name}.ValueZero")
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
context '#to_sym' do
|
240
|
+
subject { enum.get(:value_zero) }
|
241
|
+
|
242
|
+
it 'returns a snake_case symbol' do
|
243
|
+
expect(subject.to_sym).to eql(:value_zero)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
context '#to_i' do
|
248
|
+
subject { enum.get(:value_1) }
|
249
|
+
|
250
|
+
it 'returns the Enum index' do
|
251
|
+
expect(subject.to_i).to eql(1)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
context '#to_json' do
|
256
|
+
subject { enum.get(:value_1) }
|
257
|
+
|
258
|
+
it 'returns a symbol string key value' do
|
259
|
+
expect(subject.to_json).to eql('"value_1"')
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
@@ -0,0 +1,81 @@
|
|
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 'event fan out' do
|
18
|
+
specify do
|
19
|
+
expect(obj).to receive(:received_message).with(msg).twice
|
20
|
+
2.times do
|
21
|
+
subject.on(:message) { |msg| obj.received_message msg }
|
22
|
+
end
|
23
|
+
subject.trigger :message, msg
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'sends only messages to matching event names' do
|
27
|
+
expect(obj).to receive(:received_message).with(msg).once
|
28
|
+
subject.on(:valid) { |msg| obj.received_message msg }
|
29
|
+
subject.trigger :valid, msg
|
30
|
+
subject.trigger :ignored, msg
|
31
|
+
subject.trigger 'valid', msg
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'with coercion' do
|
35
|
+
let(:options) do
|
36
|
+
{ coerce_into: Proc.new { |event| String(event) } }
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'calls the provided proc to coerce the event name' do
|
40
|
+
expect(obj).to receive(:received_message).with(msg).once
|
41
|
+
subject.on('valid') { |msg| obj.received_message msg }
|
42
|
+
subject.trigger :valid, msg
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'without coercion' do
|
47
|
+
it 'only matches event names on type matches' do
|
48
|
+
expect(obj).to_not receive(:received_message).with(msg)
|
49
|
+
subject.on('valid') { |msg| obj.received_message msg }
|
50
|
+
subject.trigger :valid, msg
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context '#off' do
|
56
|
+
let(:callback) { Proc.new { |msg| obj.received_message msg } }
|
57
|
+
|
58
|
+
before do
|
59
|
+
subject.on(:message, &callback)
|
60
|
+
end
|
61
|
+
|
62
|
+
after do
|
63
|
+
subject.trigger :message, msg
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'deletes matching callbacks' do
|
67
|
+
expect(obj).to_not receive(:received_message).with(msg)
|
68
|
+
subject.off(:message, &callback)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'deletes all callbacks if not block given' do
|
72
|
+
expect(obj).to_not receive(:received_message).with(msg)
|
73
|
+
subject.off(:message)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'continues if the block does not exist' do
|
77
|
+
expect(obj).to receive(:received_message).with(msg)
|
78
|
+
subject.off(:message) { true }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|