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.
Files changed (81) hide show
  1. data/CHANGELOG.txt +32 -0
  2. data/CREDITS.txt +3 -0
  3. data/Rakefile +4 -4
  4. data/TODO.txt +55 -11
  5. data/examples/barley.rb +2 -1
  6. data/examples/flickr_report.rb +5 -6
  7. data/examples/web_first_page.rb +11 -0
  8. data/lib/ruote/context.rb +36 -13
  9. data/lib/ruote/engine.rb +88 -56
  10. data/lib/ruote/engine/process_error.rb +13 -0
  11. data/lib/ruote/engine/process_status.rb +33 -1
  12. data/lib/ruote/error_handler.rb +122 -0
  13. data/lib/ruote/evt/tracker.rb +27 -10
  14. data/lib/ruote/exp/fe_apply.rb +69 -0
  15. data/lib/ruote/exp/fe_participant.rb +33 -5
  16. data/lib/ruote/exp/flowexpression.rb +37 -5
  17. data/lib/ruote/exp/ro_persist.rb +8 -4
  18. data/lib/ruote/exp/ro_variables.rb +2 -2
  19. data/lib/ruote/fei.rb +59 -7
  20. data/lib/ruote/log/storage_history.rb +2 -0
  21. data/lib/ruote/log/test_logger.rb +28 -19
  22. data/lib/ruote/log/wait_logger.rb +4 -2
  23. data/lib/ruote/parser.rb +2 -1
  24. data/lib/ruote/part/dispatch_pool.rb +10 -10
  25. data/lib/ruote/part/engine_participant.rb +2 -2
  26. data/lib/ruote/part/local_participant.rb +99 -7
  27. data/lib/ruote/part/participant_list.rb +18 -7
  28. data/lib/ruote/part/storage_participant.rb +9 -6
  29. data/lib/ruote/receiver/base.rb +109 -10
  30. data/lib/ruote/storage/base.rb +118 -41
  31. data/lib/ruote/storage/fs_storage.rb +1 -0
  32. data/lib/ruote/storage/hash_storage.rb +2 -1
  33. data/lib/ruote/util/lookup.rb +22 -2
  34. data/lib/ruote/util/misc.rb +5 -5
  35. data/lib/ruote/version.rb +1 -1
  36. data/lib/ruote/worker.rb +50 -63
  37. data/lib/ruote/workitem.rb +64 -0
  38. data/ruote.gemspec +17 -12
  39. data/test/functional/base.rb +3 -1
  40. data/test/functional/concurrent_base.rb +35 -29
  41. data/test/functional/crunner.sh +19 -0
  42. data/test/functional/ct_0_concurrence.rb +17 -30
  43. data/test/functional/ct_1_iterator.rb +20 -17
  44. data/test/functional/ct_2_cancel.rb +32 -25
  45. data/test/functional/eft_12_listen.rb +2 -1
  46. data/test/functional/eft_23_apply.rb +23 -0
  47. data/test/functional/eft_3_participant.rb +27 -0
  48. data/test/functional/ft_11_recursion.rb +1 -1
  49. data/test/functional/ft_13_variables.rb +22 -0
  50. data/test/functional/ft_14_re_apply.rb +3 -0
  51. data/test/functional/ft_15_timeout.rb +1 -0
  52. data/test/functional/ft_20_storage_participant.rb +20 -2
  53. data/test/functional/ft_21_forget.rb +30 -0
  54. data/test/functional/ft_22_process_definitions.rb +2 -1
  55. data/test/functional/ft_24_block_participants.rb +1 -1
  56. data/test/functional/ft_25_receiver.rb +83 -1
  57. data/test/functional/ft_26_participant_timeout.rb +1 -1
  58. data/test/functional/ft_2_errors.rb +2 -5
  59. data/test/functional/ft_30_smtp_participant.rb +47 -45
  60. data/test/functional/ft_36_storage_history.rb +4 -4
  61. data/test/functional/ft_37_engine_participant.rb +14 -10
  62. data/test/functional/ft_38_participant_more.rb +178 -0
  63. data/test/functional/ft_39_wait_for.rb +100 -0
  64. data/test/functional/ft_40_participant_on_reply.rb +87 -0
  65. data/test/functional/ft_41_participants.rb +65 -0
  66. data/test/functional/ft_42_storage_copy.rb +67 -0
  67. data/test/functional/ft_5_on_error.rb +103 -0
  68. data/test/functional/ft_9_subprocesses.rb +2 -1
  69. data/test/functional/storage_helper.rb +5 -1
  70. data/test/functional/test.rb +4 -1
  71. data/test/functional/vertical.rb +46 -0
  72. data/test/unit/storage.rb +17 -1
  73. data/test/unit/storages.rb +27 -7
  74. data/test/unit/ut_11_lookup.rb +36 -0
  75. data/test/unit/ut_16_parser.rb +43 -0
  76. data/test/unit/ut_1_fei.rb +28 -1
  77. data/test/unit/ut_7_workitem.rb +23 -0
  78. metadata +67 -105
  79. data/lib/ruote/log/fs_history.rb +0 -182
  80. data/test/functional/ft_32_fs_history.rb +0 -188
  81. data/test/mpc_test.rb +0 -29
