ruote-amqp 2.2.0 → 2.3.0

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/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
+