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.
@@ -1,126 +1,234 @@
1
1
 
2
- require File.join(File.dirname(__FILE__), 'spec_helper')
2
+ require 'spec_helper'
3
3
 
4
- require 'ruote/participant'
5
4
 
5
+ describe Ruote::Amqp::Receiver do
6
6
 
7
- describe RuoteAMQP::Receiver do
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
+
18
+ @dashboard.register(
19
+ :toto,
20
+ Ruote::Amqp::Participant,
21
+ :exchange => [ 'direct', '' ],
22
+ :routing_key => 'alpha')
23
+
24
+ @queue = AMQP::Channel.new.queue('alpha')
25
+ end
8
26
 
9
27
  after(:each) do
10
- purge_engine
28
+
29
+ @dashboard.shutdown
30
+ @receiver.shutdown
31
+
32
+ #@queue.status { |mc, acc| p [ :messages, mc, :active_consumers, acc ] }
33
+ @queue.delete
11
34
  end
12
35
 
13
- it "handles replies" do
36
+ #
37
+ # A participant that spits back an error (over AMQP)
38
+ #
39
+ class ParticipantWithError
40
+ include Ruote::LocalParticipant
14
41
 
15
- pdef = Ruote.process_definition :name => 'test' do
16
- set :field => 'foo', :value => 'foo'
17
- sequence do
18
- echo '${f:foo}'
19
- amqp :queue => 'test3'
20
- echo '${f:foo}'
21
- end
42
+ def initialize(options)
43
+
44
+ @options = options
22
45
  end
23
46
 
24
- @engine.register_participant(:amqp, RuoteAMQP::ParticipantProxy)
47
+ def on_workitem
48
+
49
+ wi = workitem.h
50
+ wi['error'] = @options['error']
25
51
 
26
- RuoteAMQP::Receiver.new(@engine)
52
+ exchange = AMQP::Exchange.new(AMQP::Channel.new, :direct, '')
53
+ exchange.publish(Rufus::Json.encode(wi), :routing_key => 'alpha')
27
54
 
28
- wfid = @engine.launch(pdef)
55
+ # no replying to the engine
56
+ end
29
57
 
30
- workitem = nil
58
+ def on_cancel
31
59
 
32
- begin
33
- Timeout::timeout(5) do
60
+ # nothing
61
+ end
62
+ end
34
63
 
35
- MQ.queue('test3', :durable => true).subscribe { |msg|
36
- wi = Ruote::Workitem.new(Rufus::Json.decode(msg))
37
- workitem = wi if wi.wfid == wfid
38
- }
64
+ context 'plain receiver' do
39
65
 
40
- loop do
41
- break unless workitem.nil?
42
- sleep 0.1
66
+ before(:each) do
67
+
68
+ @receiver = Ruote::Amqp::Receiver.new(@dashboard, @queue)
69
+
70
+ sleep 0.100 # give some time for the receiver to settle in
71
+ end
72
+
73
+ context 'with workitems' do
74
+
75
+ it 'grabs workitems from a queue' do
76
+
77
+ pdef = Ruote.define do
78
+ toto
43
79
  end
80
+
81
+ wfid = @dashboard.launch(pdef)
82
+ r = @dashboard.wait_for(wfid)
83
+
84
+ r['action'].should == 'terminated'
85
+ end
86
+
87
+ it 'offers a hook for errors' do
88
+
89
+ $errs = []
90
+
91
+ def @receiver.handle_error(e)
92
+ $errs << e
93
+ end
94
+
95
+ exchange = AMQP::Exchange.new(AMQP::Channel.new, :direct, '')
96
+
97
+ exchange.publish('nada zero', :routing_key => 'alpha')
98
+ exchange.publish('nada one', :routing_key => 'alpha')
99
+
100
+ sleep 0.300
101
+
102
+ $errs.size.should == 2
44
103
  end
