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.
@@ -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