ruote-amqp 0.9.20
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/Manifest.txt +20 -0
- data/PostInstall.txt +12 -0
- data/README.rdoc +67 -0
- data/Rakefile +26 -0
- data/lib/ruote-amqp.rb +26 -0
- data/lib/ruote-amqp/listener.rb +92 -0
- data/lib/ruote-amqp/participant.rb +153 -0
- data/lib/spec/ruote.rb +5 -0
- data/lib/spec/ruote_example_group.rb +8 -0
- data/lib/spec/ruote_helpers.rb +24 -0
- data/lib/spec/ruote_matchers.rb +56 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/listener_spec.rb +53 -0
- data/spec/participant_spec.rb +108 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +87 -0
- data/tasks/rspec.rake +21 -0
- metadata +105 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
PostInstall.txt
|
4
|
+
README.rdoc
|
5
|
+
Rakefile
|
6
|
+
lib/ruote-amqp.rb
|
7
|
+
lib/ruote-amqp/listener.rb
|
8
|
+
lib/ruote-amqp/participant.rb
|
9
|
+
lib/spec/ruote.rb
|
10
|
+
lib/spec/ruote_example_group.rb
|
11
|
+
lib/spec/ruote_helpers.rb
|
12
|
+
lib/spec/ruote_matchers.rb
|
13
|
+
script/console
|
14
|
+
script/destroy
|
15
|
+
script/generate
|
16
|
+
spec/listener_spec.rb
|
17
|
+
spec/participant_spec.rb
|
18
|
+
spec/spec.opts
|
19
|
+
spec/spec_helper.rb
|
20
|
+
tasks/rspec.rake
|
data/PostInstall.txt
ADDED
@@ -0,0 +1,12 @@
|
|
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
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
= ruote-amqp
|
2
|
+
|
3
|
+
* http://github.com/kennethkalmer/ruote-amqp
|
4
|
+
* http://openwfe.rubyforge.org
|
5
|
+
|
6
|
+
== DESCRIPTION:
|
7
|
+
|
8
|
+
ruote-amqp provides an AMQP participant/listener pair that allows you to
|
9
|
+
distribute workitems out to AMQP consumers for processing.
|
10
|
+
|
11
|
+
To learn more about remote participants in ruote please see
|
12
|
+
http://openwfe.rubyforge.org/part.html
|
13
|
+
|
14
|
+
== FEATURES/PROBLEMS:
|
15
|
+
|
16
|
+
* Flexible participant for sending workitems
|
17
|
+
* Flexible listener for receiving replies
|
18
|
+
* Fully evented (thanks to the amqp gem)
|
19
|
+
|
20
|
+
== SYNOPSIS:
|
21
|
+
|
22
|
+
Please review the detailed RDOC in RuoteAMQP::Participant and Ruote::AMQP::Listener
|
23
|
+
|
24
|
+
== REQUIREMENTS:
|
25
|
+
|
26
|
+
* ruote[http://openwfe.rubyforge.org] 0.9.20 or later
|
27
|
+
* amqp[http://github.com/tmm1/amqp] 0.6.1 or later
|
28
|
+
|
29
|
+
NOTE: It might be required that you build the amqp gem yourself
|
30
|
+
|
31
|
+
== INSTALL:
|
32
|
+
|
33
|
+
* sudo gem install ruote-amqp
|
34
|
+
|
35
|
+
== DAEMON-KIT:
|
36
|
+
|
37
|
+
Although the RuoteAMQP gem will work perfectly well with any AMQP consumer,
|
38
|
+
it is recommended that you use daemon-kit[http://github.com/kennethkalmer/daemon-kit]
|
39
|
+
to write your remote participants.
|
40
|
+
|
41
|
+
daemon-kit offers plenty of convenience for remote participants and includes
|
42
|
+
a code generator for ruote remote participants.
|
43
|
+
|
44
|
+
== LICENSE:
|
45
|
+
|
46
|
+
(The MIT License)
|
47
|
+
|
48
|
+
Copyright (c) 2009 Kenneth Kalmer
|
49
|
+
|
50
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
51
|
+
a copy of this software and associated documentation files (the
|
52
|
+
'Software'), to deal in the Software without restriction, including
|
53
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
54
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
55
|
+
permit persons to whom the Software is furnished to do so, subject to
|
56
|
+
the following conditions:
|
57
|
+
|
58
|
+
The above copyright notice and this permission notice shall be
|
59
|
+
included in all copies or substantial portions of the Software.
|
60
|
+
|
61
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
62
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
63
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
64
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
65
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
66
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
67
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'hoe', '>= 2.1.0'
|
3
|
+
require 'hoe'
|
4
|
+
require 'fileutils'
|
5
|
+
require './lib/ruote-amqp'
|
6
|
+
|
7
|
+
Hoe.plugin :newgem
|
8
|
+
# Hoe.plugin :website
|
9
|
+
# Hoe.plugin :cucumberfeatures
|
10
|
+
|
11
|
+
# Generate all the Rake tasks
|
12
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
13
|
+
$hoe = Hoe.spec 'ruote-amqp' do
|
14
|
+
self.developer 'Kenneth Kalmer', 'kenneth.kalmer@gmail.com'
|
15
|
+
self.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
|
16
|
+
self.rubyforge_name = self.name # TODO this is default value
|
17
|
+
self.extra_deps = [['ruote','= 0.9.20'], ['amqp', '= 0.6.0']]
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'newgem/tasks'
|
22
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
23
|
+
|
24
|
+
# TODO - want other tests/tasks run by default? Add them to the list
|
25
|
+
# remove_task :default
|
26
|
+
# task :default => [:spec, :features]
|
data/lib/ruote-amqp.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'openwfe'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
gem 'ruote', '>= 0.9.20'
|
9
|
+
require 'openwfe'
|
10
|
+
end
|
11
|
+
require 'openwfe/version'
|
12
|
+
|
13
|
+
if OpenWFE::OPENWFERU_VERSION < '0.9.20'
|
14
|
+
raise "ruote-amqp requires at least ruote-0.9.20"
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'yaml'
|
18
|
+
require 'json'
|
19
|
+
require 'mq'
|
20
|
+
|
21
|
+
module RuoteAMQP
|
22
|
+
VERSION = '0.9.20'
|
23
|
+
|
24
|
+
autoload 'Participant', 'ruote-amqp/participant'
|
25
|
+
autoload 'Listener', 'ruote-amqp/listener'
|
26
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'openwfe/service'
|
2
|
+
require 'openwfe/listeners/listener'
|
3
|
+
|
4
|
+
module RuoteAMQP
|
5
|
+
|
6
|
+
#
|
7
|
+
# = AMQP Listeners
|
8
|
+
#
|
9
|
+
# Used in conjunction with the AMQPParticipant, the AMQPListener
|
10
|
+
# subscribes to a specific direct exchange and monitors for
|
11
|
+
# incoming workitems. It expects workitems to arrive serialized as
|
12
|
+
# JSON (prefered), XML or YAML.
|
13
|
+
#
|
14
|
+
# == Configuration
|
15
|
+
#
|
16
|
+
# AMQP configuration is handled by directly manipulating the values of
|
17
|
+
# the +AMQP.settings+ hash, as provided by the AMQP gem. No
|
18
|
+
# defaults are set by the participant. The only +option+ parsed by
|
19
|
+
# the initializer of the listener is the +queue+ key (Hash
|
20
|
+
# expected). If no +queue+ key is set, the listener will subscribe
|
21
|
+
# to the +ruote+ direct exchange for workitems, otherwise it will
|
22
|
+
# subscribe to the direct exchange provided.
|
23
|
+
#
|
24
|
+
# The participant requires version 0.6.1 or later of the amqp gem.
|
25
|
+
#
|
26
|
+
# == Usage
|
27
|
+
#
|
28
|
+
# Register the listener with the engine:
|
29
|
+
#
|
30
|
+
# engine.register_listener( OpenWFE::Extras::AMQPListener )
|
31
|
+
#
|
32
|
+
# The listener leverages the asynchronous nature of the amqp gem,
|
33
|
+
# so no timers are setup when initialized.
|
34
|
+
#
|
35
|
+
# See the AMQPParticipany docs for information on sending
|
36
|
+
# workitems out to remote participants, and have them send replies
|
37
|
+
# to the correct direct exchange specified in the workitem
|
38
|
+
# attributes.
|
39
|
+
#
|
40
|
+
class Listener < OpenWFE::Service
|
41
|
+
include OpenWFE::WorkItemListener
|
42
|
+
|
43
|
+
# Listening queue
|
44
|
+
@@queue = 'ruote'
|
45
|
+
|
46
|
+
class << self
|
47
|
+
|
48
|
+
def queue
|
49
|
+
@@queue
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
# Only one option is used (:queue) to determine where to listen
|
55
|
+
# for work
|
56
|
+
def initialize( service_name, options )
|
57
|
+
|
58
|
+
if q = options.delete(:queue)
|
59
|
+
@@queue = q
|
60
|
+
end
|
61
|
+
|
62
|
+
super( service_name, options )
|
63
|
+
|
64
|
+
#if AMQP.connection.nil?
|
65
|
+
@em_thread = Thread.new { EM.run } unless EM.reactor_running?
|
66
|
+
#end
|
67
|
+
|
68
|
+
MQ.queue( @@queue, :durable => true ).subscribe do |message|
|
69
|
+
workitem = decode_workitem( message )
|
70
|
+
ldebug { "workitem from '#{@@queue}': #{workitem.inspect}" }
|
71
|
+
handle_item( workitem )
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def stop
|
76
|
+
linfo { "Stopping..." }
|
77
|
+
|
78
|
+
AMQP.stop { EM.stop } #if EM.reactor_running? }
|
79
|
+
@em_thread.join if @em_thread
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
# Complicated guesswork that needs to happen here to detect the format
|
85
|
+
def decode_workitem( msg )
|
86
|
+
ldebug { "decoding workitem from: #{msg}" }
|
87
|
+
|
88
|
+
hash = JSON.parse(msg)
|
89
|
+
OpenWFE.workitem_from_h( hash )
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
module RuoteAMQP
|
2
|
+
|
3
|
+
# = AMQP Participants
|
4
|
+
#
|
5
|
+
# The AMQPParticipant allows you to send workitems (serialized as
|
6
|
+
# JSON) or messages to any AMQP queues right from the process
|
7
|
+
# definition. When combined with the AMQPListener you can easily
|
8
|
+
# leverage an extremely powerful local/remote participant
|
9
|
+
# combinations.
|
10
|
+
#
|
11
|
+
# By default the participant relies on the presence of an AMQP
|
12
|
+
# listener. Workitems are sent and no replies are given to the
|
13
|
+
# engine. The participant can be configured to reply to the engine
|
14
|
+
# immediately after queueing a message, see the usage section below.
|
15
|
+
#
|
16
|
+
# == Configuration
|
17
|
+
#
|
18
|
+
# AMQP configuration is handled by directly manipulating the
|
19
|
+
# values of the +AMQP.settings+ hash, as provided by the AMQP
|
20
|
+
# gem. No AMQP defaults are set by the participant.
|
21
|
+
#
|
22
|
+
# The participant requires version 0.6.1 or later of the amqp gem.
|
23
|
+
#
|
24
|
+
# == Usage
|
25
|
+
#
|
26
|
+
# Currently it's possible to send either workitems or messages
|
27
|
+
# directly to a specific queue, and have the engine wait for
|
28
|
+
# replies on another queue (see AMQPListener).
|
29
|
+
#
|
30
|
+
# Setting up the participant
|
31
|
+
#
|
32
|
+
# engine.register_participant(
|
33
|
+
# :amqp, OpenWFE::Extras::AMQPParticipant )
|
34
|
+
#
|
35
|
+
# Setup a participant that always replies to the engine
|
36
|
+
#
|
37
|
+
# engine.register_participant(
|
38
|
+
# :amp, OpenWFE::Extras::AMQPParticipant.new(:reply_by_default => true ) )
|
39
|
+
#
|
40
|
+
# Sending a message example
|
41
|
+
#
|
42
|
+
# class AmqpMessageExample0 < OpenWFE::ProcessDefinition
|
43
|
+
# sequence do
|
44
|
+
# amqp :queue => 'test', :message => 'foo'
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# Sending a workitem
|
49
|
+
#
|
50
|
+
# class AmqpWorkitemExample0 < OpenWFE::ProcessDefinition
|
51
|
+
# sequence do
|
52
|
+
# amqp :queue => 'test'
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# Let the participant reply to the engine without involving the listener
|
57
|
+
#
|
58
|
+
# class AmqpWaitExample < OpenWFE::ProcessDefinition
|
59
|
+
# sequence do
|
60
|
+
# amqp :queue => 'test', :reply_anyway => true
|
61
|
+
# end
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# When waiting for a reply it only makes sense to send a workitem.
|
65
|
+
#
|
66
|
+
# == Workitem modifications
|
67
|
+
#
|
68
|
+
# To ease replies, and additional workitem attribute is set:
|
69
|
+
#
|
70
|
+
# 'reply_queue'
|
71
|
+
#
|
72
|
+
# +reply_queue+ has the name of the queue where the AMQPListener
|
73
|
+
# expects replies from remote participants
|
74
|
+
#
|
75
|
+
# == AMQP notes
|
76
|
+
#
|
77
|
+
# The participant currently only makes use of direct
|
78
|
+
# exchanges. Possible future improvements might see use for topic
|
79
|
+
# and fanout exchanges as well.
|
80
|
+
#
|
81
|
+
# The direct exchanges are always marked as durable by the
|
82
|
+
# participant.
|
83
|
+
#
|
84
|
+
class Participant
|
85
|
+
include OpenWFE::LocalParticipant
|
86
|
+
|
87
|
+
# Accepts an options hash with the following keys:
|
88
|
+
#
|
89
|
+
# * :reply_by_default => (bool) false by default
|
90
|
+
def initialize( options = {} )
|
91
|
+
ensure_reactor!
|
92
|
+
|
93
|
+
@options = {
|
94
|
+
:reply_by_default => false
|
95
|
+
}.merge( options )
|
96
|
+
end
|
97
|
+
|
98
|
+
# Process the workitem at hand. By default the workitem will be
|
99
|
+
# published to the direct exchange specified in the +queue+
|
100
|
+
# workitem parameter. You can specify a +message+ workitem
|
101
|
+
# parameter to have that sent instead of the workitem.
|
102
|
+
#
|
103
|
+
# To force the participant to reply to the engine, set the
|
104
|
+
# +reply_anyway+ workitem parameter.
|
105
|
+
def consume( workitem )
|
106
|
+
ldebug { "consuming workitem" }
|
107
|
+
ensure_reactor!
|
108
|
+
|
109
|
+
if target_queue = workitem.params['queue']
|
110
|
+
|
111
|
+
q = MQ.queue( target_queue, :durable => true )
|
112
|
+
|
113
|
+
# Message or workitem?
|
114
|
+
if message = ( workitem.attributes['message'] || workitem.params['message'] )
|
115
|
+
ldebug { "sending message to queue: #{target_queue}" }
|
116
|
+
q.publish( message )
|
117
|
+
|
118
|
+
else
|
119
|
+
ldebug { "sending workitem to queue: #{target_queue}" }
|
120
|
+
|
121
|
+
q.publish( encode_workitem( workitem ) )
|
122
|
+
end
|
123
|
+
else
|
124
|
+
lerror { "no queue in workitem params!" }
|
125
|
+
end
|
126
|
+
|
127
|
+
if @options[:reply_by_default] || workitem.params['reply-anyway'] == true
|
128
|
+
reply_to_engine( workitem )
|
129
|
+
end
|
130
|
+
|
131
|
+
ldebug { "done" }
|
132
|
+
end
|
133
|
+
|
134
|
+
def stop
|
135
|
+
linfo { "Stopping..." }
|
136
|
+
|
137
|
+
AMQP.stop { EM.stop } #if EM.reactor_running? }
|
138
|
+
@em_thread.join if @em_thread
|
139
|
+
end
|
140
|
+
|
141
|
+
protected
|
142
|
+
|
143
|
+
# Encode (and extend) the workitem as JSON
|
144
|
+
def encode_workitem( wi )
|
145
|
+
wi.attributes['reply_queue'] = Listener.queue
|
146
|
+
JSON.generate( wi.to_h )
|
147
|
+
end
|
148
|
+
|
149
|
+
def ensure_reactor!
|
150
|
+
@em_thread = Thread.new { EM.run } unless EM.reactor_running?
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
data/lib/spec/ruote.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module RuoteSpecHelpers
|
2
|
+
|
3
|
+
def purge_engine
|
4
|
+
@engine.application_context.values.each do |s|
|
5
|
+
s.purge if s.respond_to?(:purge)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def run_definition( pdef )
|
10
|
+
fei = @engine.launch( pdef )
|
11
|
+
wait( fei )
|
12
|
+
|
13
|
+
@engine.should_not have_errors
|
14
|
+
@engine.should_not have_remaining_expressions
|
15
|
+
|
16
|
+
purge_engine
|
17
|
+
end
|
18
|
+
|
19
|
+
def wait( fei )
|
20
|
+
Thread.pass
|
21
|
+
return if @terminated_processes.include?( fei.wfid )
|
22
|
+
@engine.wait_for( fei )
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
Spec::Matchers.define :have_errors do |*args|
|
2
|
+
match do |engine|
|
3
|
+
@ps = if fei = args.shift
|
4
|
+
engine.process_status( fei )
|
5
|
+
else
|
6
|
+
engine.process_statuses.values.first
|
7
|
+
end
|
8
|
+
|
9
|
+
if @ps
|
10
|
+
@ps.errors.size != 0
|
11
|
+
else
|
12
|
+
false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
failure_message_for_should do |engine|
|
17
|
+
"Expected engine to have errors, but didn't"
|
18
|
+
end
|
19
|
+
failure_message_for_should_not do |engine|
|
20
|
+
"Expected the engine to not have errors, but it did.\n" +
|
21
|
+
@ps.errors.values.map do |e|
|
22
|
+
" * error: #{e.error_class} \"#{e.stacktrace}\""
|
23
|
+
end.join("\n")
|
24
|
+
end
|
25
|
+
description do
|
26
|
+
#
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
Spec::Matchers.define :have_remaining_expressions do
|
31
|
+
match do |engine|
|
32
|
+
exp_count = engine.get_expression_storage.size
|
33
|
+
|
34
|
+
if exp_count == 1
|
35
|
+
false
|
36
|
+
else
|
37
|
+
50.times { Thread.pass }
|
38
|
+
exp_count = engine.get_expression_storage.size
|
39
|
+
if exp_count == 1
|
40
|
+
false
|
41
|
+
else
|
42
|
+
true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
failure_message_for_should do |engine|
|
48
|
+
"Expected engine to have processes remaining, but it didn't"
|
49
|
+
end
|
50
|
+
failure_message_for_should_not do |engine|
|
51
|
+
"Expected engine to have no processes remaining, but it did.#{engine.get_expression_storage.to_s}"
|
52
|
+
end
|
53
|
+
description do
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/ruote-amqp.rb'}"
|
9
|
+
puts "Loading ruote-amqp gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe RuoteAMQP::Listener do
|
4
|
+
|
5
|
+
it "should handle replies" do
|
6
|
+
pdef = <<-EOF
|
7
|
+
class AmqpParticipant2 < OpenWFE::ProcessDefinition
|
8
|
+
|
9
|
+
set :field => 'foo', :value => 'foo'
|
10
|
+
|
11
|
+
sequence do
|
12
|
+
echo '${f:foo}'
|
13
|
+
amqp :queue => 'test3'
|
14
|
+
echo '${f:foo}'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
EOF
|
18
|
+
|
19
|
+
@engine.register_participant( :amqp, RuoteAMQP::Participant )
|
20
|
+
|
21
|
+
@engine.register_listener( RuoteAMQP::Listener )
|
22
|
+
|
23
|
+
fei = @engine.launch pdef
|
24
|
+
|
25
|
+
begin
|
26
|
+
Timeout::timeout(10) do
|
27
|
+
msg = nil
|
28
|
+
MQ.queue('test3').subscribe { |msg| @msg = msg }
|
29
|
+
|
30
|
+
loop do
|
31
|
+
break unless @msg.nil?
|
32
|
+
sleep 1
|
33
|
+
end
|
34
|
+
end
|
35
|
+
rescue Timeout::Error
|
36
|
+
violated "Timeout waiting for message"
|
37
|
+
end
|
38
|
+
|
39
|
+
wi = OpenWFE::InFlowWorkItem.from_h( JSON.parse( @msg ) )
|
40
|
+
wi.attributes['foo'] = "bar"
|
41
|
+
|
42
|
+
MQ.queue( wi.attributes['reply_queue'] ).publish( JSON.generate( wi.to_h ) )
|
43
|
+
|
44
|
+
wait( fei )
|
45
|
+
|
46
|
+
@engine.should_not have_errors( fei )
|
47
|
+
@engine.should_not have_remaining_expressions
|
48
|
+
|
49
|
+
@tracer.to_s.should == "foo\nbar"
|
50
|
+
|
51
|
+
purge_engine
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe RuoteAMQP::Participant, :type => :ruote do
|
4
|
+
|
5
|
+
it "should support 'reply anyway' on expression parameter" do
|
6
|
+
|
7
|
+
pdef = <<-EOF
|
8
|
+
class AmqpParticipant0 < OpenWFE::ProcessDefinition
|
9
|
+
|
10
|
+
sequence do
|
11
|
+
amqp :queue => 'test1', 'reply_anyway' => true
|
12
|
+
echo 'done.'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
EOF
|
16
|
+
|
17
|
+
@engine.register_participant( :amqp, RuoteAMQP::Participant )
|
18
|
+
|
19
|
+
run_definition( pdef )
|
20
|
+
|
21
|
+
@tracer.to_s.should == 'done.'
|
22
|
+
|
23
|
+
begin
|
24
|
+
Timeout::timeout(10) do
|
25
|
+
@msg = nil
|
26
|
+
MQ.queue('test1').subscribe { |msg| @msg = msg }
|
27
|
+
|
28
|
+
loop do
|
29
|
+
break unless @msg.nil?
|
30
|
+
sleep 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
rescue Timeout::Error
|
34
|
+
violated "Timeout waiting for message"
|
35
|
+
end
|
36
|
+
|
37
|
+
@msg.should match(/^\{.*\}$/) # JSON message by default
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should support 'reply anyway' as participant configuration" do
|
41
|
+
pdef = <<-EOF
|
42
|
+
class AmqpParticipant0 < OpenWFE::ProcessDefinition
|
43
|
+
|
44
|
+
sequence do
|
45
|
+
amqp :queue => 'test4'
|
46
|
+
echo 'done.'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
EOF
|
50
|
+
|
51
|
+
p = RuoteAMQP::Participant.new( :reply_by_default => true )
|
52
|
+
@engine.register_participant( :amqp, p )
|
53
|
+
|
54
|
+
run_definition( pdef )
|
55
|
+
|
56
|
+
@tracer.to_s.should == "done."
|
57
|
+
|
58
|
+
begin
|
59
|
+
Timeout::timeout(10) do
|
60
|
+
@msg = nil
|
61
|
+
MQ.queue('test4').subscribe { |msg| @msg = msg }
|
62
|
+
|
63
|
+
loop do
|
64
|
+
break unless @msg.nil?
|
65
|
+
sleep 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
rescue Timeout::Error
|
69
|
+
violated "Timeout waiting for message"
|
70
|
+
end
|
71
|
+
|
72
|
+
@msg.should match( /^\{.*\}$/) # JSON message by default
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should support custom messages instead of workitems" do
|
76
|
+
pdef = <<-EOF
|
77
|
+
class AmqpParticipant1 < OpenWFE::ProcessDefinition
|
78
|
+
|
79
|
+
sequence do
|
80
|
+
amqp :queue => 'test2', :message => 'foo', 'reply_anyway' => true
|
81
|
+
echo 'done.'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
EOF
|
85
|
+
|
86
|
+
@engine.register_participant( :amqp, RuoteAMQP::Participant )
|
87
|
+
|
88
|
+
run_definition( pdef )
|
89
|
+
|
90
|
+
@tracer.to_s.should == "done."
|
91
|
+
|
92
|
+
begin
|
93
|
+
Timeout::timeout(10) do
|
94
|
+
@msg = nil
|
95
|
+
MQ.queue('test2').subscribe { |msg| @msg = msg }
|
96
|
+
|
97
|
+
loop do
|
98
|
+
break unless @msg.nil?
|
99
|
+
sleep 1
|
100
|
+
end
|
101
|
+
end
|
102
|
+
rescue Timeout::Error
|
103
|
+
violated "Timeout waiting for message"
|
104
|
+
end
|
105
|
+
|
106
|
+
@msg.should == 'foo'
|
107
|
+
end
|
108
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
begin
|
2
|
+
require 'spec'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
5
|
+
gem 'rspec'
|
6
|
+
require 'spec'
|
7
|
+
end
|
8
|
+
|
9
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
10
|
+
require 'ruote-amqp'
|
11
|
+
require 'spec/ruote'
|
12
|
+
require 'fileutils'
|
13
|
+
|
14
|
+
# AMQP magic worked here
|
15
|
+
AMQP.settings[:vhost] = '/ruote-test'
|
16
|
+
AMQP.settings[:user] = 'ruote'
|
17
|
+
AMQP.settings[:pass] = 'ruote'
|
18
|
+
|
19
|
+
Spec::Runner.configure do |config|
|
20
|
+
|
21
|
+
config.include( RuoteSpecHelpers )
|
22
|
+
|
23
|
+
config.before(:each) do
|
24
|
+
@tracer = Tracer.new
|
25
|
+
|
26
|
+
ac = {}
|
27
|
+
|
28
|
+
class << ac
|
29
|
+
alias :old_put :[]=
|
30
|
+
def []= (k, v)
|
31
|
+
raise("!!!!! #{k.class}\n#{k.inspect}") \
|
32
|
+
if k.class != String and k.class != Symbol
|
33
|
+
old_put(k, v)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
#
|
37
|
+
# useful for tracking misuses of the application context
|
38
|
+
|
39
|
+
ac['__tracer'] = @tracer
|
40
|
+
ac[:ruby_eval_allowed] = true
|
41
|
+
ac[:definition_in_launchitem_allowed] = true
|
42
|
+
|
43
|
+
@engine = OpenWFE::Engine.new( ac )
|
44
|
+
|
45
|
+
@terminated_processes = []
|
46
|
+
@engine.get_expression_pool.add_observer(:terminate) do |c, fe, wi|
|
47
|
+
@terminated_processes << fe.fei.wfid
|
48
|
+
#p [ :terminated, @terminated_processes ]
|
49
|
+
end
|
50
|
+
|
51
|
+
if ENV['DEBUG']
|
52
|
+
$OWFE_LOG = Logger.new( STDOUT )
|
53
|
+
$OWFE_LOG.level = Logger::DEBUG
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
config.after(:each) do
|
58
|
+
@engine.stop
|
59
|
+
AMQP.stop { EM.stop }
|
60
|
+
sleep 0.001 while EM.reactor_running?
|
61
|
+
end
|
62
|
+
|
63
|
+
config.after(:all) do
|
64
|
+
base = File.expand_path( File.dirname(__FILE__) + '/..' )
|
65
|
+
FileUtils.rm_rf( base + '/logs' )
|
66
|
+
FileUtils.rm_rf( base + '/work' )
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
class Tracer
|
72
|
+
def initialize
|
73
|
+
@trace = ''
|
74
|
+
end
|
75
|
+
def to_s
|
76
|
+
@trace.to_s.strip
|
77
|
+
end
|
78
|
+
def << s
|
79
|
+
@trace << s
|
80
|
+
end
|
81
|
+
def clear
|
82
|
+
@trace = ''
|
83
|
+
end
|
84
|
+
def puts s
|
85
|
+
@trace << "#{s}\n"
|
86
|
+
end
|
87
|
+
end
|
data/tasks/rspec.rake
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
begin
|
2
|
+
require 'spec'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
5
|
+
require 'spec'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'spec/rake/spectask'
|
9
|
+
rescue LoadError
|
10
|
+
puts <<-EOS
|
11
|
+
To use rspec for testing you must install rspec gem:
|
12
|
+
gem install rspec
|
13
|
+
EOS
|
14
|
+
exit(0)
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Run the specs under spec/models"
|
18
|
+
Spec::Rake::SpecTask.new do |t|
|
19
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
20
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruote-amqp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.20
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kenneth Kalmer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-07-13 00:00:00 +02:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: ruote
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - "="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.9.20
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: amqp
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.6.0
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: hoe
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 2.3.2
|
44
|
+
version:
|
45
|
+
description: ruote-amqp provides an AMQP participant/listener pair that allows you to distribute workitems out to AMQP consumers for processing. To learn more about remote participants in ruote please see http://openwfe.rubyforge.org/part.html
|
46
|
+
email:
|
47
|
+
- kenneth.kalmer@gmail.com
|
48
|
+
executables: []
|
49
|
+
|
50
|
+
extensions: []
|
51
|
+
|
52
|
+
extra_rdoc_files:
|
53
|
+
- History.txt
|
54
|
+
- Manifest.txt
|
55
|
+
- PostInstall.txt
|
56
|
+
files:
|
57
|
+
- History.txt
|
58
|
+
- Manifest.txt
|
59
|
+
- PostInstall.txt
|
60
|
+
- README.rdoc
|
61
|
+
- Rakefile
|
62
|
+
- lib/ruote-amqp.rb
|
63
|
+
- lib/ruote-amqp/listener.rb
|
64
|
+
- lib/ruote-amqp/participant.rb
|
65
|
+
- lib/spec/ruote.rb
|
66
|
+
- lib/spec/ruote_example_group.rb
|
67
|
+
- lib/spec/ruote_helpers.rb
|
68
|
+
- lib/spec/ruote_matchers.rb
|
69
|
+
- script/console
|
70
|
+
- script/destroy
|
71
|
+
- script/generate
|
72
|
+
- spec/listener_spec.rb
|
73
|
+
- spec/participant_spec.rb
|
74
|
+
- spec/spec.opts
|
75
|
+
- spec/spec_helper.rb
|
76
|
+
- tasks/rspec.rake
|
77
|
+
has_rdoc: true
|
78
|
+
homepage: http://github.com/kennethkalmer/ruote-amqp
|
79
|
+
post_install_message: PostInstall.txt
|
80
|
+
rdoc_options:
|
81
|
+
- --main
|
82
|
+
- README.rdoc
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: "0"
|
90
|
+
version:
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: "0"
|
96
|
+
version:
|
97
|
+
requirements: []
|
98
|
+
|
99
|
+
rubyforge_project: ruote-amqp
|
100
|
+
rubygems_version: 1.3.1
|
101
|
+
signing_key:
|
102
|
+
specification_version: 2
|
103
|
+
summary: ruote-amqp provides an AMQP participant/listener pair that allows you to distribute workitems out to AMQP consumers for processing
|
104
|
+
test_files: []
|
105
|
+
|