45
- rescue Timeout::Error
46
- violated "Timeout waiting for message"
47
104
  end
48
105
 
49
- workitem.fields['foo'] = "bar"
106
+ context 'with launchitems' do
50
107
 
51
- MQ.queue('ruote_workitems', :durable => true).publish(Rufus::Json.encode(workitem.to_h), :persistent => true)
108
+ it 'accepts launchitems' do
52
109
 
53
- @engine.wait_for(wfid)
110
+ @dashboard.register('noop', Ruote::NoOpParticipant)
54
111
 
55
- @engine.should_not have_errors
56
- @engine.should_not have_remaining_expressions
112
+ launchitem = {
113
+ 'definition' => Ruote.define { noop },
114
+ 'fields' => { 'kilroy' => 'was here' }
115
+ }
57
116
 
58
- @tracer.to_s.should == "foo\nbar"
59
- end
117
+ exchange = AMQP::Exchange.new(AMQP::Channel.new, :direct, '')
118
+ exchange.publish(Rufus::Json.encode(launchitem), :routing_key => 'alpha')
60
119
 
61
- it "launches processes" do
120
+ r = @dashboard.wait_for('terminated')
62
121
 
63
- json = {
64
- 'definition' => %{
65
- Ruote.process_definition :name => 'test' do
66
- sequence do
67
- echo '${f:foo}'
68
- end
69
- end
70
- },
71
- 'fields' => { 'foo' => 'bar' }
72
- }.to_json
122
+ r['workitem']['fields']['kilroy'].should == 'was here'
123
+ end
124
+ end
125
+
126
+ context 'with errors' do
127
+
128
+ it 'propagates errors passed back as strings' do
129
+
130
+ @dashboard.register_participant(
131
+ 'alf', ParticipantWithError, 'error' => 'something went wrong')
132
+
133
+ wfid = @dashboard.launch(Ruote.define { alf })
134
+ r = @dashboard.wait_for(wfid)
135
+
136
+ r['action'].should == 'error_intercepted'
137
+
138
+ r['error']['class'].should == 'Ruote::Amqp::RemoteError'
139
+ r['error']['message'].should == 'something went wrong'
140
+ end
141
+
142
+ it 'propagates errors passed back as hashes' do
143
+
144
+ @dashboard.register_participant(
145
+ 'alf',
146
+ ParticipantWithError,
147
+ 'error' => {
148
+ 'class' => 'ArgumentError', 'message' => 'something missing' })
149
+
150
+ wfid = @dashboard.launch(Ruote.define { alf })
151
+ r = @dashboard.wait_for(wfid)
152
+
153
+ r['action'].should == 'error_intercepted'
154
+
155
+ r['error']['class'].should == 'ArgumentError'
156
+ r['error']['message'].should == 'something missing'
157
+ end
73
158
 
74
- RuoteAMQP::Receiver.new(@engine, :launchitems => true, :unsubscribe => true)
159
+ it 'propagates errors passed back as whatever' do
75
160
 
76
- MQ.queue(
77
- 'ruote_workitems', :durable => true
78
- ).publish(
79
- json, :persistent => true
80
- )
161
+ @dashboard.register_participant(
162
+ 'alf', ParticipantWithError, 'error' => %w[ not good ])
81
163
 
82
- sleep 0.5
164
+ wfid = @dashboard.launch(Ruote.define { alf })
165
+ r = @dashboard.wait_for(wfid)
83
166
 
84
- @engine.should_not have_errors
85
- @engine.should_not have_remaining_expressions
167
+ r['action'].should == 'error_intercepted'
86
168
 
87
- @tracer.to_s.should == 'bar'
169
+ r['error']['class'].should == 'Ruote::Amqp::RemoteError'
170
+ r['error']['message'].should == %w[ not good ].inspect
171
+ end
172
+ end
88
173
  end
89
174
 
90
- it 'accepts a custom :queue' do
175
+ context 'launch_only receiver' do
91
176
 
