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/CHANGELOG.txt CHANGED
@@ -2,6 +2,11 @@
2
2
  = ruote-amqp
3
3
 
4
4
 
5
+ == ruote-amqp - 2.3.0 released 2012/09/02
6
+
7
+ - complete rework to better adhere to the AMQP philosophy
8
+
9
+
5
10
  == ruote-amqp - 2.2.0 released 2011/03/01
6
11
 
7
12
  - receiver : exposing #decode_workitem for overwriting
data/CREDITS.txt CHANGED
@@ -12,6 +12,7 @@
12
12
 
13
13
  == CONTRIBUTORS
14
14
 
15
+ * Jiří Kubíček - https://github.com/kubicek
15
16
  * Mario Camou
16
17
  * Sean Johnson - https://github.com/belucid
17
18
  * Victor Liu - https://github.com/pennymax
@@ -27,3 +28,6 @@
27
28
 
28
29
  == FEEDBACK
29
30
 
31
+ * Jim Li - https://github.com/marsbomber - auto_recovery and co
32
+ * Marco Sehrer - https://github.com/pixelvitamina - ruote 2.2 vs 2.3
33
+
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+
2
+ Copyright (c) 2010-2012 Kenneth Kalmer
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+
data/README.md ADDED
@@ -0,0 +1,173 @@
1
+
2
+ # ruote-amqp
3
+
4
+ ruote-amqp is a set of classes that let a ruote engine publish and/or receive messages over AMQP.
5
+
6
+ The most common use case is publishing workitems for processing by AMQP consumers and eventually receiving them back to resume the flow.
7
+
8
+ Another use case would be to listen on an AMQP queue for workflow launch requests.
9
+
10
+ Listening for arbitrary AMQP messages before resuming a flow (ambush/alert) is also possible.
11
+
12
+
13
+ ## usage
14
+
15
+ ### Ruote::Amqp::Participant
16
+
17
+ Publishing messages
18
+
19
+ ```ruby
20
+ $dashboard.register(
21
+ :toto,
22
+ Ruote::Amqp::Participant,
23
+ :exchange => [ 'direct', '' ],
24
+ :routing_key => 'alpha')
25
+
26
+ pdef = Ruote.define do
27
+ toto
28
+ end
29
+
30
+ $dashboard.launch(pdef)
31
+
32
+ # ...
33
+ ```
34
+
35
+ ### Ruote::Amqp::AlertParticipant
36
+
37
+ Ambushing messages from a process definition. The alert participant when
38
+ receiving a workitem starts waiting for the next message on a given queue. When
39
+ the message arrives, it responds to the engine (with a
40
+
41
+ ```ruby
42
+ $dashboard.register(
43
+ :wait_for_info,
44
+ Ruote::Amqp::AlertParticipant,
45
+ :queue => 'info')
46
+
47
+ pdef = Ruote.define do
48
+ # ... before
49
+ wait_for_job # flows wait for first message on 'info' queue
50
+ # ... after
51
+ end
52
+ ```
53
+
54
+ By default it waits for 1 message that it places in the "amqp_message" field
55
+ of the workitem going back to the engine.
56
+
57
+ One can override the #handle method to change the way the workitem is modified
58
+ according to the message.
59
+
60
+ It's also OK to override the #on_workitem method of this participant if one
61
+ waits to wait for more than 1 message.
62
+
63
+ See the AlertParticipant rdoc for more.
64
+
65
+ ### Ruote::Amqp::Receiver
66
+
67
+ Receiving messages.
68
+
69
+ A receiver is a ruote service subscribed to a queue. When a message comes on
70
+ the queue, the receiver will look at it and, according to the payload, either
71
+ launch a new workflow instance, resume a currently workflow instance segment
72
+ or pass an error back from a participant to the engine.
73
+
74
+ (In fact the resume workflow / pass participant error back to the engine are
75
+ closely related)
76
+
77
+ (NTS: at some point, receivers should be able to deal with "cancel messages")
78
+
79
+ ```ruby
80
+ # A simple coupling between participant "toto" and a receiver via AMQP
81
+ #
82
+ # A real world example would have toto publishing somewhere, the message
83
+ # getting fetched by the real (remote) participant and then handed back
84
+ # on the queue the receiver is subscribed to.
85
+
86
+ $dashboard.register(
87
+ :toto,
88
+ Ruote::Amqp::Participant,
89
+ :exchange => [ 'direct', '' ],
90
+ :routing_key => 'alpha')
91
+
92
+ receiver = Ruote::Amqp::Receiver.new(
93
+ $dashboard, AMQP::Channel.new.queue('alpha'))
94
+
95
+ # ...
96
+ ```
97
+
98
+ ### Controlling the connection (AMQP session)
99
+
100
+ The Ruote::Amqp module has a handy singleton for connections (actually
101
+ AMQP::Session instances).
102
+
103
+ ```ruby
104
+ # (before registering participants)
105
+
106
+ Ruote::Amqp.session = AMQP.connect(:auto_recovery => true) do |con|
107
+ con.on_recovery do |con|
108
+ puts "Recovered..."
109
+ end
110
+ connection.on_tcp_connection_loss do |con, settings|
111
+ puts "Reconnecting... please wait"
112
+ conn.reconnect(false, 20)
113
+ end
114
+ end
115
+ ```
116
+
117
+ When a participant tries to connect to AMQP, it will automatically use the value in Ruote::Amqp.session (else it will set up a new connection).
118
+
119
+ The receivers expect a queue when they are set up, feel free to set Ruote::Amqp.session, then use it when instantiating receivers (the participant will follow suit).
120
+
121
+ If you want a different way of connecting to AMQP for the participants, you can override their #amqp_connect methods (or pass them AMQP connection settings when registering them).
122
+
123
+
124
+ ## requirements
125
+
126
+ * ruote[http://ruote.rubyforge.org] 2.3.0 or later
127
+ * amqp[http://rubyamqp.info/] 0.9.0 or later
128
+ * rabbitmq[http://www.rabbitmq.com/] 2.2.0 or later
129
+
130
+
131
+ ## install
132
+
133
+ Please be sure to have read the requirements section above
134
+
135
+ gem install ruote-amqp
136
+
137
+ or via your Gemfile (thanks [bundler](http://gembundler.com)).
138
+
139
+
140
+ ## tests / specs
141
+
142
+ To run the tests you need the following requirements met, or the testing environment will fail horribly (or simply get stuck without output).
143
+
144
+
145
+ ### RabbitMQ vhost
146
+
147
+ The tests use dedicated vhost on a running AMQP broker. To configure RabbitMQ
148
+ you can run the following commands (the RabbitMQ server must be running):
149
+
150
+ $ rabbitmqctl add_vhost ruote-test
151
+ $ rabbitmqctl add_user ruote ruote
152
+ $ rabbitmqctl set_permissions -p ruote-test ruote '.*' '.*' '.*'
153
+
154
+ or by running:
155
+
156
+ $ rake prepare
157
+
158
+
159
+ If you need to change the AMQP configuration used by the tests, edit the
160
+ +spec/spec_helper.rb+ file.
161
+
162
+
163
+ ## daemon-kit
164
+
165
+ Kenneth Kalmer, the original author of the ruote-amqp gem is also the author of [DaemonKit](https://github.com/kennethkalmer/daemon-kit) a library/toolbox for building daemons.
166
+
167
+ It used to be the preferred way to wrap remote participants (as daemons) but lately Kenneth hasn't had much time for support. It's still full of excellent ideas.
168
+
169
+
170
+ ## license
171
+
172
+ MIT, see LICENSE.txt
173
+
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'rubygems/user_interaction' if Gem::RubyGemsVersion == '1.5.0'
6
6
 
7
7
  require 'rake'
8
8
  require 'rake/clean'
9
- require 'rake/rdoctask'
9
+ #require 'rake/rdoctask'
10
10
 
11
11
 
12
12
  #
@@ -68,34 +68,36 @@ task :prepare do
68
68
  end
69
69
 
70
70
 
71
+ ##
72
+ ## rdoc
73
+ ##
74
+ ## make sure to have rdoc 2.5.x to run that
71
75
  #
72
- # rdoc
76
+ #Rake::RDocTask.new do |rd|
73
77
  #
74
- # make sure to have rdoc 2.5.x to run that
75
-
76
- Rake::RDocTask.new do |rd|
77
-
78
- rd.main = 'README.rdoc'
79
- rd.rdoc_dir = 'rdoc'
80
-
81
- rd.rdoc_files.include(
82
- 'README.rdoc', 'CHANGELOG.txt', 'CREDITS.txt', 'lib/**/*.rb')
83
-
84
- rd.title = "#{GEMSPEC.name} #{GEMSPEC.version}"
85
- end
86
-
87
-
78
+ # rd.main = 'README.rdoc'
79
+ # rd.rdoc_dir = 'rdoc'
88
80
  #
89
- # upload_rdoc
90
-
91
- desc %{
92
- upload the rdoc to rubyforge
93
- }
94
- task :upload_rdoc => [ :clean, :rdoc ] do
95
-
96
- account = 'jmettraux@rubyforge.org'
97
- webdir = '/var/www/gforge-projects/ruote'
98
-
99
- sh "rsync -azv -e ssh rdoc/#{GEMSPEC.name}_rdoc #{account}:#{webdir}/"
100
- end
81
+ # rd.rdoc_files.include(
82
+ # 'README.rdoc', 'CHANGELOG.txt', 'CREDITS.txt', 'lib/**/*.rb')
83
+ #
84
+ # rd.title = "#{GEMSPEC.name} #{GEMSPEC.version}"
85
+ #end
86
+ #
87
+ #
88
+ ##
89
+ ## upload_rdoc
90
+ #
91
+ #desc %{
92
+ # upload the rdoc to rubyforge
93
+ #}
94
+ #task :upload_rdoc => [ :clean, :rdoc ] do
95
+ #
96
+ # account = 'jmettraux@rubyforge.org'
97
+ # webdir = '/var/www/gforge-projects/ruote'
98
+ #
99
+ # sh "rsync -azv -e ssh rdoc/#{GEMSPEC.name}_rdoc #{account}:#{webdir}/"
100
+ #end
101
+ #
102
+ # leverarge rdoc.info instead
101
103
 
data/TODO.txt CHANGED
@@ -6,3 +6,11 @@
6
6
  [ ] have a class method ParticipantProxy.stop_all ?
7
7
  [ ] use Ruote::Workitem #as_json and #from_json(s)
8
8
 
9
+ *** 2.3.0
10
+
11
+ [x] https://gist.github.com/1944228
12
+ [ ] ON_CANCEL !
13
+
14
+ [ ] incorporate
15
+ https://github.com/marsbomber/ruote-amqp/commit/0f36a41f4a0254847a7b9a7c4b2098c8164f21f3
16
+
@@ -0,0 +1,146 @@
1
+ #--
2
+ # Copyright (c) 2010-2012, Kenneth Kalmer, John Mettraux.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #++
22
+
23
+
24
+ module Ruote::Amqp
25
+
26
+ #
27
+ # The alert participant, when invoked from a process instance will
28
+ # lay in wait for the next message on a given queue. As soon as the message
29
+ # comes in, it will pack it in the workitem fields and let the process
30
+ # definition resume.
31
+ #
32
+ # @dashboard.register(
33
+ # :wait_for_info,
34
+ # Ruote::Amqp::AlertParticipant,
35
+ # :queue => 'info')
36
+ #
37
+ # pdef = Ruote.define do
38
+ # wait_for_job
39
+ # # ... the rest of the flow
40
+ # end
41
+ #
42
+ # == configuration
43
+ #
44
+ # This class is mostly a subclass of Ruote::Amqp::Participant, it accepts
45
+ # the same configuration options (but has no need for 'exchange'). It
46
+ # accepts a 'queue' option in the form [ 'queue_name', { queue options } ].
47
+ #
48
+ #
49
+ # == overriding #handle(header, payload)
50
+ #
51
+ # The default implementation for this method is:
52
+ #
53
+ # def handle(header, payload)
54
+ # workitem.fields['amqp_message'] = [ header.to_hash, payload ]
55
+ # end
56
+ #
57
+ # One is free to override it:
58
+ #
59
+ # class MyAlertParticipant < Ruote::Amqp::AlertParticipant
60
+ # def handle(header, payload)
61
+ # fields = Rufus::Json.decode(payload)
62
+ # workitem.fields.merge!(fields)
63
+ # end
64
+ # end
65
+ #
66
+ #
67
+ # == overriding #on_workitem
68
+ #
69
+ # Out of the box, the alert participant listens for 1 message on 1 queue.
70
+ # It's not too difficult to change that.
71
+ #
72
+ # Resuming after 3 messages:
73
+ #
74
+ # class MyAlertParticipant < Ruote::Amqp::AlertParticipant
75
+ #
76
+ # def on_workitem
77
+ #
78
+ # messages = []
79
+ #
80
+ # queue.subscribe { |header, payload |
81
+ #
82
+ # messages << payload
83
+ #
84
+ # if messages.size > 2
85
+ # queue.unsubscribe
86
+ # workitem.fields['messages'] = messages
87
+ # reply # let the flow resume
88
+ # end
89
+ # }
90
+ # end
91
+ # end
92
+ #
93
+ # Observing 2 queues:
94
+ #
95
+ # class MyAlertParticipant < Ruote::Amqp::AlertParticipant
96
+ #
97
+ # def on_workitem
98
+ #
99
+ # messages = []
100
+ #
101
+ # q0 = channel.queue('zero')
102
+ # q1 = channel.queue('one')
103
+ #
104
+ # [ q0, q1 ].subscribe { |header, payload |
105
+ # messages << payload
106
+ # }
107
+ #
108
+ # sleep 1.0 while messages.size < 2
109
+ #
110
+ # reply # let the flow resume
111
+ # end
112
+ # end
113
+ #
114
+ class AlertParticipant < Participant
115
+
116
+ def on_workitem
117
+
118
+ queue.subscribe { |header, payload|
119
+
120
+ queue.unsubscribe
121
+ handle(header, payload)
122
+ reply
123
+ }
124
+ end
125
+
126
+ protected
127
+
128
+ # Called when the AMQP message comes in. This default implementation
129
+ # stuffs the AMQP [ header, payload ] into an 'amqp_message' workitem
130
+ # field.
131
+ #
132
+ def handle(header, payload)
133
+
134
+ workitem.fields['amqp_message'] = [ header.to_hash, payload ]
135
+ end
136
+
137
+ # Looks at the configuration options ('connection' and 'queue') and
138
+ # returns the queue the participant will fetch a message from.
139
+ #
140
+ def queue
141
+
142
+ @queue ||= channel.queue(*(opt('queue') || [ '' ]))
143
+ end
144
+ end
145
+ end
146
+