@@ -38,6 +38,7 @@ require 'rufus/cloche'
38
38
 
39
39
  module Ruote
40
40
 
41
+ #
41
42
  # A basic FS-bound ruote storage. Leverages rufus-cloche
42
43
  # (http://github.com/jmettraux/rufus-cloche).
43
44
  #
@@ -57,7 +57,8 @@ module Ruote
57
57
 
58
58
  pre = get(doc['type'], doc['_id'])
59
59
 
60
- if pre && ( ! opts[:update_rev]) && pre['_rev'] != doc['_rev']
60
+ #if pre && ( ! opts[:update_rev]) && pre['_rev'] != doc['_rev']
61
+ if pre && pre['_rev'] != doc['_rev']
61
62
  return pre
62
63
  end
63
64
 
@@ -34,8 +34,9 @@ module Ruote
34
34
  key, rest = pop_key(key)
35
35
  value = flookup(collection, key)
36
36
 
37
+ return [ key, collection ] if container_lookup && rest.size == 0
37
38
  return [ rest.first, value ] if container_lookup && rest.size == 1
38
- return value if rest.empty?
39
+ return value if rest.size == 0
39
40
  return nil if value == nil
40
41
 
41
42
  lookup(value, rest)
@@ -59,7 +60,26 @@ module Ruote
59
60
  end
60
61
  end
61
62
 
62
- protected
63
+ # h = { 'customer' => { 'name' => 'alpha', 'rank' => '1st' } }
64
+ # r = Ruote.unset(h, 'customer.rank')
65
+ #
66
+ # h # => { 'customer' => { 'name' => 'alpha' } }
67
+ # r # => '1st'
68
+ #
69
+ def Ruote.unset (collection, key)
70
+
71
+ k, c = lookup(collection, key, true)
72
+
73
+ return collection.delete(key) unless c
74
+
75
+ if c.is_a?(Array)
76
+ c.delete_at(Integer(k)) rescue nil
77
+ else
78
+ c.delete(k)
79
+ end
80
+ end
81
+
82
+ protected # well...
63
83
 
64
84
  def Ruote.pop_key (key)
65
85
 
@@ -58,11 +58,11 @@ module Ruote
58
58
  rescue TypeError => te
59
59
  end
60
60
 
61
- if object.is_a?(REXML::Element)
62
- d = REXML::Document.new object.to_s
63
- return d if object.kind_of?(REXML::Document)
64
- return d.root
65
- end
61
+ #if object.is_a?(REXML::Element)
62
+ # d = REXML::Document.new object.to_s
63
+ # return d if object.kind_of?(REXML::Document)
64
+ # return d.root
65
+ #end
66
66
  # avoiding "TypeError: singleton can't be dumped"
67
67
 
68
68
  o = object.class.allocate
@@ -23,6 +23,6 @@
23
23
  #++
24
24
 
25
25
  module Ruote
26
- VERSION = '2.1.9'
26
+ VERSION = '2.1.10'
27
27
  end
28
28
 
@@ -27,9 +27,14 @@ require 'ruote/fei'
27
27
 