92
- #@engine.noisy = true
177
+ before(:each) do
93
178
 
94
- RuoteAMQP::Receiver.new(
95
- @engine, :queue => 'mario', :launchitems => true, :unsubscribe => true)
179
+ @receiver = Ruote::Amqp::Receiver.new(
180
+ #@dashboard, @queue, 'launch_only' => true)
181
+ @dashboard, @queue, :launch_only => true)
182
+
183
+ sleep 0.100 # give some time for the receiver to settle in
184
+ end
96
185
 
97
- @engine.register_participant 'alpha', Ruote::StorageParticipant
186
+ # ...
98
187
 
99
- json = Rufus::Json.encode({
100
- 'definition' => "Ruote.define { alpha }"
101
- })
188
+ it 'accepts launchitems' do
102
189
 
103
- MQ.queue(
104
- 'ruote_workitems', :durable => true
105
- ).publish(
106
- json, :persistent => true
107
- )
190
+ @dashboard.register('noop', Ruote::NoOpParticipant)
108
191
 
109
- sleep 1
192
+ launchitem = {
193
+ 'definition' => Ruote.define { noop },
194
+ 'fields' => { 'launch' => 'yes' }
195
+ }
110
196
 
111
- @engine.processes.size.should == 0
112
- # nothing happened
197
+ exchange = AMQP::Exchange.new(AMQP::Channel.new, :direct, '')
198
+ exchange.publish(Rufus::Json.encode(launchitem), :routing_key => 'alpha')
113
199
 
114
- MQ.queue(
115
- 'mario', :durable => true
116
- ).publish(
117
- json, :persistent => true
118
- )
200
+ r = @dashboard.wait_for('terminated')
119
201
 
120
- sleep 1
202
+ r['workitem']['fields']['launch'].should == 'yes'
203
+ end
204
+
205
+ it 'discards workitems' do
206
+
207
+ pdef = Ruote.define do
208
+ toto
209
+ end
121
210
 
122
- @engine.processes.size.should == 1
123
- # launch happened
211
+ wfid = @dashboard.launch(pdef)
212
+ @dashboard.wait_for('dispatched')
213
+ sleep 0.500
214
+
215
+ @dashboard.ps(wfid).should_not == nil
216
+ end
217
+
218
+ it 'discards errors' do
219
+
220
+ @dashboard.register_participant(
221
+ 'alf', ParticipantWithError, 'error' => 'something went wrong')
222
+
223
+ wfid = @dashboard.launch(Ruote.define { alf })
224
+ @dashboard.wait_for('dispatched')
225
+ sleep 0.500
226
+
227
+ status = @dashboard.ps(wfid)
228
+
229
+ status.should_not == nil
230
+ status.errors.size.should == 0
231
+ end
124
232
  end
125
233
  end
126
234
 
data/spec/spec_helper.rb CHANGED
@@ -1,79 +1,21 @@
1
1
 
2
- require 'rubygems'
3
- require 'rspec'
2
+ require 'rufus-json/automatic'
3
+ require 'ruote/amqp'
4
4
 
5
- $:.unshift(File.join(File.dirname(__FILE__), '../lib'))
6
- $:.unshift(File.join(File.dirname(__FILE__), '../../ruote/lib'))
5
+ Dir[File.expand_path('../support/**/*.rb', __FILE__)].each { |f| require(f) }
7
6
 
8
- #gem 'amqp', '=0.6.7'
9
-
10
- require 'fileutils'
11
- require 'json'
12
- require 'timeout'
13
-
14
- require 'ruote/engine'
15
- require 'ruote/worker'
16
- require 'ruote/storage/hash_storage'
17
- require 'ruote/log/test_logger'
18
-
19
- require 'ruote-amqp'
20
-
21
- Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].each { |path|
22
- require(path)
23
- }
24
-
25
-
26
- # AMQP magic worked here
27
- AMQP.settings[:host] = 'localhost'
28
- AMQP.settings[:vhost] = 'ruote-test'
29
- AMQP.settings[:user] = 'ruote'
30
- AMQP.settings[:pass] = 'ruote'
31
-
32
- #AMQP.logging = true
33
7
 
