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.
@@ -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
+
@@ -1,6 +1,5 @@
1
- #require 'spec_helper'
2
- require_relative '../lib/amqp-events/events'
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