28
28
  module Ruote
29
29
 
30
+ #
31
+ # Workers fetch 'msgs' and 'schedules' from the storage and process them.
32
+ #
33
+ # Read more at http://ruote.rubyforge.org/configuration.html
34
+ #
30
35
  class Worker
31
36
 
32
- EXP_ACTIONS = %w[ reply cancel fail receive ]
37
+ EXP_ACTIONS = %w[ reply cancel fail receive dispatched ]
33
38
  # 'apply' is comprised in 'launch'
34
39
  # 'receive' is a ParticipantExpression alias for 'reply'
35
40
 
@@ -43,10 +48,12 @@ module Ruote
43
48
 
44
49
  def initialize (storage)
45
50
 
46
- @storage = storage
47
-
48
51
  @subscribers = []
49
- @context = Ruote::Context.new(@storage, self)
52
+ # must be ready before the storage is created
53
+ # services like Logger to subscribe to the worker
54
+
55
+ @storage = storage
56
+ @context = Ruote::Context.new(storage, self)
50
57
 
51
58
  @last_time = Time.at(0.0).utc # 1970...
52
59
 
@@ -54,7 +61,7 @@ module Ruote
54
61
  @run_thread = nil
55
62
 
56
63
  @msgs = []
57
- @sleep_time = 0.001
64
+ @sleep_time = 0.000
58
65
  end
59
66
 
60
67
  def run
@@ -66,7 +73,7 @@ module Ruote
66
73
 
67
74
  def run_in_thread
68
75
 
69
- #Thread.abort_on_exception = true
76
+ Thread.abort_on_exception = true
70
77
  # TODO : remove me at some point
71
78
 
72
79
  @running = true
@@ -82,62 +89,40 @@ module Ruote
82
89
  def shutdown
83
90
 
84
91
  @running = false
85
- @run_thread.join if @run_thread
92
+
93
+ return unless @run_thread
94
+
95
+ begin
96
+ @run_thread.join
97
+ rescue Exception => e
98
+ end
86
99
  end
87
100
 
88
- # This method is public, since it's used by the DispatchPool when
89
- # reporting an error that occurred in the dispatch/consume thread of
90
- # a participant.
101
+ # Returns true if the engine system is inactive, ie if all the process
102
+ # instances are terminated or are stuck in an error.
91
103
  #
92
- def handle_exception (msg, fexp, ex)
93
-
94
- wfid = msg['wfid'] || (msg['fei']['wfid'] rescue nil)
95
- fei = msg['fei'] || (fexp.h.fei rescue nil)
96
-
97
- # debug only
98
-
99
- if ARGV.include?('-d')
100
-
101
- puts "\n== worker intercepted error =="
102
- puts
103
- p ex
104
- ex.backtrace[0, 10].each { |l| puts l }
105
- puts "..."
106
- puts
107
- puts "-- msg --"
108
- msg.keys.sort.each { |k|
109
- puts " #{k.inspect} =>\n#{msg[k].inspect}"
110
- }
111
- puts "-- . --"
112
- puts
113
- end
104
+ # NOTE : for now, if a branch of a process is in errors while another is
105
+ # still running, this methods will still consider the process instance
106
+ # as inactive (and it will return true if all the processes are considered
107
+ # inactive).
108
+ #
109
+ def inactive?
114
110
 
115
- # on_error ?
111
+ # the cheaper tests first
116
112
 
117
- if not(fexp) && fei
118
- fexp = Ruote::Exp::FlowExpression.fetch(@context, fei)
119
- end
113
+ return false if @msgs.size > 0
114
+ return false unless @context.storage.empty?('schedules')
115
+ return false unless @context.storage.empty?('msgs')
120
116
 
121
- return if fexp && fexp.handle_on_error
117
+ wfids = @context.storage.get_many('expressions').collect { |exp|
118
+ exp['fei']['wfid']
119
+ }.sort.uniq
122
120
 
123
- # emit 'msg'
121
+ error_wfids = @context.storage.get_many('errors').collect { |err|
122
+ err['fei']['wfid']
123
+ }.sort.uniq
124
124
 
