amqp-events 0.0.2 → 0.0.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.
- data/HISTORY +4 -0
- data/Rakefile +1 -7
- data/VERSION +1 -1
- data/lib/amqp-events.rb +15 -2
- data/lib/amqp-events/event.rb +140 -0
- data/lib/amqp-events/event_manager.rb +19 -0
- data/lib/amqp-events/events.rb +47 -126
- data/spec/amqp-events/event_manager_spec.rb +63 -0
- data/spec/amqp-events/event_spec.rb +74 -0
- data/spec/amqp-events/events_spec.rb +89 -0
- data/spec/cs_spec.rb +2 -4
- data/spec/shared_examples.rb +282 -0
- data/spec/spec_helper.rb +40 -11
- data/tasks/spec.rake +3 -7
- metadata +37 -15
- data/spec/amqp-events_spec.rb +0 -214
- data/spec/spec.opts +0 -2
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AMQP::Events::Event, ' class' do
|
4
|
+
subject { AMQP::Events::Event }
|
5
|
+
|
6
|
+
specify { should respond_to :create }
|
7
|
+
|
8
|
+
it 'should hide its new method' do
|
9
|
+
expect { subject.new 'Test' }.to raise_error NoMethodError
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe AMQP::Events::Event, ' as created event' do
|
14
|
+
subject { AMQP::Events::Event.create self, 'TestEvent' }
|
15
|
+
|
16
|
+
its(:name) { should == :TestEvent }
|
17
|
+
its(:subscribers) { should be_empty }
|
18
|
+
|
19
|
+
it_should_behave_like 'event'
|
20
|
+
end
|
21
|
+
|
22
|
+
describe AMQP::Events::ExternalEvent, ' class' do
|
23
|
+
subject { AMQP::Events::ExternalEvent }
|
24
|
+
|
25
|
+
specify { should respond_to :create }
|
26
|
+
|
27
|
+
it 'should hide its new method' do
|
28
|
+
expect { subject.new 'Test' }.to raise_error NoMethodError
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'creating external Events' do
|
32
|
+
it 'impossible to create without :routing' do
|
33
|
+
expect { AMQP::Events::Event.create self, 'TestEvent', transport: @transport }.
|
34
|
+
to raise_error /Unable to create ExternalEvent .* without routing/
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'impossible to create without :transport' do
|
38
|
+
expect { AMQP::Events::Event.create self, 'TestEvent', routing: 'routing' }.
|
39
|
+
to raise_error /Unable to create ExternalEvent .* without transport/
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'unless host exposes #transport' do
|
43
|
+
def transport
|
44
|
+
'some transport'
|
45
|
+
end
|
46
|
+
expect { AMQP::Events::Event.create self, 'TestEvent', routing: 'routing' }.
|
47
|
+
to_not raise_error
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
describe AMQP::Events::ExternalEvent, ' as created event' do
|
54
|
+
before { @transport = mock('transport').as_null_object } # Needed by ExternalEvent to set up subscription
|
55
|
+
subject { AMQP::Events::Event.create self, 'TestEvent', routing: 'routing', transport: @transport }
|
56
|
+
|
57
|
+
specify { should be_an AMQP::Events::ExternalEvent }
|
58
|
+
specify { should respond_to :transport }
|
59
|
+
its(:transport) { should_not be_nil }
|
60
|
+
its(:name) { should == :TestEvent }
|
61
|
+
its(:subscribers) { should be_empty }
|
62
|
+
|
63
|
+
it_should_behave_like 'event'
|
64
|
+
|
65
|
+
it 'should fire when transport calls subscription...'
|
66
|
+
it 'should subscribe transport when it is subscribed to'
|
67
|
+
it 'should unsubscribe transport when no subscribers left...'
|
68
|
+
it 'should NOT unsubscribe transport when one subscriber unsubscribes, but there are others left...'
|
69
|
+
|
70
|
+
context 'for evented objects where ExternalEvents may be defined' do
|
71
|
+
it 'should raise error if existing Event is redefined with different options'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class TestClassWithoutEvents
|
4
|
+
include AMQP::Events
|
5
|
+
end
|
6
|
+
|
7
|
+
class TestClassWithEvents
|
8
|
+
include AMQP::Events
|
9
|
+
|
10
|
+
event :Bar
|
11
|
+
event :Baz
|
12
|
+
end
|
13
|
+
|
14
|
+
describe TestClassWithoutEvents, ' that includes AMQPEvents::Events' do
|
15
|
+
subject { TestClassWithoutEvents }
|
16
|
+
|
17
|
+
its(:instance_events) { should be_empty }
|
18
|
+
it_should_behave_like 'evented class'
|
19
|
+
end
|
20
|
+
|
21
|
+
describe TestClassWithoutEvents, ' when instantiated' do
|
22
|
+
|
23
|
+
its(:events) { should be_empty }
|
24
|
+
it_should_behave_like 'evented object'
|
25
|
+
|
26
|
+
context "when Event is defined for this object, it" do
|
27
|
+
before do
|
28
|
+
@object = TestClassWithoutEvents.new
|
29
|
+
@object.event :Bar
|
30
|
+
end
|
31
|
+
subject { @object.Bar }
|
32
|
+
it_should_behave_like 'event'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe TestClassWithEvents, ' that includes AMQPEvents::Events and pre-defines events Bar/Baz' do
|
37
|
+
subject { TestClassWithEvents }
|
38
|
+
|
39
|
+
its(:instance_events) { should include :Bar }
|
40
|
+
its(:instance_events) { should include :Baz }
|
41
|
+
|
42
|
+
it_should_behave_like 'evented class'
|
43
|
+
|
44
|
+
context 'creating new Events' do
|
45
|
+
before { @events_size = subject.instance_events.size }
|
46
|
+
|
47
|
+
it 'should create events on instance, with Symbol as a name' do
|
48
|
+
res = subject.event :Foo
|
49
|
+
res.should == :Foo
|
50
|
+
should_be_defined_event(subject.new, :Foo)
|
51
|
+
subject.instance_events.size.should == @events_size + 1
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should create events on instance, with String as a name' do
|
55
|
+
res = subject.event 'Boo'
|
56
|
+
res.should == :Boo
|
57
|
+
should_be_defined_event(subject.new, :Boo)
|
58
|
+
subject.instance_events.size.should == @events_size + 1
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should not redefine already defined events' do
|
62
|
+
res = subject.event :Bar
|
63
|
+
res.should == :Bar
|
64
|
+
should_be_defined_event(subject.new, :Bar)
|
65
|
+
subject.instance_events.size.should == @events_size
|
66
|
+
res = subject.event 'Bar'
|
67
|
+
res.should == :Bar
|
68
|
+
should_be_defined_event(subject.new, :Bar)
|
69
|
+
subject.instance_events.size.should == @events_size
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe TestClassWithEvents, ' when instantiated' do
|
75
|
+
|
76
|
+
it_should_behave_like 'evented object'
|
77
|
+
|
78
|
+
its(:events) { should have_key :Bar }
|
79
|
+
its(:events) { should have_key :Baz }
|
80
|
+
|
81
|
+
context "its pre-defined Events" do
|
82
|
+
before do
|
83
|
+
@object = TestClassWithEvents.new
|
84
|
+
end
|
85
|
+
subject { @object.Bar }
|
86
|
+
it_should_behave_like 'event'
|
87
|
+
end
|
88
|
+
end # TestClassWithEvents, ' when instantiated'
|
89
|
+
|
data/spec/cs_spec.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module EventsTest
|
1
|
+
require 'spec_helper'
|
2
|
+
|
4
3
|
describe AMQP::Events, ' when running Second Change Event Example' do
|
5
4
|
before { @clock = SecondChangeEvent::Clock.new }
|
6
5
|
let(:messages) { [] }
|
@@ -26,7 +25,6 @@ module EventsTest
|
|
26
25
|
end
|
27
26
|
|
28
27
|
end
|
29
|
-
end # module AMQPEventsTest
|
30
28
|
|
31
29
|
# This is a reproduction of "The Second Change Event Example" from:
|
32
30
|
# http://www.akadia.com/services/dotnet_delegates_and_events.html
|
@@ -0,0 +1,282 @@
|
|
1
|
+
|
2
|
+
shared_examples_for 'evented class' do
|
3
|
+
specify { should respond_to :instance_events }
|
4
|
+
specify { should respond_to :event }
|
5
|
+
its(:instance_events) { should be_a Hash }
|
6
|
+
|
7
|
+
end
|
8
|
+
|
9
|
+
shared_examples_for 'evented object' do
|
10
|
+
|
11
|
+
specify { should respond_to :event }
|
12
|
+
specify { should respond_to :events }
|
13
|
+
its(:events) { should be_a Hash }
|
14
|
+
|
15
|
+
it 'it`s events property contains Events that know about their name and host' do
|
16
|
+
subject.events.each do |name, event|
|
17
|
+
event.should be_kind_of AMQP::Events::Event
|
18
|
+
event.name.should == name
|
19
|
+
event.host.should == subject
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'with proc/method/block @subscribers and at least Bar event defined' do
|
24
|
+
before do
|
25
|
+
define_subscribers
|
26
|
+
subject.event :Bar unless subject.class.instance_events.include? :Bar
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'creating new (class-wide) Events' do
|
30
|
+
before { @events_size = subject.events.size }
|
31
|
+
|
32
|
+
it 'should create events on instance, with Symbol as a name' do
|
33
|
+
# object effectively defines new Event for all similar instances... Should it be allowed?
|
34
|
+
res = subject.event :Grumple
|
35
|
+
res.should == subject.Grumple # returns new Event
|
36
|
+
should_be_defined_event :Grumple
|
37
|
+
subject.events.size.should == @events_size + 1
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should create events on instance, with String as a name' do
|
41
|
+
# object effectively defines new Event for all similar instances... Should it be allowed?
|
42
|
+
res = subject.event 'Blurp'
|
43
|
+
res.should == subject.Blurp # returns new Event
|
44
|
+
should_be_defined_event :Blurp
|
45
|
+
subject.events.size.should == @events_size + 1
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should not redefine already defined events' do
|
49
|
+
res = subject.event :Bar
|
50
|
+
res.should == subject.Bar # returns existing Event
|
51
|
+
should_be_defined_event :Bar
|
52
|
+
subject.events.size.should == @events_size
|
53
|
+
|
54
|
+
res = subject.event 'Bar'
|
55
|
+
res.should == subject.Bar # returns existing Event
|
56
|
+
should_be_defined_event :Bar
|
57
|
+
subject.events.size.should == @events_size
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context '#Bar and #Bar= accessor method for defined Event' do
|
62
|
+
it 'should respond to #Bar and #Bar=' do
|
63
|
+
should respond_to :Bar
|
64
|
+
should respond_to :Bar=
|
65
|
+
end
|
66
|
+
|
67
|
+
specify 'calling #Bar without args or block returns Bar Event itself' do
|
68
|
+
subject.Bar.should == subject.events[:Bar]
|
69
|
+
end
|
70
|
+
|
71
|
+
specify 'calling #Bar without args, but WITH block subscribes block to Bar Event' do
|
72
|
+
subject.Bar { |*args| args.should == ["data"]; @counter += 1 }
|
73
|
+
|
74
|
+
subscribers_to_be_called 1, subject.Bar
|
75
|
+
end
|
76
|
+
|
77
|
+
specify 'calling #Bar with args fires Bar Event (like in C#)' do
|
78
|
+
subject.Bar.should_receive(:fire).with("whatever")
|
79
|
+
subject.Bar("whatever")
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'allows assignment of pre-defined Event to self (+=/-=)' do
|
83
|
+
events_size = subject.events.size
|
84
|
+
subject.Bar = subject.Bar
|
85
|
+
subject.Bar = subject.event(:Bar)
|
86
|
+
subject.Bar = subject.events[:Bar]
|
87
|
+
|
88
|
+
subject.class.instance_events.should include :Bar
|
89
|
+
subject.events.size.should == events_size
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'raises error if you try to assign anything else to Event' do
|
93
|
+
events_size = subject.events.size
|
94
|
+
other_event = AMQP::Events::Event.create(self, :Foo)
|
95
|
+
same_name_event = AMQP::Events::Event.create(self, :Bar)
|
96
|
+
other_object_event = AMQP::Events::Event.create(Object.new, :Bar)
|
97
|
+
|
98
|
+
[1, 'event', :Baz, other_event, same_name_event, other_object_event, proc {}].each do |rvalue|
|
99
|
+
expect { subject.Bar = rvalue }.to raise_error AMQP::Events::EventError, /Wrong assignment/
|
100
|
+
end
|
101
|
+
subject.class.instance_events.should include :Bar
|
102
|
+
subject.events.size.should == events_size
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
context "#subscribe to object's Event" do
|
108
|
+
|
109
|
+
it "allows access to object's Events through its #events property" do
|
110
|
+
subject.events[:Bar].subscribe(:bar1) { |*args| args.should == ["data"]; @counter += 1 }
|
111
|
+
subject.events[:Bar].subscribe(:bar2, method(:subscriber_method))
|
112
|
+
subject.events[:Bar].subscribe(:bar3, @subscriber_proc)
|
113
|
+
|
114
|
+
subscribers_to_be_called 3, subject.Bar
|
115
|
+
end
|
116
|
+
|
117
|
+
it "allows shorthand subscription through #Bar (blocks only!)" do
|
118
|
+
subject.Bar { |*args| args.should == ["data"]; @counter += 1 }
|
119
|
+
subject.Bar &@subscriber_proc
|
120
|
+
|
121
|
+
subscribers_to_be_called 2, subject.Bar
|
122
|
+
end
|
123
|
+
|
124
|
+
it "syntax-sugars object.Event#subscribe as object.subscribe(:Event)" do
|
125
|
+
subject.subscribe(:Bar) { |*args| args.should == ["data"]; @counter += 1 }
|
126
|
+
subject.subscribe(:Bar, :bar1, @subscriber_proc)
|
127
|
+
subject.subscribe(:Bar, :bar2, @subscriber_proc)
|
128
|
+
subject.subscribe(:Bar, :bar3, &@subscriber_proc)
|
129
|
+
subject.subscribe :Bar, @subscriber_proc
|
130
|
+
subject.subscribe :Bar, &@subscriber_proc
|
131
|
+
subject.listen :Bar, @subscriber_proc
|
132
|
+
subject.subscribe(:Bar, :bar4, method(:subscriber_method))
|
133
|
+
subject.subscribe(:Bar, :bar5, method(:subscriber_method))
|
134
|
+
res = subject.listen(:Bar, :bar6, method(:subscriber_method))
|
135
|
+
|
136
|
+
res.should be_an AMQP::Events::Event
|
137
|
+
res.name.should == :Bar
|
138
|
+
subscribers_to_be_called 10, subject.Bar
|
139
|
+
end
|
140
|
+
end #subscribe
|
141
|
+
|
142
|
+
context "#unsubscribe from object's Event" do
|
143
|
+
before { define_subscribers }
|
144
|
+
|
145
|
+
it "allows you to unsubscribe from Events by name" do
|
146
|
+
subject.events[:Bar].subscribe(:bar1) { |*args| args.should == ["data"]; @counter += 1 }
|
147
|
+
subject.events[:Bar].subscribe(:bar2, method(:subscriber_method))
|
148
|
+
subject.events[:Bar].subscribe(:bar3, @subscriber_proc)
|
149
|
+
|
150
|
+
subject.events[:Bar].unsubscribe(:bar1)
|
151
|
+
subject.events[:Bar].unsubscribe(:bar2)
|
152
|
+
subject.events[:Bar].unsubscribe(:bar3)
|
153
|
+
|
154
|
+
subscribers_to_be_called 0, subject.Bar
|
155
|
+
end
|
156
|
+
|
157
|
+
it "syntax-sugars object.Event#unsubscribe as object.unsubscribe(:Event)" do
|
158
|
+
subject.events[:Bar].subscribe(:bar1) { |*args| args.should == ["data"]; @counter += 1 }
|
159
|
+
subject.events[:Bar].subscribe(:bar2, method(:subscriber_method))
|
160
|
+
subject.events[:Bar].subscribe(:bar3, @subscriber_proc)
|
161
|
+
|
162
|
+
subject.unsubscribe(:Bar, :bar1)
|
163
|
+
subject.unsubscribe(:Bar, :bar2)
|
164
|
+
subject.remove(:Bar, :bar3)
|
165
|
+
|
166
|
+
subscribers_to_be_called 0, subject.Bar
|
167
|
+
end
|
168
|
+
|
169
|
+
it "raises error trying to unsubscribe undefined Event)" do
|
170
|
+
expect { subject.unsubscribe(:Gurgle, :bar) }.
|
171
|
+
to raise_error /Unable to unsubscribe, there is no event Gurgle/
|
172
|
+
|
173
|
+
subscribers_to_be_called 0, subject.Bar
|
174
|
+
end
|
175
|
+
|
176
|
+
it "raises error trying to unsubscribe unknown subscriber)" do
|
177
|
+
subject.events[:Bar].subscribe(:bar1) { |*args| args.should == ["data"]; @counter += 1 }
|
178
|
+
|
179
|
+
expect { subject.unsubscribe(:Bar, @subscriber_proc) }.
|
180
|
+
to raise_error /Unable to unsubscribe handler/
|
181
|
+
|
182
|
+
subscribers_to_be_called 1, subject.Bar
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end #unsubscribe
|
186
|
+
end
|
187
|
+
|
188
|
+
shared_examples_for('event') do
|
189
|
+
before do
|
190
|
+
define_subscribers
|
191
|
+
@subject = subject # subject += subscriber # Doesn't work, Ruby considers subject a local var here
|
192
|
+
end
|
193
|
+
|
194
|
+
specify { should respond_to :name }
|
195
|
+
specify { should respond_to :host } # Event should know what object it is attached to
|
196
|
+
its(:subscribers) { should be_a Hash }
|
197
|
+
|
198
|
+
context "#subscribe to Event" do
|
199
|
+
it 'allows anyone to add block subscribers/listeners (multiple syntax)' do
|
200
|
+
subject.subscribe(:bar1) { |*args| args.should == ["data"]; @counter += 1 }
|
201
|
+
subject.listen(:bar2) { |*args| args.should == ["data"]; @counter += 1 }
|
202
|
+
|
203
|
+
subscribers_to_be_called 2
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'allows anyone to add method subscribers/listeners (multiple syntax)' do
|
207
|
+
subject.subscribe(:bar1, method(:subscriber_method))
|
208
|
+
subject.listen(:bar2, method(:subscriber_method))
|
209
|
+
subject.listen(method(:subscriber_method))
|
210
|
+
@subject += method(:subscriber_method)
|
211
|
+
|
212
|
+
subscribers_to_be_called 4
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'allows anyone to add proc subscribers/listeners (multiple syntax)' do
|
216
|
+
subject.subscribe(:bar1, @subscriber_proc)
|
217
|
+
subject.subscribe(:bar2, &@subscriber_proc)
|
218
|
+
subject.subscribe @subscriber_proc
|
219
|
+
subject.subscribe &@subscriber_proc
|
220
|
+
subject.listen @subscriber_proc
|
221
|
+
@subject += @subscriber_proc
|
222
|
+
|
223
|
+
subscribers_to_be_called 6
|
224
|
+
end
|
225
|
+
|
226
|
+
it "allows you to mix subscriber types" do
|
227
|
+
subject.subscribe { |*args| args.should == ["data"]; @counter += 1 }
|
228
|
+
@subject += method :subscriber_method
|
229
|
+
@subject += @subscriber_proc
|
230
|
+
|
231
|
+
subscribers_to_be_called 3
|
232
|
+
end
|
233
|
+
|
234
|
+
it "raises exception if the given handler is not callable" do
|
235
|
+
[:subscriber_symbol, 1, [1, 2, 3], {me: 2}].each do |args|
|
236
|
+
expect { subject.subscribe(args) }.
|
237
|
+
to raise_error /Handler .* does not respond to #call/
|
238
|
+
expect { subject.subscribe(:good_name, args) }.
|
239
|
+
to raise_error /Handler .* does not respond to #call/
|
240
|
+
|
241
|
+
subscribers_to_be_called 0
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
it "raises exception when adding handler with duplicate name" do
|
246
|
+
subject.listen(:bar1) { |*args| args.should == ["data"]; @counter += 1 }
|
247
|
+
|
248
|
+
expect { subject.listen(:bar1) { |*args| args.should == ["data"]; @counter += 1 } }.
|
249
|
+
to raise_error /Handler name bar1 already in use/
|
250
|
+
expect { subject.listen(:bar1, @subscriber_proc) }.
|
251
|
+
to raise_error /Handler name bar1 already in use/
|
252
|
+
|
253
|
+
subscribers_to_be_called 1
|
254
|
+
end
|
255
|
+
end #subscribe
|
256
|
+
|
257
|
+
context "#unsubscribe from Event" do
|
258
|
+
|
259
|
+
it "allows you to unsubscribe from Events by name" do
|
260
|
+
subject.subscribe(:bar1) { |*args| args.should == ["data"]; @counter += 1 }
|
261
|
+
subject.subscribe(:bar2, method(:subscriber_method))
|
262
|
+
subject.subscribe(:bar3, @subscriber_proc)
|
263
|
+
|
264
|
+
subject.unsubscribe(:bar1)
|
265
|
+
subject.remove(:bar2)
|
266
|
+
@subject -= :bar3
|
267
|
+
|
268
|
+
subscribers_to_be_called 0
|
269
|
+
end
|
270
|
+
|
271
|
+
it "raises exception if the name is unknown or wrong" do
|
272
|
+
subject.subscribe(@subscriber_proc)
|
273
|
+
|
274
|
+
expect { subject.unsubscribe(@subscriber_proc) }.
|
275
|
+
to raise_error /Unable to unsubscribe handler/
|
276
|
+
expect { subject.unsubscribe('I-dont-know') }.
|
277
|
+
to raise_error /Unable to unsubscribe handler I-dont-know/
|
278
|
+
|
279
|
+
subscribers_to_be_called 1
|
280
|
+
end
|
281
|
+
end #unsubscribe
|
282
|
+
end
|