34
8
  RSpec.configure do |config|
35
9
 
36
- config.include(RuoteSpecHelpers)
37
-
38
- config.before(:each) do
39
- @tracer = Tracer.new
40
-
41
- @engine = Ruote::Engine.new(
42
- Ruote::Worker.new(
43
- Ruote::HashStorage.new(
44
- 's_logger' => [ 'ruote/log/test_logger', 'Ruote::TestLogger' ])))
45
-
46
- @engine.add_service('tracer', @tracer)
47
- end
48
-
49
- config.after(:each) do
50
- @engine.shutdown
51
- @engine.context.storage.purge!
52
- end
53
-
54
- config.after(:all) do
55
- base = File.expand_path(File.dirname(__FILE__) + '/..')
56
- FileUtils.rm_rf(base + '/logs')
57
- FileUtils.rm_rf(base + '/work')
58
- end
59
- end
60
-
10
+ # == Mock Framework
11
+ #
12
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
13
+ #
14
+ # config.mock_with :mocha
15
+ # config.mock_with :flexmock
16
+ # config.mock_with :rr
17
+ config.mock_with :rspec
61
18
 
62
- class Tracer
63
- def initialize
64
- @trace = ''
65
- end
66
- def to_s
67
- @trace.to_s.strip
68
- end
69
- def << s
70
- @trace << s
71
- end
72
- def clear
73
- @trace = ''
74
- end
75
- def puts s
76
- @trace << "#{s}\n"
77
- end
19
+ config.include RuoteAmqpHelper
78
20
  end
79
21
 
@@ -0,0 +1,40 @@
1
+
2
+ module RuoteAmqpHelper
3
+
4
+ def ensure_em_is_running
5
+
6
+ unless @em
7
+ @em = Thread.new { EM.run {} }
8
+ sleep 0.5
9
+ end
10
+ end
11
+
12
+ def count_amqp_objects
13
+
14
+ objects = {}
15
+
16
+ ObjectSpace.each_object do |o|
17
+
18
+ next unless [
19
+ AMQP::Queue, AMQP::Channel, AMQP::Exchange
20
+ ].include?(o.class)
21
+
22
+ k = o.class.to_s
23
+
24
+ objects[k] ||= 0
25
+ objects[k] = objects[k] + 1
26
+ end
27
+
28
+ objects
29
+ end
30
+
31
+ def display_amqp_object_count
32
+
33
+ objects = count_amqp_objects
34
+
35
+ objects.keys.sort.each do |k|
36
+ puts "#{k}: #{objects[k]}"
37
+ end
38
+ end
39
+ end
40
+
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruote-amqp
3
3
  version: !ruby/object:Gem::Version
4
- hash: 7
4
+ hash: 3
5
5
  prerelease:
6
6
  segments:
7
7
  - 2
8
- - 2
8
+ - 3
9
9
  - 0
10
- version: 2.2.0
10
+ version: 2.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Kenneth Kalmer
@@ -16,8 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2011-03-01 00:00:00 +09:00
20
- default_executable:
19
+ date: 2012-09-02 00:00:00 Z
21
20
  dependencies:
22
21
  - !ruby/object:Gem::Dependency
23
22
  name: amqp
@@ -25,14 +24,13 @@ dependencies:
25
24
  requirement: &id001 !ruby/object:Gem::Requirement
26
25
  none: false
27
26
  requirements:
28
- - - "="
27
+ - - ~>
29
28
  - !ruby/object:Gem::Version
30
- hash: 3
29
+ hash: 25
31
30
  segments:
32
31
  - 0
