ruote-amqp 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/ruote-amqp.gemspec CHANGED
@@ -1,17 +1,20 @@
1
- # encoding: utf-8
2
1
 
3
2
  Gem::Specification.new do |s|
4
3
 
5
4
  s.name = 'ruote-amqp'
6
- s.version = File.read('lib/ruote-amqp/version.rb').match(/VERSION = '([^']+)'/)[1]
5
+
6
+ s.version = File.read(
7
+ File.expand_path('../lib/ruote/amqp/version.rb', __FILE__)
8
+ ).match(/ VERSION *= *['"]([^'"]+)/)[1]
9
+
7
10
  s.platform = Gem::Platform::RUBY
8
11
  s.authors = [ 'Kenneth Kalmer', 'John Mettraux' ]
9
12
  s.email = [ 'kenneth.kalmer@gmail.com', 'jmettraux@gmail.com' ]
10
13
  s.homepage = 'http://ruote.rubyforge.org'
11
14
  s.rubyforge_project = 'ruote'
12
- s.summary = 'AMQP participant/listener pair for ruote 2.1'
15
+ s.summary = 'AMQP participant/listener pair for ruote'
13
16
  s.description = %{
14
- AMQP participant/listener pair for ruote 2.1
17
+ AMQP participant/listener pair for ruote
15
18
  }
16
19
 
17
20
  #s.files = `git ls-files`.split("\n")
@@ -21,11 +24,11 @@ AMQP participant/listener pair for ruote 2.1
21
24
  '*.gemspec', '*.txt', '*.rdoc', '*.md'
22
25
  ]
23
26
 
24
- s.add_runtime_dependency 'amqp', '0.7.0'
27
+ s.add_runtime_dependency 'amqp', '~> 0.9'
25
28
  s.add_runtime_dependency 'ruote', ">= #{s.version}"
26
29
 
27
30
  s.add_development_dependency 'rake'
28
- s.add_development_dependency 'rspec', ">= 2.2.1"
31
+ s.add_development_dependency 'rspec', '~> 2.8'
29
32
 
30
33
  s.require_path = 'lib'
31
34
  end
@@ -0,0 +1,58 @@
1
+
2
+ require 'spec_helper'
3
+
4
+
5
+ describe Ruote::Amqp::AlertParticipant do
6
+
7
+ before(:all) do
8
+
9
+ ensure_em_is_running
10
+ end
11
+
12
+ before(:each) do
13
+
14
+ @dashboard = Ruote::Dashboard.new(Ruote::Worker.new(Ruote::HashStorage.new))
15
+
16
+ @dashboard.noisy = ENV['NOISY'].to_s == 'true'
17
+ end
18
+
19
+ after(:each) do
20
+
21
+ @dashboard.shutdown
22
+ end
23
+
24
+ it 'lets the flow resume upon receiving a message' do
25
+
26
+ @dashboard.register(
27
+ :snare,
28
+ Ruote::Amqp::AlertParticipant,
29
+ :queue => 'x')
30
+
31
+ pdef = Ruote.define do
32
+ snare
33
+ end
34
+
35
+ wfid = @dashboard.launch(pdef)
36
+
37
+ sleep 0.100 # let some time for the alert participant to settle in
38
+
39
+ exchange = AMQP::Exchange.new(AMQP::Channel.new, :direct, '')
40
+ exchange.publish('nada', :routing_key => 'x')
41
+
42
+ r = @dashboard.wait_for(wfid)
43
+
44
+ r['action'].should == 'terminated'
45
+
46
+ r['workitem']['fields'].should == {
47
+ 'amqp_message' => [
48
+ {
49
+ 'content_type' => 'application/octet-stream',
50
+ 'priority' => 0,
51
+ 'delivery_mode' => 1
52
+ },
53
+ 'nada'
54
+ ]
55
+ }
56
+ end
57
+ end
58
+
@@ -1,192 +1,338 @@
1
1
 
2
- require File.join(File.dirname(__FILE__), 'spec_helper')
2
+ require 'spec_helper'
3
3
 
4
4
 
5
- describe RuoteAMQP::ParticipantProxy, :type => :ruote do
5
+ describe Ruote::Amqp::Participant do
6
6
 
7
- it "supports 'forget' as participant attribute" do
7
+ before(:all) do
8
8
 
9
- pdef = ::Ruote.process_definition :name => 'test' do
10
- sequence do
11
- amqp :queue => 'test1', :forget => true
12
- echo 'done.'
13
- end
9
+ ensure_em_is_running
10
+ end
11
+
12
+ before(:each) do
13
+
14
+ @dashboard = Ruote::Dashboard.new(Ruote::Worker.new(Ruote::HashStorage.new))
15
+
16
+ @dashboard.noisy = ENV['NOISY'].to_s == 'true'
17
+ end
18
+
19
+ after(:each) do
20
+
21
+ @dashboard.shutdown
22
+ @queue.delete rescue nil
23
+ end
24
+
25
+ it 'publishes messages on the given exchange' do
26
+
27
+ @dashboard.register(
28
+ :toto,
29
+ Ruote::Amqp::Participant,
30
+ :exchange => [ 'direct', '' ],
31
+ :routing_key => 'alpha',
32
+ :forget => true)
33
+
34
+ wi = nil
35
+
36
+ @queue = AMQP::Channel.new.queue('alpha')
37
+ @queue.subscribe { |headers, payload| wi = Rufus::Json.decode(payload) }
38
+
39
+ sleep 0.1
40
+
41
+ pdef = Ruote.define do
42
+ toto
14
43
  end
15
44
 
16
- @engine.register_participant(:amqp, RuoteAMQP::ParticipantProxy)
45
+ wfid = @dashboard.launch(pdef)
46
+ r = @dashboard.wait_for(wfid)
17
47
 
18
- run_definition(pdef)
48
+ r['action'].should == 'terminated'
19
49
 
20
- @tracer.to_s.should == 'done.'
50
+ sleep 0.1
21
51
 
22
- begin
23
- Timeout::timeout(10) do
24
- @msg = nil
25
- MQ.queue('test1', :durable => true).subscribe { |msg| @msg = msg }
52
+ wi['participant_name'].should == 'toto'
53
+ end
26
54
 
27
- loop do
28
- break unless @msg.nil?
29
- sleep 0.1
30
- end
31
- end
32
- rescue Timeout::Error
33
- violated "Timeout waiting for message"
55
+ it 'blocks if :forget is not set to true' do
56
+
57
+ @dashboard.register(
58
+ :toto,
59
+ Ruote::Amqp::Participant,
60
+ :exchange => [ 'direct', '' ],
61
+ :routing_key => 'alpha')
62
+
63
+ wi = nil
64
+
65
+ @queue = AMQP::Channel.new.queue('alpha')
66
+ @queue.subscribe { |headers, payload| wi = Rufus::Json.decode(payload) }
67
+
68
+ sleep 0.1
69
+
70
+ pdef = Ruote.define do
71
+ toto
34
72
  end
35
73
 
36
- @msg.should match(/^\{.*\}$/) # JSON message by default
74
+ wfid = @dashboard.launch(pdef, 'customer' => 'taira no kiyomori')
75
+ @dashboard.wait_for('dispatched')
76
+
77
+ sleep 0.1
78
+
79
+ wi['fields']['customer'].should == 'taira no kiyomori'
80
+
81
+ @dashboard.ps(wfid).expressions.size.should == 2
37
82
  end
38
83
 
39
- it "supports 'forget' as participant option" do
84
+ it 'supports :forget as a participant attribute' do
40
85
 
41
- pdef = ::Ruote.process_definition :name => 'test' do
42
- sequence do
43
- amqp :queue => 'test4'
44
- echo 'done.'
45
- end
86
+ @dashboard.register(
87
+ :toto,
88
+ Ruote::Amqp::Participant,
89
+ :exchange => [ 'direct', '' ],
90
+ :routing_key => 'alpha')
91
+
92
+ wi = nil
93
+
94
+ @queue = AMQP::Channel.new.queue('alpha')
95
+ @queue.subscribe { |headers, payload| wi = Rufus::Json.decode(payload) }
96
+
97
+ pdef = Ruote.define do
98
+ toto :forget => true
46
99
  end
47
100
 
48
- @engine.register_participant(
49
- :amqp, RuoteAMQP::ParticipantProxy, 'forget' => true)
101
+ wfid = @dashboard.launch(pdef, 'customer' => 'minamoto no yoshitomo')
102
+ r = @dashboard.wait_for(wfid)
103
+
104
+ r['action'].should == 'terminated'
50
105
 
51
- run_definition(pdef)
106
+ sleep 0.1
52
107
 
53
- @tracer.to_s.should == "done."
108
+ wi['fields']['customer'].should == 'minamoto no yoshitomo'
109
+ end
54
110
 
55
- begin
56
- Timeout::timeout(5) do
57
- @msg = nil
58
- MQ.queue('test4', :durable => true).subscribe { |msg| @msg = msg }
111
+ it 'supports the :message option' do
59
112
 
60
- loop do
61
- break unless @msg.nil?
62
- sleep 0.1
63
- end
64
- end
65
- rescue Timeout::Error
66
- violated "Timeout waiting for message"
113
+ @dashboard.register(
114
+ :alpha,
115
+ Ruote::Amqp::Participant,
116
+ :exchange => [ 'direct', '' ],
117
+ :routing_key => 'alpha',
118
+ :message => 'hello world!',
119
+ :forget => true)
120
+
121
+ msg = nil
122
+
123
+ @queue = AMQP::Channel.new.queue('alpha')
124
+ @queue.subscribe { |headers, payload| msg = payload }
125
+
126
+ sleep 0.1
127
+
128
+ pdef = Ruote.define do
129
+ alpha
67
130
  end
68
131
 
69
- @msg.should match(/^\{.*\}$/) # JSON message by default
132
+ wfid = @dashboard.launch(pdef)
133
+ @dashboard.wait_for(wfid)
134
+
135
+ sleep 0.1
136
+
137
+ msg.should == 'hello world!'
70
138
  end
71
139
 
72
- it "supports custom messages instead of workitems" do
140
+ it 'publishes messages with the given correlation-id' do
73
141
 
74
- pdef = ::Ruote.process_definition :name => 'test' do
75
- sequence do
76
- amqp :queue => 'test2', :message => 'foo', :forget => true
77
- echo 'done.'
78
- end
142
+ @dashboard.register(
143
+ :toto,
144
+ Ruote::Amqp::Participant,
145
+ :exchange => [ 'direct', '' ],
146
+ :routing_key => 'alpha',
147
+ :correlation_id => 'beta',
148
+ :forget => true)
149
+
150
+ correlation_id = nil
151
+
152
+ @queue = AMQP::Channel.new.queue('alpha')
153
+ @queue.subscribe { |headers, payload| correlation_id = headers.correlation_id }
154
+
155
+ sleep 0.1
156
+
157
+ pdef = Ruote.define do
158
+ toto
79
159
  end
80
160
 
81
- @engine.register_participant(:amqp, RuoteAMQP::ParticipantProxy)
161
+ wfid = @dashboard.launch(pdef)
162
+ @dashboard.wait_for(wfid)
163
+
164
+ sleep 0.1
82
165
 
83
- run_definition(pdef)
166
+ correlation_id.should == 'beta'
167
+ end
168
+
169
+ it 'reuses channels and exchanges within a thread' do
84
170
 
85
- @tracer.to_s.should == "done."
171
+ @dashboard.register(
172
+ :alpha,
173
+ Ruote::Amqp::Participant,
174
+ :exchange => [ 'direct', '' ],
175
+ :routing_key => 'alpha',
176
+ :message => 'hello world!',
177
+ :forget => true)
86
178
 
87
- begin
88
- Timeout::timeout(5) do
89
- @msg = nil
90
- MQ.queue('test2', :durable => true).subscribe { |msg| @msg = msg }
179
+ c0 = count_amqp_objects
91
180
 
92
- loop do
93
- break unless @msg.nil?
94
- sleep 0.1
95
- end
181
+ wfid = @dashboard.launch(Ruote.define do
182
+ concurrence do
183
+ 10.times { alpha }
96
184
  end
97
- rescue Timeout::Error
98
- violated "Timeout waiting for message"
99
- end
185
+ end)
186
+
187
+ @dashboard.wait_for(2 + 3 * 10)
188
+
189
+ c1 = count_amqp_objects
100
190
 
101
- @msg.should == 'foo'
191
+ 3.times { GC.start }
192
+ sleep 2
193
+ # doesn't change much...
194
+
195
+ c2 = count_amqp_objects
196
+
197
+ c2.should == c1
198
+ c1['AMQP::Channel'].should == (c0['AMQP::Channel'] || 0) + 1
199
+ c1['AMQP::Exchange'].should == (c0['AMQP::Exchange'] || 0) + 1
102
200
  end
103
201
 
104
- it "supports 'queue' as a participant option" do
202
+ context 'cancelitems' do
203
+
204
+ it 'publishes "cancelitems"' do
205
+
206
+ @dashboard.register(
207
+ :toto,
208
+ Ruote::Amqp::Participant,
209
+ :exchange => [ 'direct', '' ],
210
+ :routing_key => 'alpha')
211
+
212
+ msgs = []
105
213
 
106
- pdef = ::Ruote.process_definition :name => 'test' do
107
- sequence do
108
- amqp :forget => true
109
- echo 'done.'
214
+ @queue = AMQP::Channel.new.queue('alpha')
215
+ @queue.subscribe { |headers, payload| msgs << [ headers, payload ] }
216
+
217
+ pdef = Ruote.define do
218
+ toto
110
219
  end
220
+
221
+ wfid = @dashboard.launch(pdef)
222
+ @dashboard.wait_for('dispatched')
223
+
224
+ sleep 0.1
225
+
226
+ @dashboard.cancel(wfid)
227
+
228
+ sleep 0.1
229
+
230
+ msgs.size.should == 2
231
+
232
+ items = msgs.collect { |msg| Rufus::Json.decode(msg.last) }
233
+
234
+ items.first['participant_name'].should == 'toto'
235
+
236
+ items.last.keys.sort.should == %w[ cancel fei flavour ]
237
+ items.last['cancel'].should == true
238
+ items.last['fei']['wfid'].should == wfid
239
+ items.last['flavour'].should == nil
111
240
  end
112
241
 
113
- @engine.register_participant(
114
- :amqp, RuoteAMQP::ParticipantProxy, 'queue' => 'test5')
242
+ it "doesn't publish \"cancelitems\" if 'discard_cancel' => true" do
115
243
 
116
- run_definition(pdef)
244
+ @dashboard.register(
245
+ :toto,
246
+ Ruote::Amqp::Participant,
247
+ :exchange => [ 'direct', '' ],
248
+ :routing_key => 'alpha',
249
+ :discard_cancel => true)
117
250
 
118
- @tracer.to_s.should == 'done.'
251
+ msgs = []
119
252
 
120
- begin
121
- Timeout::timeout(5) do
122
- @msg = nil
123
- MQ.queue('test5', :durable => true).subscribe { |msg| @msg = msg }
253
+ @queue = AMQP::Channel.new.queue('alpha')
254
+ @queue.subscribe { |headers, payload| msgs << [ headers, payload ] }
124
255
 
125
- loop do
126
- break unless @msg.nil?
127
- sleep 0.1
128
- end
256
+ pdef = Ruote.define do
257
+ toto
129
258
  end
130
- rescue Timeout::Error
131
- violated "Timeout waiting for message"
259
+
260
+ wfid = @dashboard.launch(pdef)
261
+ @dashboard.wait_for('dispatched')
262
+
263
+ sleep 0.1
264
+
265
+ @dashboard.cancel(wfid)
266
+
267
+ sleep 0.1
268
+
269
+ msgs.size.should == 1
132
270
  end
133
271
  end
134
272
 
135
- it "passes 'participant_options' over amqp" do
273
+ context 'Ruote::Amqp.session' do
274
+
275
+ after(:each) do
136
276
 
137
- pdef = ::Ruote.process_definition :name => 'test' do
138
- amqp :queue => 'test6', :forget => true
277
+ Ruote::Amqp.session = nil
139
278
  end
140
279
 
141
- @engine.register_participant(:amqp, RuoteAMQP::ParticipantProxy)
280
+ it 'uses Ruote::Amqp.session if set for connecting to AMQP' do
142
281
 
143
- run_definition(pdef)
282
+ Ruote::Amqp.session = 'fail!'
144
283
 
145
- msg = nil
284
+ @dashboard.register(
285
+ :toto,
286
+ Ruote::Amqp::Participant,
287
+ :exchange => [ 'direct', '' ],
288
+ :routing_key => 'alpha',
289
+ :forget => true)
146
290
 
147
- begin
148
- Timeout::timeout(10) do
291
+ pdef = Ruote.define do
292
+ toto
293
+ end
149
294
 
150
- MQ.queue('test6', :durable => true).subscribe { |m| msg = m }
295
+ wfid = @dashboard.launch(pdef)
296
+ r = @dashboard.wait_for(wfid)
151
297
 
152
- loop do
153
- break unless msg.nil?
154
- sleep 0.1
155
- end
156
- end
157
- rescue Timeout::Error
158
- violated "Timeout waiting for message"
159
- end
298
+ r['action'].should == 'error_intercepted'
160
299
 
161
- wi = Rufus::Json.decode(msg)
162
- params = wi['fields']['params']
300
+ r['error']['class'].should ==
301
+ 'NoMethodError'
302
+ r['error']['message'].should ==
303
+ "undefined method `auto_recovering?' for \"fail!\":String"
304
+ end
163
305
 
164
- params['queue'].should == 'test6'
165
- params['forget'].should == true
166
- params['participant_options'].should == { 'forget' => false, 'queue' => nil }
167
- end
306
+ it "doesn't use Ruote::Amqp.session if a 'connection' option is given" do
168
307
 
169
- it "doesn't create 1 queue instance per delivery" do
308
+ Ruote::Amqp.session = 'do fail now!'
170
309
 
171
- pdef = ::Ruote.process_definition do
172
- amqp :queue => 'test7', :forget => true
173
- end
310
+ @dashboard.register(
311
+ :toto,
312
+ Ruote::Amqp::Participant,
313
+ :exchange => [ 'direct', '' ],
314
+ :routing_key => 'alpha',
315
+ :forget => true,
316
+ :connection => {})
174
317
 
175
- mq_count = 0
176
- ObjectSpace.each_object(MQ) { |o| mq_count += 1 }
318
+ wi = nil
177
319
 
178
- @engine.register_participant(:amqp, RuoteAMQP::ParticipantProxy)
320
+ @queue = AMQP::Channel.new.queue('alpha')
321
+ @queue.subscribe { |headers, payload| wi = Rufus::Json.decode(payload) }
179
322
 
180
- 10.times do
181
- run_definition(pdef)
182
- end
323
+ sleep 0.1
183
324
 
184
- sleep 1
325
+ pdef = Ruote.define do
326
+ toto
327
+ end
185
328
 
186
- count = 0
187
- ObjectSpace.each_object(MQ) { |o| count += 1 }
329
+ wfid = @dashboard.launch(pdef)
330
+ r = @dashboard.wait_for(wfid)
331
+ sleep 0.1
188
332
 
189
- count.should == mq_count + 1
333
+ r['action'].should == 'terminated'
334
+ wi['participant_name'].should == 'toto'
335
+ end
190
336
  end
191
337
  end
192
338
 
@@ -0,0 +1,66 @@
1
+
2
+ require 'spec_helper'
3
+
4
+
5
+ describe 'X < Ruote::Amqp::Participant' do
6
+
7
+ before(:all) do
8
+
9
+ ensure_em_is_running
10
+ end
11
+
12
+ before(:each) do
13
+
14
+ @dashboard = Ruote::Dashboard.new(Ruote::Worker.new(Ruote::HashStorage.new))
15
+
16
+ @dashboard.noisy = ENV['NOISY'].to_s == 'true'
17
+ end
18
+
19
+ after(:each) do
20
+
21
+ @dashboard.shutdown
22
+ @queue.delete rescue nil
23
+ end
24
+
25
+ context 'RoutingKeyParticipant' do
26
+
27
+ #
28
+ # Sample subclass, simply uses the participant name as the routing_key.
29
+ #
30
+ class RoutingKeyParticipant < Ruote::Amqp::Participant
31
+
32
+ def routing_key
33
+
34
+ @workitem.participant_name
35
+ end
36
+ end
37
+
38
+ it 'sets the routing key to the participant name' do
39
+
40
+ @dashboard.register(
41
+ /.+/, RoutingKeyParticipant, :forget => true)
42
+
43
+ wi = nil
44
+
45
+ @queue = AMQP::Channel.new.queue('alpha')
46
+ @queue.subscribe { |headers, payload| wi = Rufus::Json.decode(payload) }
47
+
48
+ sleep 0.1
49
+
50
+ pdef = Ruote.define do
51
+ alpha
52
+ end
53
+
54
+ wfid = @dashboard.launch(pdef, 'customer' => 'goshirakawa')
55
+ r = @dashboard.wait_for(wfid)
56
+
57
+ r['action'].should == 'terminated'
58
+
59
+ sleep 0.1
60
+
61
+ wi['participant_name'].should == 'alpha'
62
+ wi['fields']['customer'].should == 'goshirakawa'
63
+ end
64
+ end
65
+ end
66
+