125
- @storage.put_msg(
126
- 'error_intercepted',
127
- 'message' => ex.inspect,
128
- 'wfid' => wfid,
129
- 'msg' => msg)
130
-
131
- # fill error in the error journal
132
-
133
- @storage.put(
134
- 'type' => 'errors',
135
- '_id' => "err_#{Ruote.to_storage_id(fei)}",
136
- 'message' => ex.inspect,
137
- 'trace' => ex.backtrace.join("\n"),
138
- 'fei' => fei,
139
- 'msg' => msg
140
- ) if fei
125
+ (wfids - error_wfids == [])
141
126
  end
142
127
 
143
128
  protected
@@ -186,14 +171,14 @@ module Ruote
186
171
  break if Time.now.utc - @last_time >= 0.8
187
172
  end
188
173
 
189
- #puts processed.to_s
174
+ #p processed
190
175
 
191
176
  if processed == 0
192
177
  @sleep_time += 0.001
193
178
  @sleep_time = 0.499 if @sleep_time > 0.499
194
179
  sleep(@sleep_time)
195
180
  else
196
- @sleep_time = 0.001
181
+ @sleep_time = 0.000
197
182
  end
198
183
  end
199
184
 
@@ -201,7 +186,7 @@ module Ruote
201
186
 
202
187
  msg = Ruote.fulldup(schedule['msg'])
203
188
 
204
- return false unless @storage.delete(schedule).nil?
189
+ return false unless @storage.reserve(schedule)
205
190
 
206
191
  @storage.put_msg(msg.delete('action'), msg)
207
192
 
@@ -212,7 +197,7 @@ module Ruote
212
197
 
213
198
  return false if cannot_handle(msg)
214
199
 
215
- return false unless @storage.delete(msg).nil?
200
+ return false unless @storage.reserve(msg)
216
201
 
217
202
  begin
218
203
 
@@ -242,9 +227,9 @@ module Ruote
242
227
 
243
228
  notify(msg)
244
229
 
245
- rescue Exception => ex
230
+ rescue Exception => exception
246
231
 
247
- handle_exception(msg, nil, ex)
232
+ @context.error_handler.msg_handle(msg, exception)
248
233
  end
249
234
 
250
235
  true
@@ -260,6 +245,10 @@ module Ruote
260
245
  end
261
246
  end
262
247
 
248
+ # Should always return false. Except when the message is a 'dispatch'
249
+ # and it's for a participant only available to an 'engine_worker'
250
+ # (block participants, stateful participants)
251
+ #
263
252
  def cannot_handle (msg)
264
253
 
265
254
  return false if msg['action'] != 'dispatch'
@@ -322,6 +311,7 @@ module Ruote
322
311
  raise_unknown_expression_error(exp_hash) unless exp_class
323
312
 
324
313
  exp = exp_class.new(@context, exp_hash.merge!('original_tree' => tree))
314
+
325
315
  exp.initial_persist
326
316
  exp.do_apply
327
317
  end
@@ -357,11 +347,8 @@ module Ruote
357
347
  tree[1]['original_ref'] = tree[0] if key != tree[0]
358
348
 
359
349
  if sub
360
-
361
350
  [ Ruote::Exp::SubprocessExpression, [ 'subprocess', *tree[1..2] ] ]
362
-
363
351
  else
364
-
365
352
  [ Ruote::Exp::ParticipantExpression, [ 'participant', *tree[1..2] ] ]
366
353
  end
367
354
  else
@@ -43,13 +43,47 @@ module Ruote
43
43
 
44
44
  @h = h
45
45
  class << @h; include Ruote::HashDot; end
46
+
47
+ #class << @h['fields']
48
+ # alias_method :__get, :[]
49
+ # alias_method :__set, :[]=
50
+ # def [] (key)
51
+ # __get(key.to_s)
52
+ # end
53
+ # def []= (key, value)
54
+ # __set(key.to_s, value)
55
+ # end
56
+ #end
57
+ # indifferent access, not activated for now
46
58
  end
47
59
 
60
+ # Returns the underlying Hash instance.
61
+ #
48
62
  def to_h
49
63
 
50
64
  @h