33
- - 7
34
- - 0
35
- version: 0.7.0
32
+ - 9
33
+ version: "0.9"
36
34
  type: :runtime
37
35
  version_requirements: *id001
38
36
  - !ruby/object:Gem::Dependency
@@ -43,12 +41,12 @@ dependencies:
43
41
  requirements:
44
42
  - - ">="
45
43
  - !ruby/object:Gem::Version
46
- hash: 7
44
+ hash: 3
47
45
  segments:
48
46
  - 2
49
- - 2
47
+ - 3
50
48
  - 0
51
- version: 2.2.0
49
+ version: 2.3.0
52
50
  type: :runtime
53
51
  version_requirements: *id002
54
52
  - !ruby/object:Gem::Dependency
@@ -71,18 +69,17 @@ dependencies:
71
69
  requirement: &id004 !ruby/object:Gem::Requirement
72
70
  none: false
73
71
  requirements:
74
- - - ">="
72
+ - - ~>
75
73
  - !ruby/object:Gem::Version
76
- hash: 5
74
+ hash: 19
77
75
  segments:
78
76
  - 2
79
- - 2
80
- - 1
81
- version: 2.2.1
77
+ - 8
78
+ version: "2.8"
82
79
  type: :development
83
80
  version_requirements: *id004
84
81
  description: "\n\
85
- AMQP participant/listener pair for ruote 2.1\n "
82
+ AMQP participant/listener pair for ruote\n "
86
83
  email:
87
84
  - kenneth.kalmer@gmail.com
88
85
  - jmettraux@gmail.com
@@ -94,27 +91,24 @@ extra_rdoc_files: []
94
91
 
95
92
  files:
96
93
  - Rakefile
97
- - lib/ruote-amqp/launchitem_listener.rb
98
- - lib/ruote-amqp/participant.rb
99
- - lib/ruote-amqp/receiver.rb
100
- - lib/ruote-amqp/version.rb
101
- - lib/ruote-amqp/workitem_listener.rb
94
+ - lib/ruote/amqp/alert_participant.rb
95
+ - lib/ruote/amqp/participant.rb
96
+ - lib/ruote/amqp/receiver.rb
97
+ - lib/ruote/amqp/version.rb
98
+ - lib/ruote/amqp.rb
102
99
  - lib/ruote-amqp.rb
103
- - spec/launchitem_listener_spec.rb
100
+ - spec/alert_participant_spec.rb
104
101
  - spec/participant_spec.rb
102
+ - spec/participant_subclass_spec.rb
105
103
  - spec/receiver_spec.rb
106
- - spec/ruote_amqp_spec.rb
107
104
  - spec/spec_helper.rb
108
- - spec/support/ruote_helpers.rb
109
- - spec/support/ruote_matchers.rb
110
- - spec/workitem_listener_spec.rb
105
+ - spec/support/ruote_amqp_helper.rb
111
106
  - ruote-amqp.gemspec
112
107
  - CHANGELOG.txt
113
108
  - CREDITS.txt
114
- - PostInstall.txt
109
+ - LICENSE.txt
115
110
  - TODO.txt
116
- - README.rdoc
117
- has_rdoc: true
111
+ - README.md
118
112
  homepage: http://ruote.rubyforge.org
119
113
  licenses: []
120
114
 
@@ -144,9 +138,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
144
138
  requirements: []
145
139
 
146
140
  rubyforge_project: ruote
147
- rubygems_version: 1.5.0
141
+ rubygems_version: 1.8.15
148
142
  signing_key:
149
143
  specification_version: 3
150
- summary: AMQP participant/listener pair for ruote 2.1
144
+ summary: AMQP participant/listener pair for ruote
151
145
  test_files: []
152
146
 
