amqp-events 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|