51
65
  end
52
66
 
67
+ # Returns the String id for this workitem (something like
68
+ # "0_0!!20100507-wagamama").
69
+ #
70
+ # It's in fact a shortcut for
71
+ #
72
+ # Ruote::FlowExpressionId.to_storage_id(h.fei)
73
+ #
74
+ def sid
75
+
76
+ Ruote::FlowExpressionId.to_storage_id(h.fei)
77
+ end
78
+
79
+ # Returns the "workflow instance id" (unique process instance id) of
80
+ # the process instance which issued this workitem.
81
+ #
82
+ def wfid
83
+
84
+ h.fei['wfid']
85
+ end
86
+
53
87
  # Returns a Ruote::FlowExpressionId instance.
54
88
  #
55
89
  def fei
@@ -170,6 +204,36 @@ module Ruote
170
204
 
171
205
  Ruote.set(@h['fields'], key, value)
172
206
  end
207
+
208
+ # Shortcut for wi.fields['__timed_out__']
209
+ #
210
+ def timed_out
211
+
212
+ @h['fields']['__timed_out__']
213
+ end
214
+
215
+ # Shortcut for wi.fields['__error__']
216
+ #
217
+ def error
218
+
219
+ @h['fields']['__error__']
220
+ end
221
+
222
+ # Shortcut for wi.fields['params']
223
+ #
224
+ # When a participant is invoked like in
225
+ #
226
+ # participant :ref => 'toto', :task => 'x"
227
+ #
228
+ # then
229
+ #
230
+ # p workitem.params
231
+ # # => { 'ref' => 'toto', 'task' => 'x' }
232
+ #
233
+ def params
234
+
235
+ @h['fields']['params']
236
+ end
173
237
  end
174
238
  end
175
239
 
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{ruote}
8
- s.version = "2.1.9"
8
+ s.version = "2.1.10"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["John Mettraux", "Kenneth Kalmer", "Torsten Schoenebaum"]
12
- s.date = %q{2010-03-23}
12
+ s.date = %q{2010-06-15}
13
13
  s.description = %q{
14
14
  ruote is an open source ruby workflow engine.
15
15
  }
@@ -35,6 +35,7 @@ ruote is an open source ruby workflow engine.
35
35
  "lib/ruote/engine.rb",
36
36
  "lib/ruote/engine/process_error.rb",
37
37
  "lib/ruote/engine/process_status.rb",
38
+ "lib/ruote/error_handler.rb",
38
39
  "lib/ruote/evt/tracker.rb",
39
40
  "lib/ruote/exp/command.rb",
40
41
  "lib/ruote/exp/commanded.rb",
@@ -79,7 +80,6 @@ ruote is an open source ruby workflow engine.
79
80
  "lib/ruote/fei.rb",
80
81
  "lib/ruote/id/mnemo_wfid_generator.rb",
81
82
  "lib/ruote/id/wfid_generator.rb",
82
- "lib/ruote/log/fs_history.rb",
83
83
  "lib/ruote/log/storage_history.rb",
84
84
  "lib/ruote/log/test_logger.rb",
85
85
  "lib/ruote/log/wait_logger.rb",
@@ -131,6 +131,7 @@ ruote is an open source ruby workflow engine.
131
131
  "test/functional/base.rb",
132
132
  "test/functional/concurrent_base.rb",
133
133
  "test/functional/crunner.rb",
134
+ "test/functional/crunner.sh",
134
135
  "test/functional/ct_0_concurrence.rb",
135
136
  "test/functional/ct_1_iterator.rb",
136
137
  "test/functional/ct_2_cancel.rb",
@@ -189,13 +190,17 @@ ruote is an open source ruby workflow engine.
189
190
  "test/functional/ft_2_errors.rb",
190
191
  "test/functional/ft_30_smtp_participant.rb",
191
192
  "test/functional/ft_31_part_blocking.rb",
192
- "test/functional/ft_32_fs_history.rb",
193
193
  "test/functional/ft_33_participant_subprocess_priority.rb",
194
194
  "test/functional/ft_34_cursor_rewind.rb",
195
195
  "test/functional/ft_35_add_service.rb",