data/PostInstall.txt DELETED
@@ -1,12 +0,0 @@
1
-
2
- For more information on ruote-amqp, see http://github.com/kennethkalmer/ruote-amqp
3
-
4
- Please note that this gem requires access to an AMQP broker and a good
5
- understanding of AMQP and remote participants in general.
6
-
7
- Join us in #ruote on Freenode or on the openwfe-users Google Group to discuss
8
- it's uses.
9
-
10
- You might also want to look at daemon-kit for help with writing external
11
- participants that communicate with ruote via AMQP.
12
-
data/README.rdoc DELETED
@@ -1,91 +0,0 @@
1
-
2
- = ruote-amqp
3
-
4
- * http://github.com/kennethkalmer/ruote-amqp
5
- * http://rdoc.info/projects/kennethkalmer/ruote-amqp
6
- * http://ruote.rubyforge.org
7
-
8
- == DESCRIPTION:
9
-
10
- ruote-amqp provides an AMQP participant/listener pair that allows you to
11
- distribute workitems out to AMQP consumers for processing, as well as launching
12
- processes over AMQP.
13
-
14
- To learn more about remote participants in ruote please see
15
- http://ruote.rubyforge.org/part_implementations.html
16
-
17
- == FEATURES/PROBLEMS:
18
-
19
- * Flexible participant for sending workitems
20
- * Flexible receiver for receiving replies
21
- * Flexible launch item listener for launching processes over AMQP
22
- * Fully evented (thanks to the amqp gem)
23
-
24
- == SYNOPSIS:
25
-
26
- Please review the rdoc in RuoteAMQP::Participant and Ruote::AMQP::Listener
27
-
28
- == REQUIREMENTS:
29
-
30
- * ruote[http://ruote.rubyforge.org] 2.2.0 or later
31
- * amqp[http://github.com/tmm1/amqp] 0.6.7 or later
32
- * rabbitmq[http://www.rabbitmq.com/] 1.6.0 or later
33
-
34
- == INSTALL:
35
-
36
- Please be sure to have read the requirements section above
37
-
38
- * sudo gem install ruote-amqp
39
-
40
- == TESTS:
41
-
42
- To run the tests you need the following requirements met, or the testing environment
43
- will fail horribly (or simply get stuck without output).
44
-
45
- === RabbitMQ vhost
46
-
47
- The tests use dedicated vhost on a running AMQP broker. To configure RabbitMQ
48
- you can run the following commands:
49
-
50
- # rabbitmqctl add_vhost ruote-test
51
- # rabbitmqctl add_user ruote ruote
52
- # rabbitmqctl set_permissions -p ruote-test ruote '.*' '.*' '.*'
53
-
54
- If you need to change the AMQP configuration used by the tests, edit the
55
- +spec/spec_helper.rb+ file.
56
-
57
- == DAEMON-KIT:
58
-
59
- Although the RuoteAMQP gem will work perfectly well with any AMQP consumer,
60
- it is recommended that you use daemon-kit[http://github.com/kennethkalmer/daemon-kit]
61
- to write your remote participants.
62
-
63
- daemon-kit offers plenty of convenience for remote participants and includes
64
- a code generator for ruote remote participants.
65
-
66
- DaemonKit doesn't currently support ruote 2.1, support is forthcoming.
67
-
68
- == LICENSE:
69
-
70
- (The MIT License)
71
-
72
- Copyright (c) 2010-2011 Kenneth Kalmer
73
-
74
- Permission is hereby granted, free of charge, to any person obtaining
75
- a copy of this software and associated documentation files (the
76
- 'Software'), to deal in the Software without restriction, including
77
- without limitation the rights to use, copy, modify, merge, publish,
78
- distribute, sublicense, and/or sell copies of the Software, and to
79
- permit persons to whom the Software is furnished to do so, subject to
80
- the following conditions:
81
-
82
- The above copyright notice and this permission notice shall be
83
- included in all copies or substantial portions of the Software.
84
-
85
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
86
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
87
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
88
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
89
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
90
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
91
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.