ruote 2.1.4 → 2.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +26 -6
- data/CREDITS.txt +1 -1
- data/TODO.txt +20 -9
- data/examples/ruote_quickstart.rb +3 -2
- data/lib/ruote.rb +1 -0
- data/lib/ruote/context.rb +5 -0
- data/lib/ruote/engine.rb +5 -12
- data/lib/ruote/engine/process_error.rb +13 -0
- data/lib/ruote/engine/process_status.rb +18 -1
- data/lib/ruote/exp/condition.rb +0 -5
- data/lib/ruote/exp/fe_participant.rb +12 -6
- data/lib/ruote/exp/fe_subprocess.rb +4 -37
- data/lib/ruote/exp/fe_when.rb +1 -1
- data/lib/ruote/exp/flowexpression.rb +59 -28
- data/lib/ruote/exp/ro_persist.rb +4 -7
- data/lib/ruote/exp/ro_variables.rb +6 -2
- data/lib/ruote/fei.rb +9 -0
- data/lib/ruote/log/wait_logger.rb +1 -0
- data/lib/ruote/part/engine_participant.rb +185 -0
- data/lib/ruote/part/storage_participant.rb +69 -13
- data/lib/ruote/participant.rb +1 -0
- data/lib/ruote/storage/base.rb +1 -1
- data/lib/ruote/subprocess.rb +68 -0
- data/lib/ruote/util/dollar.rb +23 -5
- data/lib/ruote/worker.rb +30 -9
- data/ruote.gemspec +5 -2
- data/test/functional/eft_27_inc.rb +6 -6
- data/test/functional/ft_10_dollar.rb +84 -1
- data/test/functional/ft_1_process_status.rb +43 -0
- data/test/functional/ft_20_storage_participant.rb +124 -5
- data/test/functional/ft_2_errors.rb +11 -4
- data/test/functional/ft_37_engine_participant.rb +295 -0
- metadata +5 -2
data/CHANGELOG.txt
CHANGED
@@ -1,7 +1,27 @@
|
|
1
1
|
|
2
2
|
= ruote - CHANGELOG.txt
|
3
3
|
|
4
|
-
|
4
|
+
|
5
|
+
== ruote - 2.1.5 released 2010/01/28
|
6
|
+
|
7
|
+
- fixed StorageParticipant a to b flow, fix by Torsten
|
8
|
+
- fixed StorageParticipant#cancel
|
9
|
+
- dollar : made sure of ${fei} and ${wfid}
|
10
|
+
- implemented ProcessStatus#to_dot
|
11
|
+
- if a participant implementation cancel method returns false,
|
12
|
+
reply_to_parent will not get called
|
13
|
+
- changes about errors without [stored] expressions, they now appear in process
|
14
|
+
statuses
|
15
|
+
- EngineParticipant implemented
|
16
|
+
- 'ruby_eval_allowed' instead of :ruby_eval_allowed
|
17
|
+
- ${v:customer.address.1} deep trick now ok (as it was ok for fields)
|
18
|
+
- added d() to the $ notation : echo "${r: d('f:toto') }"
|
19
|
+
- Engine#kill_process(wfid) can cure errored participant expressions
|
20
|
+
- made sure ${r:wi} and ${r:workitem} is and instance of Ruote::Workitem
|
21
|
+
- implemented StorageParticipant#by_field
|
22
|
+
|
23
|
+
|
24
|
+
== ruote - 2.1.4 released 2010/01/11
|
5
25
|
|
6
26
|
- implemented StorageHistory
|
7
27
|
- using yyyy-mm-dd instead of yyyy/mm/dd for Ruote.time_to_utc_s(t)
|
@@ -9,19 +29,19 @@
|
|
9
29
|
- Engine#add_service now returning just bound service
|
10
30
|
|
11
31
|
|
12
|
-
== ruote - 2.1.3
|
32
|
+
== ruote - 2.1.3 released 2010/01/04
|
13
33
|
|
14
34
|
- fixed issue with Rufus.is_cron_string (thanks Torsten)
|
15
35
|
- fixed issue with FlowExpression#cancel (Kenneth)
|
16
36
|
|
17
37
|
|
18
|
-
== ruote - 2.1.2
|
38
|
+
== ruote - 2.1.2 released 2010/01/03
|
19
39
|
|
20
40
|
- fixed issue when initializing engine without worker. Thanks Matt Nichols.
|
21
41
|
|
22
42
|
|
23
|
-
== ruote - 2.1.1
|
43
|
+
== ruote - 2.1.1 released 2009/12/31
|
24
44
|
|
25
|
-
== OpenWFEru - 0.9.2
|
26
|
-
== openwfe-ruby - 1.7.0
|
45
|
+
== OpenWFEru - 0.9.2 released 2007/01/26
|
46
|
+
== openwfe-ruby - 1.7.0 released 2006/05/08
|
27
47
|
|
data/CREDITS.txt
CHANGED
@@ -15,12 +15,12 @@ Kenneth Kalmer http://www.opensourcery.co.za
|
|
15
15
|
Contributors
|
16
16
|
------------
|
17
17
|
|
18
|
+
Torsten Schoenebaum - ActiveResourceParticipant and much more
|
18
19
|
Matt Nichols - http://github.com/mattnichols
|
19
20
|
Nicholas Faiz - http://github.com/biv
|
20
21
|
Chris Beer - http://github.com/cbeer
|
21
22
|
Enrico Bianco - http://github.com/enricob
|
22
23
|
Andrew Timberlake - timeout 'at'
|
23
|
-
Torsten Schoenebaum - ActiveResourceParticipant and more
|
24
24
|
Raphael Simon - error handling mechanism design and QA
|
25
25
|
Maarten Oelering - bug reports and test cases
|
26
26
|
Nick Petrella - socket listener issues and Python interactivity, dollar patch
|
data/TODO.txt
CHANGED
@@ -171,6 +171,19 @@
|
|
171
171
|
[o] maybe cancel should have a safely / redo_reply thing
|
172
172
|
[o] implement Storage#clear!(type)
|
173
173
|
[o] ruote/util/time.rb utc_to_s 'YYYY/MM/DD' --> 'YYYY-MM-DD' (regex friendly)
|
174
|
+
[x] store participant bytecode/AST ?
|
175
|
+
[o] ${r:puts(d("f:nada"))}
|
176
|
+
[o] :ruby_eval_allowed vs 'ruby_eval_allowed'
|
177
|
+
[o] check : what if a reply on a concurrence wants to save, whereas the
|
178
|
+
concurrence terminated (got removed) meanwhile ?
|
179
|
+
the reply returns true...
|
180
|
+
[o] implement StorageHistory
|
181
|
+
[x] nuke FsHistory ? keep
|
182
|
+
[o] EngineParticipant
|
183
|
+
[x] expstorage.to_dot
|
184
|
+
[o] process_status.to_dot
|
185
|
+
[o] EngineParticipant : don't wait in case of forget (reply could NEVER come !)
|
186
|
+
[x] align :forget behaviour on EngineParticipant forget... OK as it is
|
174
187
|
|
175
188
|
[ ] exp : exp (restricted form of eval ?)
|
176
189
|
[ ] exp : case (is it necessary ?)
|
@@ -197,8 +210,6 @@
|
|
197
210
|
|
198
211
|
[ ] participant dispatch thread throttling ?
|
199
212
|
|
200
|
-
[ ] expstorage.to_dot
|
201
|
-
|
202
213
|
[ ] tailcall
|
203
214
|
[ ] subprocesses participants (alias ?)
|
204
215
|
|
@@ -277,15 +288,15 @@
|
|
277
288
|
[ ] at expression ?
|
278
289
|
[ ] listen to participants/errors/tags {in|out}
|
279
290
|
|
280
|
-
[ ] implement StorageHistory
|
281
|
-
[ ] nuke FsHistory ?
|
282
|
-
|
283
291
|
[ ] remove abort_on_exception=true
|
284
292
|
|
285
|
-
[ ] check : what if a reply on a concurrence wants to save, whereas the
|
286
|
-
concurrence terminated (got removed) meanwhile ?
|
287
|
-
the reply returns true...
|
288
|
-
|
289
293
|
[ ] shell ? irb ? Shell.new(storage)
|
290
294
|
[ ] focus on fulldup or json.dup (via fulldup ?)
|
291
295
|
|
296
|
+
[ ] ruote-dm 2.1
|
297
|
+
|
298
|
+
[ ] implement pause engine
|
299
|
+
[ ] implement pause process
|
300
|
+
|
301
|
+
[ ] engine.on_error = 'participant_name'
|
302
|
+
|
@@ -9,8 +9,9 @@ require 'ruote/storage/fs_storage'
|
|
9
9
|
|
10
10
|
engine = Ruote::Engine.new(
|
11
11
|
Ruote::Worker.new(
|
12
|
-
Ruote::FsStorage.new(
|
13
|
-
|
12
|
+
Ruote::FsStorage.new(
|
13
|
+
'ruote_work',
|
14
|
+
's_logger' => [ 'ruote/log/test_logger', 'Ruote::TestLogger' ])))
|
14
15
|
|
15
16
|
# registering participants
|
16
17
|
|
data/lib/ruote.rb
CHANGED
data/lib/ruote/context.rb
CHANGED
data/lib/ruote/engine.rb
CHANGED
@@ -113,14 +113,7 @@ module Ruote
|
|
113
113
|
exp.unpersist_or_raise if exp
|
114
114
|
end
|
115
115
|
|
116
|
-
|
117
|
-
#
|
118
|
-
# done when the expression gets deleted
|
119
|
-
#
|
120
|
-
# but
|
121
|
-
#
|
122
|
-
# is there a case, 5 lines above, where there is no expression
|
123
|
-
# to delete ?
|
116
|
+
@storage.delete(err.to_h) # remove error
|
124
117
|
|
125
118
|
@storage.put_msg(action, msg) # trigger replay
|
126
119
|
end
|
@@ -141,12 +134,12 @@ module Ruote
|
|
141
134
|
#
|
142
135
|
def process (wfid)
|
143
136
|
|
144
|
-
exps = @storage.get_many('expressions',
|
137
|
+
exps = @storage.get_many('expressions', /!#{wfid}$/)
|
138
|
+
errs = @storage.get_many('errors', /!#{wfid}$/)
|
145
139
|
|
146
|
-
return nil if exps.
|
140
|
+
return nil if exps.empty? && errs.empty?
|
147
141
|
|
148
|
-
ProcessStatus.new(
|
149
|
-
@context, exps, @storage.get_many('errors', /#{wfid}$/))
|
142
|
+
ProcessStatus.new(@context, exps, errs)
|
150
143
|
end
|
151
144
|
|
152
145
|
# Returns an array of ProcessStatus instances.
|
@@ -68,6 +68,19 @@ module Ruote
|
|
68
68
|
def to_h
|
69
69
|
@h
|
70
70
|
end
|
71
|
+
|
72
|
+
protected
|
73
|
+
|
74
|
+
def to_dot (opts)
|
75
|
+
|
76
|
+
i = fei.to_storage_id
|
77
|
+
label = "error : #{message.gsub(/"/, "'")}"
|
78
|
+
|
79
|
+
[
|
80
|
+
"\"err_#{i}\" [ label = \"#{label}\" ];",
|
81
|
+
"\"err_#{i}\" -> \"#{i}\" [ style = \"dotted\" ];"
|
82
|
+
]
|
83
|
+
end
|
71
84
|
end
|
72
85
|
end
|
73
86
|
|
@@ -47,9 +47,11 @@ module Ruote
|
|
47
47
|
|
48
48
|
@expressions = expressions.collect { |e|
|
49
49
|
Ruote::Exp::FlowExpression.from_h(context, e) }
|
50
|
+
@expressions.sort! { |a, b| a.fei.expid <=> b.fei.expid }
|
50
51
|
|
51
52
|
@errors = errors.collect { |e|
|
52
53
|
ProcessError.new(e) }
|
54
|
+
@errors.sort! { |a, b| a.fei.expid <=> b.fei.expid }
|
53
55
|
end
|
54
56
|
|
55
57
|
# Returns the expression at the root of the process instance.
|
@@ -100,7 +102,11 @@ module Ruote
|
|
100
102
|
#
|
101
103
|
def wfid
|
102
104
|
|
103
|
-
|
105
|
+
begin
|
106
|
+
root_expression.fei.wfid
|
107
|
+
rescue
|
108
|
+
@errors.first.fei.wfid
|
109
|
+
end
|
104
110
|
end
|
105
111
|
|
106
112
|
def definition_name
|
@@ -144,12 +150,23 @@ module Ruote
|
|
144
150
|
end
|
145
151
|
s << " errors : #{@errors.size}\n"
|
146
152
|
@errors.each do |e|
|
153
|
+
s << " #{e.fei.to_storage_id} :\n" if e.fei
|
147
154
|
s << " #{e.inspect}\n"
|
148
155
|
end
|
149
156
|
|
150
157
|
s
|
151
158
|
end
|
152
159
|
|
160
|
+
def to_dot (opts={})
|
161
|
+
|
162
|
+
s = [ "digraph \"process wfid #{wfid}\" {" ]
|
163
|
+
@expressions.each { |e| s.push(*e.send(:to_dot, opts)) }
|
164
|
+
@errors.each { |e| s.push(*e.send(:to_dot, opts)) }
|
165
|
+
s << "}"
|
166
|
+
|
167
|
+
s.join("\n")
|
168
|
+
end
|
169
|
+
|
153
170
|
def to_h
|
154
171
|
|
155
172
|
h = {}
|
data/lib/ruote/exp/condition.rb
CHANGED
@@ -162,14 +162,20 @@ module Ruote::Exp
|
|
162
162
|
|
163
163
|
def cancel (flavour)
|
164
164
|
|
165
|
-
# TODO : if flavour is 'kill', why not not trigger participant.cancel ?
|
166
|
-
|
167
165
|
participant = @context.plist.lookup(h.participant_name)
|
168
|
-
participant.cancel(fei, flavour)
|
169
|
-
# TODO should this be threaded ?
|
170
|
-
# TODO should errors be intercepted here ?
|
171
166
|
|
172
|
-
|
167
|
+
r = if flavour == 'kill'
|
168
|
+
begin
|
169
|
+
participant.cancel(fei, 'kill')
|
170
|
+
rescue Exception => e
|
171
|
+
# intercept anything
|
172
|
+
nil
|
173
|
+
end
|
174
|
+
else
|
175
|
+
participant.cancel(fei, flavour)
|
176
|
+
end
|
177
|
+
|
178
|
+
reply_to_parent(h.applied_workitem) if r != false
|
173
179
|
end
|
174
180
|
|
175
181
|
def reply_to_parent (workitem)
|
@@ -22,6 +22,8 @@
|
|
22
22
|
# Made in Japan.
|
23
23
|
#++
|
24
24
|
|
25
|
+
require 'ruote/subprocess'
|
26
|
+
|
25
27
|
|
26
28
|
module Ruote::Exp
|
27
29
|
|
@@ -116,7 +118,7 @@ module Ruote::Exp
|
|
116
118
|
#
|
117
119
|
# subprocess :ref => 'http://pdefs.example.org/account/def1.xml'
|
118
120
|
#
|
119
|
-
# Remember that the
|
121
|
+
# Remember that the 'remote_definition_allowed' option of the engine has
|
120
122
|
# to be set to true for the latter to work, else the engine will refuse
|
121
123
|
# to load definitions over HTTP.
|
122
124
|
#
|
@@ -155,7 +157,7 @@ module Ruote::Exp
|
|
155
157
|
|
156
158
|
raise "no subprocess referred in #{tree}" unless ref
|
157
159
|
|
158
|
-
pos, subtree = lookup_subprocess(ref)
|
160
|
+
pos, subtree = Ruote.lookup_subprocess(self, ref)
|
159
161
|
|
160
162
|
vars = compile_atts
|
161
163
|
vars.merge!('tree' => tree_children.first)
|
@@ -163,41 +165,6 @@ module Ruote::Exp
|
|
163
165
|
|
164
166
|
launch_sub(pos, subtree, :variables => vars)
|
165
167
|
end
|
166
|
-
|
167
|
-
protected
|
168
|
-
|
169
|
-
def lookup_subprocess (ref)
|
170
|
-
|
171
|
-
val = lookup_variable(ref)
|
172
|
-
|
173
|
-
# a classical subprocess stored in a variable ?
|
174
|
-
|
175
|
-
return [ '0', val ] if is_tree?(val)
|
176
|
-
return val if is_pos_tree?(val)
|
177
|
-
|
178
|
-
# maybe subprocess :ref => 'uri'
|
179
|
-
|
180
|
-
subtree = @context.parser.parse(ref) rescue nil
|
181
|
-
|
182
|
-
_, subtree = Ruote::Exp::DefineExpression.reorganize(subtree) \
|
183
|
-
if subtree && Ruote::Exp::DefineExpression.is_definition?(subtree)
|
184
|
-
|
185
|
-
return [ '0', subtree ] if is_tree?(subtree)
|
186
|
-
|
187
|
-
# no luck ...
|
188
|
-
|
189
|
-
raise "no subprocess named '#{ref}' found"
|
190
|
-
end
|
191
|
-
|
192
|
-
def is_tree? (a)
|
193
|
-
|
194
|
-
a.is_a?(Array) && a[1].is_a?(Hash) && a.size == 3
|
195
|
-
end
|
196
|
-
|
197
|
-
def is_pos_tree? (a)
|
198
|
-
|
199
|
-
a.is_a?(Array) && a.size == 2 && a[0].is_a?(String) && is_tree?(a[1])
|
200
|
-
end
|
201
168
|
end
|
202
169
|
end
|
203
170
|
|
data/lib/ruote/exp/fe_when.rb
CHANGED
@@ -133,7 +133,7 @@ module Ruote::Exp
|
|
133
133
|
#
|
134
134
|
# == ${ruby:'hello'}
|
135
135
|
#
|
136
|
-
# Remember that, if the engine's
|
136
|
+
# Remember that, if the engine's 'ruby_eval_allowed' is set to true, the
|
137
137
|
# condition may contain Ruby code.
|
138
138
|
#
|
139
139
|
# _when '${r:"hell" + "o"} == hello'
|
@@ -145,6 +145,21 @@ module Ruote::Exp
|
|
145
145
|
|
146
146
|
def self.do_action (context, msg)
|
147
147
|
|
148
|
+
fei = msg['fei']
|
149
|
+
action = msg['action']
|
150
|
+
|
151
|
+
if action == 'reply' && fei['engine_id'] != context.engine_id
|
152
|
+
|
153
|
+
ep = context.plist.lookup(fei['engine_id'])
|
154
|
+
|
155
|
+
raise(
|
156
|
+
"no EngineParticipant found under name '#{fei['engine_id']}'"
|
157
|
+
) unless ep
|
158
|
+
|
159
|
+
ep.reply(fei, msg['workitem'])
|
160
|
+
return
|
161
|
+
end
|
162
|
+
|
148
163
|
fexp = nil
|
149
164
|
|
150
165
|
3.times do
|
@@ -154,7 +169,7 @@ module Ruote::Exp
|
|
154
169
|
end
|
155
170
|
# this retry system is only useful with ruote-couch
|
156
171
|
|
157
|
-
fexp.send("do_#{
|
172
|
+
fexp.send("do_#{action}", msg) if fexp
|
158
173
|
end
|
159
174
|
|
160
175
|
def do_apply
|
@@ -184,15 +199,6 @@ module Ruote::Exp
|
|
184
199
|
|
185
200
|
def reply_to_parent (workitem, delete=true)
|
186
201
|
|
187
|
-
#if delete && h.state.nil?
|
188
|
-
# p @msg
|
189
|
-
# if @msg && @msg['action'] == 'reply'
|
190
|
-
# do_unpersist || return
|
191
|
-
# else
|
192
|
-
# unpersist_or_raise
|
193
|
-
# end
|
194
|
-
#end
|
195
|
-
|
196
202
|
if h.tagname
|
197
203
|
|
198
204
|
unset_variable(h.tagname)
|
@@ -291,16 +297,16 @@ module Ruote::Exp
|
|
291
297
|
#
|
292
298
|
def do_cancel (msg)
|
293
299
|
|
294
|
-
return if h.state == 'cancelling'
|
295
|
-
# cancel on cancel gets discarded
|
296
|
-
|
297
|
-
@msg = Ruote.fulldup(msg)
|
298
|
-
|
299
300
|
flavour = msg['flavour']
|
300
301
|
|
302
|
+
return if h.state == 'cancelling' && flavour != 'kill'
|
303
|
+
# cancel on cancel gets discarded
|
304
|
+
|
301
305
|
return if h.state == 'failed' && flavour == 'timeout'
|
302
306
|
# do not timeout expressions that are "in error" (failed)
|
303
307
|
|
308
|
+
@msg = Ruote.fulldup(msg)
|
309
|
+
|
304
310
|
h.state = case flavour
|
305
311
|
when 'kill' then 'dying'
|
306
312
|
when 'timeout' then 'timing_out'
|
@@ -490,8 +496,46 @@ module Ruote::Exp
|
|
490
496
|
tree[2]
|
491
497
|
end
|
492
498
|
|
499
|
+
# Generates a sub_wfid, without hitting storage.
|
500
|
+
#
|
501
|
+
# There's a better implementation for sure...
|
502
|
+
#
|
503
|
+
def get_next_sub_wfid
|
504
|
+
|
505
|
+
i = [
|
506
|
+
$$, Time.now.to_f.to_s, self.hash.to_s, @h['fei'].inspect
|
507
|
+
].join('-').hash
|
508
|
+
|
509
|
+
(i < 0 ? "1#{i * -1}" : "0#{i}").to_s
|
510
|
+
end
|
511
|
+
|
493
512
|
protected
|
494
513
|
|
514
|
+
def to_dot (opts)
|
515
|
+
|
516
|
+
i = fei()
|
517
|
+
|
518
|
+
label = "#{[ i.wfid, i.sub_wfid, i.expid].join(" ")} #{tree.first}"
|
519
|
+
label += " (#{h.state})" if h.state
|
520
|
+
|
521
|
+
a = []
|
522
|
+
a << "\"#{i.to_storage_id}\" [ label=\"#{label}\" ];"
|
523
|
+
|
524
|
+
# parent
|
525
|
+
|
526
|
+
if h.parent_id
|
527
|
+
a << "\"#{i.to_storage_id}\" -> \"#{parent_id.to_storage_id}\";"
|
528
|
+
end
|
529
|
+
|
530
|
+
# children
|
531
|
+
|
532
|
+
h.children.each do |cfei|
|
533
|
+
a << "\"#{i.to_storage_id}\" -> \"#{Ruote.to_storage_id(cfei)}\";"
|
534
|
+
end
|
535
|
+
|
536
|
+
a
|
537
|
+
end
|
538
|
+
|
495
539
|
def pre_apply_child (child_index, workitem, forget)
|
496
540
|
|
497
541
|
child_fei = h.fei.merge('expid' => "#{h.fei['expid']}_#{child_index}")
|
@@ -519,19 +563,6 @@ module Ruote::Exp
|
|
519
563
|
@context.storage.put_msg('apply', msg)
|
520
564
|
end
|
521
565
|
|
522
|
-
# Generates a sub_wfid, without hitting storage.
|
523
|
-
#
|
524
|
-
# There's a better implementation for sure...
|
525
|
-
#
|
526
|
-
def get_next_sub_wfid
|
527
|
-
|
528
|
-
i = [
|
529
|
-
$$, Time.now.to_f.to_s, self.hash.to_s, @h['fei'].inspect
|
530
|
-
].join('-').hash
|
531
|
-
|
532
|
-
(i < 0 ? "1#{i * -1}" : "0#{i}").to_s
|
533
|
-
end
|
534
|
-
|
535
566
|
def register_child (fei)
|
536
567
|
|
537
568
|
h.children << fei
|