196
196
  "test/functional/ft_36_storage_history.rb",
197
197
  "test/functional/ft_37_engine_participant.rb",
198
+ "test/functional/ft_38_participant_more.rb",
199
+ "test/functional/ft_39_wait_for.rb",
198
200
  "test/functional/ft_3_participant_registration.rb",
201
+ "test/functional/ft_40_participant_on_reply.rb",
202
+ "test/functional/ft_41_participants.rb",
203
+ "test/functional/ft_42_storage_copy.rb",
199
204
  "test/functional/ft_4_cancel.rb",
200
205
  "test/functional/ft_5_on_error.rb",
201
206
  "test/functional/ft_6_on_cancel.rb",
@@ -212,7 +217,7 @@ ruote is an open source ruby workflow engine.
212
217
  "test/functional/rtest.rb",
213
218
  "test/functional/storage_helper.rb",
214
219
  "test/functional/test.rb",
215
- "test/mpc_test.rb",
220
+ "test/functional/vertical.rb",
216
221
  "test/path_helper.rb",
217
222
  "test/pdef.xml",
218
223
  "test/test.rb",
@@ -243,7 +248,7 @@ ruote is an open source ruby workflow engine.
243
248
  s.rdoc_options = ["--charset=UTF-8"]
244
249
  s.require_paths = ["lib"]
245
250
  s.rubyforge_project = %q{ruote}
246
- s.rubygems_version = %q{1.3.6}
251
+ s.rubygems_version = %q{1.3.5}
247
252
  s.summary = %q{an open source ruby workflow engine}
248
253
  s.test_files = [
249
254
  "test/test.rb"
@@ -254,8 +259,8 @@ ruote is an open source ruby workflow engine.
254
259
  s.specification_version = 3
255
260
 
256
261
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
257
- s.add_runtime_dependency(%q<rufus-json>, [">= 0.2.0"])
258
- s.add_runtime_dependency(%q<rufus-cloche>, [">= 0.1.16"])
262
+ s.add_runtime_dependency(%q<rufus-json>, [">= 0.2.2"])
263
+ s.add_runtime_dependency(%q<rufus-cloche>, [">= 0.1.17"])
259
264
  s.add_runtime_dependency(%q<rufus-dollar>, [">= 0"])
260
265
  s.add_runtime_dependency(%q<rufus-lru>, [">= 0"])
261
266
  s.add_runtime_dependency(%q<rufus-mnemo>, [">= 1.1.0"])
@@ -268,8 +273,8 @@ ruote is an open source ruby workflow engine.
268
273
  s.add_development_dependency(%q<mailtrap>, [">= 0"])
269
274
  s.add_development_dependency(%q<jeweler>, [">= 0"])
270
275
  else
271
- s.add_dependency(%q<rufus-json>, [">= 0.2.0"])
272
- s.add_dependency(%q<rufus-cloche>, [">= 0.1.16"])
276
+ s.add_dependency(%q<rufus-json>, [">= 0.2.2"])
277
+ s.add_dependency(%q<rufus-cloche>, [">= 0.1.17"])
273
278
  s.add_dependency(%q<rufus-dollar>, [">= 0"])
274
279
  s.add_dependency(%q<rufus-lru>, [">= 0"])
275
280
  s.add_dependency(%q<rufus-mnemo>, [">= 1.1.0"])
@@ -283,8 +288,8 @@ ruote is an open source ruby workflow engine.
283
288
  s.add_dependency(%q<jeweler>, [">= 0"])
284
289
  end
285
290
  else
286
- s.add_dependency(%q<rufus-json>, [">= 0.2.0"])
287
- s.add_dependency(%q<rufus-cloche>, [">= 0.1.16"])
291
+ s.add_dependency(%q<rufus-json>, [">= 0.2.2"])
292
+ s.add_dependency(%q<rufus-cloche>, [">= 0.1.17"])
288
293
  s.add_dependency(%q<rufus-dollar>, [">= 0"])
289
294
  s.add_dependency(%q<rufus-lru>, [">= 0"])
290
295
  s.add_dependency(%q<rufus-mnemo>, [">= 1.1.0"])