ruote 2.1.9 → 2.1.10
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/CHANGELOG.txt +32 -0
- data/CREDITS.txt +3 -0
- data/Rakefile +4 -4
- data/TODO.txt +55 -11
- data/examples/barley.rb +2 -1
- data/examples/flickr_report.rb +5 -6
- data/examples/web_first_page.rb +11 -0
- data/lib/ruote/context.rb +36 -13
- data/lib/ruote/engine.rb +88 -56
- data/lib/ruote/engine/process_error.rb +13 -0
- data/lib/ruote/engine/process_status.rb +33 -1
- data/lib/ruote/error_handler.rb +122 -0
- data/lib/ruote/evt/tracker.rb +27 -10
- data/lib/ruote/exp/fe_apply.rb +69 -0
- data/lib/ruote/exp/fe_participant.rb +33 -5
- data/lib/ruote/exp/flowexpression.rb +37 -5
- data/lib/ruote/exp/ro_persist.rb +8 -4
- data/lib/ruote/exp/ro_variables.rb +2 -2
- data/lib/ruote/fei.rb +59 -7
- data/lib/ruote/log/storage_history.rb +2 -0
- data/lib/ruote/log/test_logger.rb +28 -19
- data/lib/ruote/log/wait_logger.rb +4 -2
- data/lib/ruote/parser.rb +2 -1
- data/lib/ruote/part/dispatch_pool.rb +10 -10
- data/lib/ruote/part/engine_participant.rb +2 -2
- data/lib/ruote/part/local_participant.rb +99 -7
- data/lib/ruote/part/participant_list.rb +18 -7
- data/lib/ruote/part/storage_participant.rb +9 -6
- data/lib/ruote/receiver/base.rb +109 -10
- data/lib/ruote/storage/base.rb +118 -41
- data/lib/ruote/storage/fs_storage.rb +1 -0
- data/lib/ruote/storage/hash_storage.rb +2 -1
- data/lib/ruote/util/lookup.rb +22 -2
- data/lib/ruote/util/misc.rb +5 -5
- data/lib/ruote/version.rb +1 -1
- data/lib/ruote/worker.rb +50 -63
- data/lib/ruote/workitem.rb +64 -0
- data/ruote.gemspec +17 -12
- data/test/functional/base.rb +3 -1
- data/test/functional/concurrent_base.rb +35 -29
- data/test/functional/crunner.sh +19 -0
- data/test/functional/ct_0_concurrence.rb +17 -30
- data/test/functional/ct_1_iterator.rb +20 -17
- data/test/functional/ct_2_cancel.rb +32 -25
- data/test/functional/eft_12_listen.rb +2 -1
- data/test/functional/eft_23_apply.rb +23 -0
- data/test/functional/eft_3_participant.rb +27 -0
- data/test/functional/ft_11_recursion.rb +1 -1
- data/test/functional/ft_13_variables.rb +22 -0
- data/test/functional/ft_14_re_apply.rb +3 -0
- data/test/functional/ft_15_timeout.rb +1 -0
- data/test/functional/ft_20_storage_participant.rb +20 -2
- data/test/functional/ft_21_forget.rb +30 -0
- data/test/functional/ft_22_process_definitions.rb +2 -1
- data/test/functional/ft_24_block_participants.rb +1 -1
- data/test/functional/ft_25_receiver.rb +83 -1
- data/test/functional/ft_26_participant_timeout.rb +1 -1
- data/test/functional/ft_2_errors.rb +2 -5
- data/test/functional/ft_30_smtp_participant.rb +47 -45
- data/test/functional/ft_36_storage_history.rb +4 -4
- data/test/functional/ft_37_engine_participant.rb +14 -10
- data/test/functional/ft_38_participant_more.rb +178 -0
- data/test/functional/ft_39_wait_for.rb +100 -0
- data/test/functional/ft_40_participant_on_reply.rb +87 -0
- data/test/functional/ft_41_participants.rb +65 -0
- data/test/functional/ft_42_storage_copy.rb +67 -0
- data/test/functional/ft_5_on_error.rb +103 -0
- data/test/functional/ft_9_subprocesses.rb +2 -1
- data/test/functional/storage_helper.rb +5 -1
- data/test/functional/test.rb +4 -1
- data/test/functional/vertical.rb +46 -0
- data/test/unit/storage.rb +17 -1
- data/test/unit/storages.rb +27 -7
- data/test/unit/ut_11_lookup.rb +36 -0
- data/test/unit/ut_16_parser.rb +43 -0
- data/test/unit/ut_1_fei.rb +28 -1
- data/test/unit/ut_7_workitem.rb +23 -0
- metadata +67 -105
- data/lib/ruote/log/fs_history.rb +0 -182
- data/test/functional/ft_32_fs_history.rb +0 -188
- data/test/mpc_test.rb +0 -29
@@ -69,6 +69,19 @@ module Ruote
|
|
69
69
|
@h
|
70
70
|
end
|
71
71
|
|
72
|
+
# 'apply', 'reply', 'receive', ... Indicates in which "direction" the
|
73
|
+
# error occured.
|
74
|
+
#
|
75
|
+
def action
|
76
|
+
@h['msg']['action']
|
77
|
+
end
|
78
|
+
|
79
|
+
# Exposes the workitem fields directly.
|
80
|
+
#
|
81
|
+
def fields
|
82
|
+
@h['msg']['workitem']['fields']
|
83
|
+
end
|
84
|
+
|
72
85
|
protected
|
73
86
|
|
74
87
|
def to_dot (opts)
|
@@ -58,7 +58,39 @@ module Ruote
|
|
58
58
|
#
|
59
59
|
def root_expression
|
60
60
|
|
61
|
-
|
61
|
+
#@expressions.find { |e| e.fei.expid == '0' && e.fei.sub_wfid == nil }
|
62
|
+
# vanilla implementation
|
63
|
+
|
64
|
+
root_expressions.first
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns a list of all the expressions that have no parent expression.
|
68
|
+
# The list is sorted with the deeper (closer to the original root) first.
|
69
|
+
#
|
70
|
+
def root_expressions
|
71
|
+
|
72
|
+
roots = @expressions.select { |e| e.h.parent_id == nil }
|
73
|
+
|
74
|
+
roots = roots.inject({}) { |h, e|
|
75
|
+
h["#{e.h.fei['expid']}__#{e.h.fei['sub_wfid']}"] = e; h
|
76
|
+
}
|
77
|
+
|
78
|
+
roots.keys.sort.collect { |k| roots[k] }
|
79
|
+
end
|
80
|
+
|
81
|
+
# Given an expression id, returns the root (top ancestor) for its
|
82
|
+
# expression.
|
83
|
+
#
|
84
|
+
def root_expression_for (fei)
|
85
|
+
|
86
|
+
sfei = Ruote.sid(fei)
|
87
|
+
|
88
|
+
exp = @expressions.find { |fe| sfei == Ruote.sid(fe.fei) }
|
89
|
+
|
90
|
+
return nil unless exp
|
91
|
+
return exp if exp.parent_id.nil?
|
92
|
+
|
93
|
+
root_expression_for(exp.parent_id)
|
62
94
|
end
|
63
95
|
|
64
96
|
# Returns the process variables set for this process instance.
|
@@ -0,0 +1,122 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2005-2010, John Mettraux, jmettraux@gmail.com
|
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
|
+
# Made in Japan.
|
23
|
+
#++
|
24
|
+
|
25
|
+
|
26
|
+
module Ruote
|
27
|
+
|
28
|
+
#
|
29
|
+
# A ruote service for turning exceptions into process errors (or letting
|
30
|
+
# those error fire any potential :on_error attributes in the process
|
31
|
+
# definition).
|
32
|
+
#
|
33
|
+
# This service is used, by the worker, the dispatch pool and some
|
34
|
+
# receivers (like the one in ruote-beanstalk).
|
35
|
+
#
|
36
|
+
class ErrorHandler
|
37
|
+
|
38
|
+
def initialize (context)
|
39
|
+
|
40
|
+
@context = context
|
41
|
+
end
|
42
|
+
|
43
|
+
# As used by the dispatch pool and the worker.
|
44
|
+
#
|
45
|
+
def msg_handle (msg, exception)
|
46
|
+
|
47
|
+
fexp = Ruote::Exp::FlowExpression.fetch(
|
48
|
+
@context, msg['fei'] || msg['workitem']['fei'])
|
49
|
+
|
50
|
+
handle(msg, fexp, exception)
|
51
|
+
end
|
52
|
+
|
53
|
+
# As used by some receivers (see ruote-beanstalk's receiver).
|
54
|
+
#
|
55
|
+
def action_handle (action, fei, exception)
|
56
|
+
|
57
|
+
fexp = Ruote::Exp::FlowExpression.fetch(@context, fei)
|
58
|
+
|
59
|
+
msg = {
|
60
|
+
'action' => action,
|
61
|
+
'fei' => fei,
|
62
|
+
'participant_name' => fexp.h.participant_name,
|
63
|
+
'workitem' => fexp.h.applied_workitem }
|
64
|
+
|
65
|
+
handle(msg, fexp, exception)
|
66
|
+
end
|
67
|
+
|
68
|
+
protected
|
69
|
+
|
70
|
+
# As used by the worker.
|
71
|
+
#
|
72
|
+
def handle (msg, fexp, exception)
|
73
|
+
|
74
|
+
wfid = msg['wfid'] || (msg['fei']['wfid'] rescue nil)
|
75
|
+
fei = msg['fei'] || (fexp.h.fei rescue nil)
|
76
|
+
|
77
|
+
backtrace = exception.backtrace || []
|
78
|
+
|
79
|
+
# debug only
|
80
|
+
|
81
|
+
if $DEBUG || ARGV.include?('-d')
|
82
|
+
|
83
|
+
puts "\n== worker intercepted error =="
|
84
|
+
puts
|
85
|
+
p exception
|
86
|
+
puts backtrace[0, 20].join("\n")
|
87
|
+
puts "..."
|
88
|
+
puts
|
89
|
+
puts "-- msg --"
|
90
|
+
msg.keys.sort.each { |k|
|
91
|
+
puts " #{k.inspect} =>\n#{msg[k].inspect}"
|
92
|
+
}
|
93
|
+
puts "-- . --"
|
94
|
+
puts
|
95
|
+
end
|
96
|
+
|
97
|
+
# on_error ?
|
98
|
+
|
99
|
+
return if fexp && fexp.handle_on_error(msg, exception)
|
100
|
+
|
101
|
+
# emit 'msg'
|
102
|
+
|
103
|
+
@context.storage.put_msg(
|
104
|
+
'error_intercepted',
|
105
|
+
'message' => exception.inspect,
|
106
|
+
'wfid' => wfid,
|
107
|
+
'msg' => msg)
|
108
|
+
|
109
|
+
# fill error in the error journal
|
110
|
+
|
111
|
+
@context.storage.put(
|
112
|
+
'type' => 'errors',
|
113
|
+
'_id' => "err_#{Ruote.to_storage_id(fei)}",
|
114
|
+
'message' => exception.inspect,
|
115
|
+
'trace' => backtrace.join("\n"),
|
116
|
+
'fei' => fei,
|
117
|
+
'msg' => msg
|
118
|
+
) if fei
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
data/lib/ruote/evt/tracker.rb
CHANGED
@@ -25,6 +25,13 @@
|
|
25
25
|
|
26
26
|
module Ruote
|
27
27
|
|
28
|
+
#
|
29
|
+
# The tracker service is used by the "listen" expression. This services
|
30
|
+
# sees all the msg processed by a worker and triggers any
|
31
|
+
# listener interested in a particular msg.
|
32
|
+
#
|
33
|
+
# Look at the ListenExpression for more details.
|
34
|
+
#
|
28
35
|
class Tracker
|
29
36
|
|
30
37
|
def initialize (context)
|
@@ -44,6 +51,9 @@ module Ruote
|
|
44
51
|
end
|
45
52
|
end
|
46
53
|
|
54
|
+
# The worker passes all the messages it has to process to the tracker via
|
55
|
+
# this method.
|
56
|
+
#
|
47
57
|
def notify (msg)
|
48
58
|
|
49
59
|
doc = @context.storage.get_trackers
|
@@ -67,16 +77,8 @@ module Ruote
|
|
67
77
|
end
|
68
78
|
end
|
69
79
|
|
70
|
-
|
71
|
-
|
72
|
-
conditions.each do |k, v|
|
73
|
-
val = msg[k]
|
74
|
-
return false unless val && val.match(v)
|
75
|
-
end
|
76
|
-
|
77
|
-
true
|
78
|
-
end
|
79
|
-
|
80
|
+
# Adds a tracker (usually when a 'listen' expression gets applied).
|
81
|
+
#
|
80
82
|
def add_tracker (wfid, action, fei, conditions, msg, doc=nil)
|
81
83
|
|
82
84
|
doc ||= @context.storage.get_trackers
|
@@ -94,6 +96,9 @@ module Ruote
|
|
94
96
|
# the put failed, have to redo the work
|
95
97
|
end
|
96
98
|
|
99
|
+
# Removes a tracker (usually when a 'listen' expression replies to its
|
100
|
+
# parent expression or is cancelled).
|
101
|
+
#
|
97
102
|
def remove_tracker (fei, doc=nil)
|
98
103
|
|
99
104
|
doc ||= @context.storage.get_trackers
|
@@ -105,6 +110,18 @@ module Ruote
|
|
105
110
|
remove_tracker(fei, r) if r
|
106
111
|
# the put failed, have to redo the work
|
107
112
|
end
|
113
|
+
|
114
|
+
protected
|
115
|
+
|
116
|
+
def does_match? (msg, conditions)
|
117
|
+
|
118
|
+
conditions.each do |k, v|
|
119
|
+
val = msg[k]
|
120
|
+
return false unless val && val.match(v)
|
121
|
+
end
|
122
|
+
|
123
|
+
true
|
124
|
+
end
|
108
125
|
end
|
109
126
|
end
|
110
127
|
|
data/lib/ruote/exp/fe_apply.rb
CHANGED
@@ -55,6 +55,73 @@ module Ruote::Exp
|
|
55
55
|
# echo 'nada'
|
56
56
|
#
|
57
57
|
#
|
58
|
+
# == apply and subprocesses
|
59
|
+
#
|
60
|
+
# There is an interesting way of using 'apply', it's close to the way of
|
61
|
+
# the Ruby "yield" expression.
|
62
|
+
#
|
63
|
+
# pdef = Ruote.process_definition 'test' do
|
64
|
+
# sequence do
|
65
|
+
# handle do
|
66
|
+
# participant 'alpha'
|
67
|
+
# end
|
68
|
+
# handle do
|
69
|
+
# participant 'bravo'
|
70
|
+
# end
|
71
|
+
# end
|
72
|
+
# define 'handle' do
|
73
|
+
# sequence do
|
74
|
+
# participant 'prepare_data'
|
75
|
+
# apply
|
76
|
+
# participant 'rearrange_data'
|
77
|
+
# end
|
78
|
+
# end
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# With this process definition, the particpant alpha and bravo are handed
|
82
|
+
# a workitem in sequence, but each time, the data gets prepared and
|
83
|
+
# re-arranged.
|
84
|
+
#
|
85
|
+
# 'apply' simply picks the value of the tree to apply in the local variable
|
86
|
+
# 'tree'.
|
87
|
+
#
|
88
|
+
# Passing variables to applied trees is possible :
|
89
|
+
#
|
90
|
+
# pdef = Ruote.process_definition do
|
91
|
+
# handle do
|
92
|
+
# participant '${v:target}', :message => 'x'
|
93
|
+
# end
|
94
|
+
# define 'handle' do
|
95
|
+
# sequence do
|
96
|
+
# participant 'prepare_data'
|
97
|
+
# apply :v => 'alpha'
|
98
|
+
# apply :v => 'bravo'
|
99
|
+
# participant 'rearrange_data'
|
100
|
+
# end
|
101
|
+
# end
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
#
|
105
|
+
# == on_error
|
106
|
+
#
|
107
|
+
# It's OK, to place an on_error on the apply
|
108
|
+
#
|
109
|
+
# pdef = Ruote.process_definition do
|
110
|
+
# handle do
|
111
|
+
# sequence do
|
112
|
+
# echo 'in'
|
113
|
+
# nemo
|
114
|
+
# end
|
115
|
+
# end
|
116
|
+
# define 'handle' do
|
117
|
+
# apply :on_error => 'notify' # <==
|
118
|
+
# echo 'over.'
|
119
|
+
# end
|
120
|
+
# define 'notify' do
|
121
|
+
# echo 'error'
|
122
|
+
# end
|
123
|
+
# end
|
124
|
+
#
|
58
125
|
class ApplyExpression < FlowExpression
|
59
126
|
|
60
127
|
names :apply
|
@@ -62,6 +129,8 @@ module Ruote::Exp
|
|
62
129
|
# TODO : maybe accept directly ruby and xml (and json)
|
63
130
|
# TODO : _yield ?
|
64
131
|
|
132
|
+
# TODO : apply [ 'echo', { 'nada' => nil }, [] ]
|
133
|
+
|
65
134
|
def apply
|
66
135
|
|
67
136
|
#
|
@@ -23,7 +23,6 @@
|
|
23
23
|
#++
|
24
24
|
|
25
25
|
|
26
|
-
require 'ruote/workitem'
|
27
26
|
require 'ruote/exp/condition'
|
28
27
|
|
29
28
|
|
@@ -120,6 +119,10 @@ module Ruote::Exp
|
|
120
119
|
|
121
120
|
names :participant
|
122
121
|
|
122
|
+
# Should return true when the dispatch was successful.
|
123
|
+
#
|
124
|
+
h_reader :dispatched
|
125
|
+
|
123
126
|
def apply
|
124
127
|
|
125
128
|
#
|
@@ -157,17 +160,18 @@ module Ruote::Exp
|
|
157
160
|
'fei' => h.fei,
|
158
161
|
'participant_name' => h.participant_name,
|
159
162
|
'workitem' => h.applied_workitem,
|
160
|
-
'for_engine_worker?' => participant_info.class != Array)
|
161
|
-
#
|
162
|
-
# NOTE : is this for_engine_worker? still necessary ?
|
163
|
+
'for_engine_worker?' => (participant_info.class != Array))
|
163
164
|
end
|
164
165
|
|
165
166
|
def cancel (flavour)
|
166
167
|
|
168
|
+
return reply_to_parent(h.applied_workitem) unless h.participant_name
|
169
|
+
# no participant, reply immediately
|
170
|
+
|
167
171
|
do_persist || return
|
168
172
|
#
|
169
173
|
# if do_persist returns false, it means we're operating on stale
|
170
|
-
# data and
|
174
|
+
# data and cannot continue
|
171
175
|
|
172
176
|
@context.storage.put_msg(
|
173
177
|
'dispatch_cancel',
|
@@ -177,6 +181,16 @@ module Ruote::Exp
|
|
177
181
|
'workitem' => h.applied_workitem)
|
178
182
|
end
|
179
183
|
|
184
|
+
def reply (workitem)
|
185
|
+
|
186
|
+
pa = @context.plist.lookup(
|
187
|
+
workitem['participant_name'], :on_reply => true)
|
188
|
+
|
189
|
+
pa.on_reply(Ruote::Workitem.new(workitem)) if pa
|
190
|
+
|
191
|
+
super(workitem)
|
192
|
+
end
|
193
|
+
|
180
194
|
def reply_to_parent (workitem)
|
181
195
|
|
182
196
|
workitem['fields'].delete('params')
|
@@ -186,6 +200,20 @@ module Ruote::Exp
|
|
186
200
|
|
187
201
|
protected
|
188
202
|
|
203
|
+
# Once the dispatching work (done by the dispatch pool) is done, a
|
204
|
+
# 'dispatched' msg is sent, we have to flag the participant expression
|
205
|
+
# as 'dispatched' => true
|
206
|
+
#
|
207
|
+
# See http://groups.google.com/group/openwferu-users/browse_thread/thread/ff29f26d6b5fd135
|
208
|
+
# for the motivation.
|
209
|
+
#
|
210
|
+
def do_dispatched (msg)
|
211
|
+
|
212
|
+
h.dispatched = true
|
213
|
+
do_persist
|
214
|
+
# let's not care if it fails...
|
215
|
+
end
|
216
|
+
|
189
217
|
# Overriden with an empty behaviour. The work is now done a bit later
|
190
218
|
# via the #schedule_timeout method.
|
191
219
|
#
|
@@ -31,7 +31,27 @@ require 'ruote/util/hashdot'
|
|
31
31
|
module Ruote::Exp
|
32
32
|
|
33
33
|
#
|
34
|
-
#
|
34
|
+
# Ruote is a process definition interpreter. It doesn't directly "read"
|
35
|
+
# process definitions, it relies on a parser/generator to produce "abstract
|
36
|
+
# syntax trees" that look like
|
37
|
+
#
|
38
|
+
# [ expression_name, { ... attributes ... }, [ children_expressions ] ]
|
39
|
+
#
|
40
|
+
# The nodes (and leaves) in the trees are expressions. This is the base
|
41
|
+
# class for all expressions.
|
42
|
+
#
|
43
|
+
# The most visible expressions are "define", "sequence" and "participant".
|
44
|
+
# Think :
|
45
|
+
#
|
46
|
+
# pdef = Ruote.process_definition do
|
47
|
+
# sequence do
|
48
|
+
# participant :ref => 'customer'
|
49
|
+
# participant :ref => 'accounting'
|
50
|
+
# participant :ref => 'logistics'
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# Each node is an expression...
|
35
55
|
#
|
36
56
|
class FlowExpression
|
37
57
|
|
@@ -390,9 +410,14 @@ module Ruote::Exp
|
|
390
410
|
def do_fail (msg)
|
391
411
|
|
392
412
|
@h['state'] = 'failing'
|
393
|
-
|
413
|
+
@h['applied_workitem'] = msg['workitem']
|
394
414
|
|
395
|
-
h.children.
|
415
|
+
if h.children.size < 1
|
416
|
+
reply_to_parent(@h['applied_workitem'])
|
417
|
+
else
|
418
|
+
persist_or_raise
|
419
|
+
h.children.each { |i| @context.storage.put_msg('cancel', 'fei' => i) }
|
420
|
+
end
|
396
421
|
end
|
397
422
|
|
398
423
|
#--
|
@@ -449,6 +474,7 @@ module Ruote::Exp
|
|
449
474
|
elsif h.parent_id
|
450
475
|
|
451
476
|
par = parent
|
477
|
+
# :( get_parent would probably be a better name for #parent
|
452
478
|
|
453
479
|
unless par
|
454
480
|
puts "~~"
|
@@ -469,7 +495,7 @@ module Ruote::Exp
|
|
469
495
|
|
470
496
|
# Looks up parent with on_error attribute and triggers it
|
471
497
|
#
|
472
|
-
def handle_on_error
|
498
|
+
def handle_on_error (msg, error)
|
473
499
|
|
474
500
|
return false if h.state == 'failing'
|
475
501
|
|
@@ -483,10 +509,15 @@ module Ruote::Exp
|
|
483
509
|
return false if handler == ''
|
484
510
|
# empty on_error handler nullifies ancestor's on_error
|
485
511
|
|
512
|
+
workitem = msg['workitem']
|
513
|
+
|
514
|
+
workitem['fields']['__error__'] = [
|
515
|
+
h.fei, Ruote.now_to_utc_s, error.class.to_s, error.message ]
|
516
|
+
|
486
517
|
@context.storage.put_msg(
|
487
518
|
'fail',
|
488
519
|
'fei' => oe_parent.h.fei,
|
489
|
-
'
|
520
|
+
'workitem' => workitem)
|
490
521
|
|
491
522
|
true # yes, error is being handled.
|
492
523
|
end
|
@@ -685,6 +716,7 @@ module Ruote::Exp
|
|
685
716
|
def trigger (on, workitem)
|
686
717
|
|
687
718
|
hon = h[on]
|
719
|
+
|
688
720
|
t = hon.is_a?(String) ? [ hon, {}, [] ] : hon
|
689
721
|
|
690
722
|
if on == 'on_error'
|