ruote 2.1.10 → 2.1.11

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 (94) hide show
  1. data/CHANGELOG.txt +51 -1
  2. data/CREDITS.txt +9 -0
  3. data/README.rdoc +13 -0
  4. data/Rakefile +50 -21
  5. data/TODO.txt +42 -4
  6. data/examples/pong.rb +37 -0
  7. data/lib/ruote/context.rb +19 -9
  8. data/lib/ruote/engine/process_error.rb +10 -0
  9. data/lib/ruote/engine/process_status.rb +140 -41
  10. data/lib/ruote/engine.rb +394 -27
  11. data/lib/ruote/exp/command.rb +2 -0
  12. data/lib/ruote/exp/fe_concurrence.rb +8 -0
  13. data/lib/ruote/exp/fe_concurrent_iterator.rb +3 -0
  14. data/lib/ruote/exp/fe_cursor.rb +48 -4
  15. data/lib/ruote/exp/fe_iterator.rb +40 -0
  16. data/lib/ruote/exp/fe_listen.rb +3 -3
  17. data/lib/ruote/exp/fe_participant.rb +30 -12
  18. data/lib/ruote/exp/fe_ref.rb +126 -0
  19. data/lib/ruote/exp/fe_subprocess.rb +20 -1
  20. data/lib/ruote/exp/fe_wait.rb +4 -1
  21. data/lib/ruote/exp/fe_when.rb +7 -10
  22. data/lib/ruote/exp/flowexpression.rb +23 -12
  23. data/lib/ruote/exp/ro_attributes.rb +5 -8
  24. data/lib/ruote/exp/ro_variables.rb +4 -2
  25. data/lib/ruote/fei.rb +2 -0
  26. data/lib/ruote/id/wfid_generator.rb +1 -1
  27. data/lib/ruote/log/pretty.rb +137 -0
  28. data/lib/ruote/log/storage_history.rb +1 -1
  29. data/lib/ruote/log/test_logger.rb +51 -126
  30. data/lib/ruote/log/wait_logger.rb +8 -13
  31. data/lib/ruote/parser/ruby_dsl.rb +4 -4
  32. data/lib/ruote/parser.rb +2 -2
  33. data/lib/ruote/part/block_participant.rb +1 -1
  34. data/lib/ruote/part/engine_participant.rb +1 -1
  35. data/lib/ruote/part/storage_participant.rb +27 -28
  36. data/lib/ruote/part/template.rb +8 -3
  37. data/lib/ruote/receiver/base.rb +24 -6
  38. data/lib/ruote/storage/base.rb +76 -11
  39. data/lib/ruote/storage/fs_storage.rb +10 -0
  40. data/lib/ruote/storage/hash_storage.rb +19 -8
  41. data/lib/ruote/{part → svc}/dispatch_pool.rb +3 -2
  42. data/lib/ruote/svc/dollar_sub.rb +265 -0
  43. data/lib/ruote/{error_handler.rb → svc/error_handler.rb} +6 -1
  44. data/lib/ruote/{exp → svc}/expression_map.rb +31 -37
  45. data/lib/ruote/{part → svc}/participant_list.rb +165 -25
  46. data/lib/ruote/{evt → svc}/tracker.rb +0 -0
  47. data/lib/ruote/{util → svc}/treechecker.rb +0 -0
  48. data/lib/ruote/util/look.rb +4 -1
  49. data/lib/ruote/util/ometa.rb +21 -5
  50. data/lib/ruote/{subprocess.rb → util/subprocess.rb} +0 -0
  51. data/lib/ruote/version.rb +1 -1
  52. data/lib/ruote/worker.rb +29 -69
  53. data/lib/ruote/workitem.rb +28 -1
  54. data/ruote.gemspec +26 -22
  55. data/test/functional/base.rb +3 -0
  56. data/test/functional/concurrent_base.rb +1 -0
  57. data/test/functional/crunner.sh +1 -1
  58. data/test/functional/ct_0_concurrence.rb +6 -0
  59. data/test/functional/ct_1_iterator.rb +3 -0
  60. data/test/functional/ct_2_cancel.rb +5 -0
  61. data/test/functional/eft_13_iterator.rb +39 -4
  62. data/test/functional/eft_14_cursor.rb +39 -0
  63. data/test/functional/eft_30_ref.rb +140 -0
  64. data/test/functional/eft_3_participant.rb +25 -23
  65. data/test/functional/ft_10_dollar.rb +17 -1
  66. data/test/functional/ft_14_re_apply.rb +76 -0
  67. data/test/functional/ft_1_process_status.rb +170 -29
  68. data/test/functional/ft_20_storage_participant.rb +14 -0
  69. data/test/functional/ft_24_block_participants.rb +1 -1
  70. data/test/functional/ft_26_participant_timeout.rb +93 -0
  71. data/test/functional/ft_2_errors.rb +24 -17
  72. data/test/functional/ft_30_smtp_participant.rb +7 -2
  73. data/test/functional/ft_38_participant_more.rb +15 -0
  74. data/test/functional/ft_39_wait_for.rb +34 -1
  75. data/test/functional/ft_3_participant_registration.rb +270 -2
  76. data/test/functional/ft_40_wait_logger.rb +61 -0
  77. data/test/functional/ft_42_storage_copy.rb +4 -0
  78. data/test/functional/{ft_40_participant_on_reply.rb → ft_43_participant_on_reply.rb} +17 -0
  79. data/test/functional/ft_44_var_participant.rb +35 -0
  80. data/test/functional/ft_45_participant_accept.rb +64 -0
  81. data/test/functional/ft_46_launch_single.rb +49 -0
  82. data/test/functional/ft_5_on_error.rb +39 -1
  83. data/test/functional/storage_helper.rb +7 -1
  84. data/test/test_helper.rb +1 -1
  85. data/test/unit/storage.rb +105 -32
  86. data/test/unit/ut_0_ruby_parser.rb +31 -1
  87. data/test/unit/ut_16_parser.rb +20 -0
  88. data/test/unit/ut_19_part_template.rb +11 -1
  89. data/test/unit/ut_20_composite_storage.rb +1 -1
  90. data/test/unit/ut_4_expmap.rb +1 -1
  91. data/test/unit/ut_6_condition.rb +2 -2
  92. metadata +112 -74
  93. data/lib/ruote/exp/raw.rb +0 -44
  94. data/lib/ruote/util/dollar.rb +0 -193
data/CHANGELOG.txt CHANGED
@@ -2,6 +2,56 @@
2
2
  = ruote - CHANGELOG.txt
3
3
 
4
4
 
5
+ == ruote - 2.1.11 released 2010/10/01
6
+
7
+ - Engine#process broken with process where schedule count > 1 (Thanks David)
8
+ - Engine#launch_single for 1! instance processes (Thanks Eric)
9
+ - Workitem#command and #command= helpers
10
+ - ProcessError#workitem helper
11
+ - fetch_flow_expression made public and aliased to fexp
12
+ - ${r:workitem_field} (idea: Nathan Stults)
13
+ - Ruote::Dollar::Dict and Ruote::Dollar::RubyContext
14
+ - made Ruote.dsub a service : @context.dollar_sub.s(...) (idea: Nathan Stults)
15
+ - moved simpler services to ruote/svc/
16
+ - using BlankSlate for ruby process definitions
17
+ - Engine.new(worker, :join => true) will let the worker run in the current
18
+ thread (and not return)
19
+ - StorageParticipant#query(:count => true)
20
+ - storage.get_many(type, keys, :descending => true)
21
+ - engine.processes(:skip => 50, limit => 50)
22
+ - listen expression : using lwfid instead of wfid (storage change)
23
+ - fixed issue when re-applying root expressions (Thanks Brett)
24
+ - storage_participant.query using :skip and :limit
25
+ - engine.schedules([wfid]) and ProcessStatus#schedules
26
+ - differentiating on_re_apply from on_cancel (process gardening)
27
+ - engine.participant_list= more tolerant about its input
28
+ - engine.process(wfid).stored_workitems
29
+ - engine.process(wfid).workitems and .position
30
+ - engine.process(wfid).last_active
31
+ - engine.errors(wfid=nil) now returns array of ProcessError instances
32
+ - engine.register { block } (Torsten)
33
+ - participant.accept?(workitem)
34
+ - 'ref' expression (pointing to participants or subprocesses)
35
+ - engine.process_wfids (list process instance wfids)
36
+ - set 'v:alpha' => [ 'MyParticipant', { 'flavour' => 'vanilla' }]
37
+ participants registered as variables (just for that process instance)
38
+ - engine.participant_list and .participant_list=
39
+ - engine.register_participant 'al', 'AlParticipant', :require_path => 'pa/th'
40
+ - engine.register_participant 'al', 'AlParticipant', :load_path => 'pa/th'
41
+ - engine.[un]register for .[un]register_participant
42
+ - engine.noisy = true (a shortcut)
43
+ - engine.storage_participant (a shortcut)
44
+ - removed direct dependency on rufus-lru
45
+ - WaitLogger : a transient @seen (Thanks Kaspar)
46
+ - error_handler : more error catching
47
+ - storage#copy_to(other_storage) item by item implementation
48
+ - Engine#wait_for OK with multiple threads
49
+ - ExpressionMap now loads all expressions in Ruote::Exp:: namespace
50
+ - 'error_intercepted' msg now features error_class, _message and _backtrace (RM)
51
+ - Engine#join and Worker#join
52
+ - workitem.error now includes error's backtrace (contributed by Rich Meyers)
53
+
54
+
5
55
  == ruote - 2.1.10 released 2010/06/15
6
56
 
7
57
  - storage#copy_to(other_storage) implemented
@@ -30,7 +80,7 @@
30
80
  - Workitem#error and Workitem#timed_out shortcuts
31
81
  - participant :on_error => 'x' broken. Fixed. Thanks Oleg.
32
82
  - Engine#workitem(fei) for advanced users
33
- - LocalParticpant : added a reject(workitem) method
83
+ - LocalParticipant : added a reject(workitem) method
34
84
  - participant exp : dispatched = true set right after dispatch
35
85
 
36
86
 
data/CREDITS.txt CHANGED
@@ -16,6 +16,10 @@ Torsten Schoenebaum - http://github.com/tosch
16
16
  Contributors
17
17
  ------------
18
18
 
19
+ David Goodlad - http://github.com/dgoodlad
20
+ Eric Dennis - http://github.com/threetee
21
+ David Greaves - http://github.com/lbt
22
+ Rich Meyers - http://github.com/richmeyers
19
23
  Oleg Pudeyev - http://github.com/p
20
24
  Brett Anthoine - http://github.com/anb
21
25
  Matt Nichols - http://github.com/mattnichols
@@ -39,6 +43,11 @@ Richard Jennings
39
43
  Feedback
40
44
  --------
41
45
 
46
+ Eric Platon - engine.launch_single idea
47
+ Marc Lee - storage.clear consistency
48
+ Nathan Stults - enhanced dollar substitution and service
49
+ Don H. French - Engine#register_from_dir idea
50
+ Kaspar Schiess - http://github.com/kschiess
42
51
  Oleg (foenixx) - many suggestions and bug reports
43
52
  Avishai Shalom - discussion and ideas about participant/worker locality
44
53
  Gonzalo Suarez - many help
data/README.rdoc CHANGED
@@ -12,6 +12,19 @@ Process definitions are mainly describing how workitems are routed to participan
12
12
 
13
13
  == usage
14
14
 
15
+ grab ruote
16
+
17
+ gem install ruote
18
+ gem install yajl-ruby
19
+
20
+ Note : the json gem has a serious bug :
21
+
22
+ http://github.com/flori/json/issues#issue/21
23
+
24
+ So yajl-ruby is seriously recommended.
25
+
26
+ Then
27
+
15
28
  require 'rubygems'
16
29
  require 'ruote'
17
30
  require 'ruote/storage/fs_storage'
data/Rakefile CHANGED
@@ -4,6 +4,17 @@ require 'lib/ruote/version.rb'
4
4
  require 'rubygems'
5
5
  require 'rake'
6
6
 
7
+ #
8
+ # clean
9
+
10
+ require 'rake/clean'
11
+ CLEAN.include('pkg', 'rdoc', 'ruote_work', 'ruote_data', 'logs')
12
+
13
+ task :default => [ :clean ]
14
+
15
+
16
+ #
17
+ # jeweler tasks
7
18
 
8
19
  begin
9
20
 
@@ -23,10 +34,9 @@ ruote is an open source ruby workflow engine.
23
34
  gem.rubyforge_project = 'ruote'
24
35
  gem.test_file = 'test/test.rb'
25
36
 
26
- gem.add_dependency 'rufus-json', '>= 0.2.2'
27
- gem.add_dependency 'rufus-cloche', '>= 0.1.17'
37
+ gem.add_dependency 'rufus-json', '>= 0.2.5'
38
+ gem.add_dependency 'rufus-cloche', '>= 0.1.20'
28
39
  gem.add_dependency 'rufus-dollar'
29
- gem.add_dependency 'rufus-lru'
30
40
  gem.add_dependency 'rufus-mnemo', '>= 1.1.0'
31
41
  gem.add_dependency 'rufus-scheduler', '>= 2.0.5'
32
42
  gem.add_dependency 'rufus-treechecker', '>= 1.0.3'
@@ -41,32 +51,51 @@ ruote is an open source ruby workflow engine.
41
51
  # Gem::Specification http://www.rubygems.org/read/chapter/20
42
52
  end
43
53
  Jeweler::GemcutterTasks.new
54
+
44
55
  rescue LoadError
45
56
  puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler'
46
57
  end
47
58
 
48
- begin
49
- require 'yard'
50
- YARD::Rake::YardocTask.new do |doc|
51
- doc.options = [ '-o', 'rdoc', '--title', "ruote #{Ruote::VERSION}" ]
52
- end
53
- rescue LoadError
54
- task :yard do
55
- abort 'YARD is not available. In order to run yardoc, you must: sudo gem install yard'
56
- end
59
+
60
+ #
61
+ # rdoc
62
+
63
+ #begin
64
+ # require 'yard'
65
+ # YARD::Rake::YardocTask.new do |doc|
66
+ # doc.options = [ '-o', 'rdoc', '--title', "ruote #{Ruote::VERSION}" ]
67
+ # end
68
+ #rescue LoadError
69
+ # task :yard do
70
+ # abort 'YARD is not available. In order to run yardoc, you must: sudo gem install yard'
71
+ # end
72
+ #end
73
+
74
+ #
75
+ # make sure to have rdoc 2.5.x to run that
76
+ #
77
+ require 'rake/rdoctask'
78
+ Rake::RDocTask.new do |rd|
79
+
80
+ rd.main = 'README.rdoc'
81
+ rd.rdoc_dir = 'rdoc'
82
+
83
+ rd.rdoc_files.include(
84
+ 'README.rdoc', 'CHANGELOG.txt', 'CREDITS.txt', 'lib/**/*.rb')
85
+
86
+ rd.title = "ruote #{Ruote::VERSION}"
57
87
  end
58
88
 
59
- require 'rake/clean'
60
- CLEAN.include('pkg', 'rdoc', 'work', 'logs')
61
89
 
62
- task :default => [ :clean ]
90
+ #
91
+ # upload_rdoc
63
92
 
64
93
  desc 'Upload the documentation to rubyforge'
65
- task :upload_rdoc => :yard do
66
- sh %{
67
- rsync -azv -e ssh \
68
- rdoc \
69
- jmettraux@rubyforge.org:/var/www/gforge-projects/ruote/
70
- }
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 #{account}:#{webdir}/"
71
100
  end
72
101
 
data/TODO.txt CHANGED
@@ -214,6 +214,26 @@
214
214
  [o] storage participant : accept string for fei
215
215
  [o] => Ruote::FlowExpressionId.extract(x)
216
216
  [o] fei : place engine id in fei.to_storage_id (and back)
217
+ [o] wait_for(:inactive) blocks until worker is inactive
218
+ [o] storage0.copy_to(storage1) / migrate_to as requested by Matt Nichols
219
+ [o] check if rufus-lru is still needed
220
+ [o] storage migration : eats all the memory :(
221
+ [x] remove dependency on rufus-scheduler
222
+ [o] engine.storage_participant
223
+ [o] engine.noisy = true shortcut
224
+ [o] (ft_27) set "v:mypart" => [ 'MyParticipant', {} ]
225
+ binding a participant on the fly (just for this process)
226
+ [o] participant timeout as an instance method
227
+ [o] engine.process_wfids (lighter than engine.processes)
228
+ [o] storage.get_many(x, y, :descending => true) ?
229
+ [o] storage_participant.query : use skip and limit
230
+ [o] engine.processes use skip and limit
231
+ [o] engine.errors use skip and limit
232
+ [o] ParticipantEntry is a bit brittle when editing engine.participant_list
233
+ [o] use blank slate for process definition
234
+ [o] lib/ruote/svc for treechecker, error_handler and co (tree_checker)
235
+ [o] part/template.rb, use Rufus::Json.pretty_encode
236
+ [o] storage.clear OR storage.purge! OR both (cf discussion with marc_lee) !!
217
237
 
218
238
  [ ] exp : exp (restricted form of eval ?)
219
239
  [ ] exp : case (is it necessary ?)
@@ -358,12 +378,30 @@
358
378
 
359
379
  [ ] solve the ps#root_expression_for(fei) dilemma
360
380
 
361
- [ ] engine.noisy = true shortcut
362
-
363
381
  [ ] re_apply_stalled
364
382
  http://groups.google.com/group/openwferu-users/browse_thread/thread/ff29f26d6b5fd135
365
383
 
366
- [ ] wait_for(:inactive) blocks until worker is inactive
384
+ [ ] engine.purge (arts style : worker.@msgs = [])
385
+
386
+ [ ] http://ruote.rubyforge.org/irclogs/ruote_2010-07-07.txt
387
+ configuration issue
388
+ maybe fail with a good error message when registering service...
389
+
390
+ [ ] participant 'toto', :while => '${not_good}' (really ?)
391
+
392
+ [ ] engine.register_from_dir() have a way to re-evaluate the dir
393
+ [o] OR remove engine.register_from_dir()
394
+ [o] AND provide a better register &block
395
+ [o] provide a mean for a participant to reject a workitem (intra plist)
396
+ on_reply and now filter ?
397
+
398
+ [ ] rw : upgrade [register_]participant documentation
399
+
400
+ [ ] StorageParticipant#by_store[_name]
401
+ maybe : reform the store_name system !
402
+
403
+ [ ] rename 'parser' to 'reader'
367
404
 
368
- [ ] storage0.copy_to(storage1) / migrate_to as requested by Matt Nichols
405
+ [ ] process_count and error_count ? (processes(:count => true))
406
+ [ ] engine.ps(wfid) (shortcut ?)
369
407
 
data/examples/pong.rb ADDED
@@ -0,0 +1,37 @@
1
+
2
+ # http://gist.github.com/596822
3
+
4
+ require 'rubygems'
5
+ require 'ruote'
6
+
7
+ pdef = Ruote.process_definition do
8
+ repeat do
9
+ ping # mister ping, please shoot first
10
+ pong
11
+ end
12
+ end
13
+
14
+ class Opponent
15
+ include Ruote::LocalParticipant
16
+
17
+ def initialize (options)
18
+ @options = options
19
+ end
20
+
21
+ def consume (workitem)
22
+ puts @options['sound']
23
+ reply_to_engine(workitem)
24
+ end
25
+ end
26
+
27
+ engine = Ruote::Engine.new(Ruote::Worker.new(Ruote::HashStorage.new))
28
+
29
+ engine.register_participant :ping, Opponent, 'sound' => 'ping'
30
+ engine.register_participant :pong, Opponent, 'sound' => 'pong'
31
+
32
+ wfid = engine.launch(pdef)
33
+
34
+ sleep 5 # five seconds of ping pong fun
35
+
36
+ engine.cancel_process(wfid) # game over
37
+
data/lib/ruote/context.rb CHANGED
@@ -113,7 +113,7 @@ module Ruote
113
113
 
114
114
  service = if klass
115
115
 
116
- require(path)
116
+ require(path) if path
117
117
 
118
118
  aa = [ self ]
119
119
  aa << opts if opts
@@ -125,6 +125,14 @@ module Ruote
125
125
  end
126
126
 
127
127
  self.class.class_eval %{ def #{key[2..-1]}; @services['#{key}']; end }
128
+ #
129
+ # This 'one-liner' will add an instance method to Context for this
130
+ # service.
131
+ #
132
+ # If the service key is 's_dishwasher', then the service will be
133
+ # available via Context#dishwasher.
134
+ #
135
+ # I.e. dishwasher = engine.context.dishwasher
128
136
 
129
137
  service
130
138
  end
@@ -163,19 +171,21 @@ module Ruote
163
171
  's_parser' => [
164
172
  'ruote/parser', 'Ruote::Parser' ],
165
173
  's_treechecker' => [
166
- 'ruote/util/treechecker', 'Ruote::TreeChecker' ],
174
+ 'ruote/svc/treechecker', 'Ruote::TreeChecker' ],
167
175
  's_expmap' => [
168
- 'ruote/exp/expression_map', 'Ruote::ExpressionMap' ],
176
+ 'ruote/svc/expression_map', 'Ruote::ExpressionMap' ],
169
177
  's_tracker' => [
170
- 'ruote/evt/tracker', 'Ruote::Tracker' ],
178
+ 'ruote/svc/tracker', 'Ruote::Tracker' ],
171
179
  's_plist' => [
172
- 'ruote/part/participant_list', 'Ruote::ParticipantList' ],
180
+ 'ruote/svc/participant_list', 'Ruote::ParticipantList' ],
173
181
  's_dispatch_pool' => [
174
- 'ruote/part/dispatch_pool', 'Ruote::DispatchPool' ],
175
- 's_logger' => [
176
- 'ruote/log/wait_logger', 'Ruote::WaitLogger' ],
182
+ 'ruote/svc/dispatch_pool', 'Ruote::DispatchPool' ],
183
+ 's_dollar_sub' => [
184
+ 'ruote/svc/dollar_sub', 'Ruote::DollarSubstitution' ],
177
185
  's_error_handler' => [
178
- 'ruote/error_handler', 'Ruote::ErrorHandler' ] }
186
+ 'ruote/svc/error_handler', 'Ruote::ErrorHandler' ],
187
+ 's_logger' => [
188
+ 'ruote/log/wait_logger', 'Ruote::WaitLogger' ] }
179
189
  end
180
190
  end
181
191
  end
@@ -50,6 +50,10 @@ module Ruote
50
50
  Ruote::FlowExpressionId.new(msg['fei'])
51
51
  end
52
52
 
53
+ def wfid
54
+ msg['fei']['wfid']
55
+ end
56
+
53
57
  def tree
54
58
  @h['msg']['tree']
55
59
  end
@@ -82,6 +86,12 @@ module Ruote
82
86
  @h['msg']['workitem']['fields']
83
87
  end
84
88
 
89
+ # Returns an instance of Ruote::Workitem
90
+ #
91
+ def workitem
92
+ Ruote::Workitem.new(msg['workitem'])
93
+ end
94
+
85
95
  protected
86
96
 
87
97
  def to_dot (opts)
@@ -38,20 +38,35 @@ module Ruote
38
38
  #
39
39
  attr_reader :expressions
40
40
 
41
+ # An array of the workitems currently in the storage participant for this
42
+ # process instance.
43
+ #
44
+ # Do not confuse with #workitems
45
+ #
46
+ attr_reader :stored_workitems
47
+
41
48
  # An array of errors currently plaguing the process instance. Hopefully,
42
49
  # this array is empty.
43
50
  #
44
51
  attr_reader :errors
45
52
 
46
- def initialize (context, expressions, errors)
53
+ # An array of schedules (open structs yielding information about the
54
+ # schedules of this process)
55
+ #
56
+ attr_reader :schedules
57
+
58
+ def initialize (context, expressions, stored_workitems, errors, schedules)
47
59
 
48
60
  @expressions = expressions.collect { |e|
49
61
  Ruote::Exp::FlowExpression.from_h(context, e) }
50
62
  @expressions.sort! { |a, b| a.fei.expid <=> b.fei.expid }
51
63
 
52
- @errors = errors.collect { |e|
53
- ProcessError.new(e) }
54
- @errors.sort! { |a, b| a.fei.expid <=> b.fei.expid }
64
+ @stored_workitems = stored_workitems.collect { |h|
65
+ Ruote::Workitem.new(h)
66
+ }
67
+
68
+ @errors = errors.sort! { |a, b| a.fei.expid <=> b.fei.expid }
69
+ @schedules = schedules.sort! { |a, b| a['owner'].sid <=> b['owner'].sid }
55
70
  end
56
71
 
57
72
  # Returns the expression at the root of the process instance.
@@ -134,23 +149,97 @@ module Ruote
134
149
  #
135
150
  def wfid
136
151
 
137
- begin
138
- root_expression.fei.wfid
139
- rescue
140
- @errors.first.fei.wfid
141
- end
152
+ @expressions.any? ? @expressions.first.fei.wfid : @errors.first.fei.wfid
142
153
  end
143
154
 
155
+ # For a process
156
+ #
157
+ # Ruote.process_definition :name => 'review', :revision => '0.1' do
158
+ # author
159
+ # reviewer
160
+ # end
161
+ #
162
+ # will yield 'review'.
163
+ #
144
164
  def definition_name
145
165
 
146
166
  root_expression.attribute('name') || root_expression.attribute_text
147
167
  end
148
168
 
169
+ # For a process
170
+ #
171
+ # Ruote.process_definition :name => 'review', :revision => '0.1' do
172
+ # author
173
+ # reviewer
174
+ # end
175
+ #
176
+ # will yield '0.1'.
177
+ #
149
178
  def definition_revision
150
179
 
151
180
  root_expression.attribute('revision')
152
181
  end
153
182
 
183
+ # Returns the 'position' of the process.
184
+ #
185
+ # pdef = Ruote.process_definition do
186
+ # alpha :task => 'clean car'
187
+ # end
188
+ # wfid = engine.launch(pdef)
189
+ #
190
+ # sleep 0.500
191
+ #
192
+ # engine.process(wfid) # => [["0_0", "alpha", {"task"=>"clean car"}]]
193
+ #
194
+ # A process with concurrent branches will yield multiple 'positions'.
195
+ #
196
+ # It uses #workitems underneath.
197
+ #
198
+ def position
199
+
200
+ workitems.collect { |wi|
201
+ r = [ wi.fei.sid, wi.participant_name ]
202
+ params = wi.fields['params'].dup
203
+ params.delete('ref')
204
+ r << params
205
+ r
206
+ }
207
+ end
208
+
209
+ # Returns a list of the workitems currently 'out' to participants
210
+ #
211
+ # For example, with an instance of
212
+ #
213
+ # Ruote.process_definition do
214
+ # concurrence do
215
+ # alpha :task => 'clean car'
216
+ # bravo :task => 'sell car'
217
+ # end
218
+ # end
219
+ #
220
+ # calling engine.process(wfid).workitems will yield two workitems
221
+ # (alpha and bravo).
222
+ #
223
+ # Warning : do not confuse the workitems here with the workitems held
224
+ # in a storage participant or equivalent.
225
+ #
226
+ def workitems
227
+
228
+ @expressions.select { |fexp|
229
+ fexp.is_a?(Ruote::Exp::ParticipantExpression)
230
+ }.collect { |fexp|
231
+ Ruote::Workitem.new(fexp.h.applied_workitem)
232
+ }
233
+ end
234
+
235
+ # Returns a parseable UTC datetime string which indicates when the process
236
+ # was last active.
237
+ #
238
+ def last_active
239
+
240
+ @expressions.collect { |fexp| fexp.h.put_at }.max
241
+ end
242
+
154
243
  # Returns the process definition tree as it was when this process instance
155
244
  # was launched.
156
245
  #
@@ -170,25 +259,33 @@ module Ruote
170
259
 
171
260
  "(process_status wfid '#{wfid}', " +
172
261
  "expressions #{@expressions.size}, " +
173
- "errors #{@errors.size})"
262
+ "stored_workitems #{@stored_workitems.size}, " +
263
+ "errors #{@errors.size}, " +
264
+ "schedules #{@schedules.size}, " +
265
+ ")"
174
266
  end
175
267
 
176
268
  def inspect
177
269
 
178
- s = "== #{self.class} ==\n"
179
- s << " expressions : #{@expressions.size}\n"
270
+ s = [ "== #{self.class} ==" ]
271
+ s << " expressions : #{@expressions.size}"
180
272
  @expressions.each do |e|
181
- s << " #{e.fei.to_storage_id} : #{e}\n"
273
+ s << " #{e.fei.to_storage_id} : #{e}"
182
274
  end
183
- s << " errors : #{@errors.size}\n"
275
+ s << " errors : #{@errors.size}"
184
276
  @errors.each do |e|
185
- s << " #{e.fei.to_storage_id} :\n" if e.fei
186
- s << " #{e.inspect}\n"
277
+ s << " #{e.fei.to_storage_id} :" if e.fei
278
+ s << " #{e.inspect}"
187
279
  end
280
+ s << " schedules : #{@schedules.size}"
281
+ s << " stored workitems : #{@stored_workitems.size}"
188
282
 
189
- s
283
+ s.join("\n") + "\n"
190
284
  end
191
285
 
286
+ # Returns a 'dot' representation of the process. A graph describing
287
+ # the tree of flow expressions that compose the process.
288
+ #
192
289
  def to_dot (opts={})
193
290
 
194
291
  s = [ "digraph \"process wfid #{wfid}\" {" ]
@@ -199,27 +296,24 @@ module Ruote
199
296
  s.join("\n")
200
297
  end
201
298
 
202
- def to_h
203
-
204
- h = {}
205
-
206
- %w[
207
- wfid
208
- definition_name definition_revision
209
- original_tree current_tree
210
- variables tags
211
- ].each { |m| h[m] = self.send(m) }
212
-
213
- h['launched_time'] = launched_time.to_s
214
-
215
- # all_variables and all_tags ?
216
-
217
- h['root_expression'] = nil
218
- h['expressions'] = @expressions.collect { |e| e.fei.to_h }
219
- h['errors'] = @errors.collect { |e| e.to_h }
220
-
221
- h
222
- end
299
+ #--
300
+ #def to_h
301
+ # h = {}
302
+ # %w[
303
+ # wfid
304
+ # definition_name definition_revision
305
+ # original_tree current_tree
306
+ # variables tags
307
+ # ].each { |m| h[m] = self.send(m) }
308
+ # h['launched_time'] = launched_time
309
+ # h['last_active'] = last_active
310
+ # # all_variables and all_tags ?
311
+ # h['root_expression'] = nil
312
+ # h['expressions'] = @expressions.collect { |e| e.fei.to_h }
313
+ # h['errors'] = @errors.collect { |e| e.to_h }
314
+ # h
315
+ #end
316
+ #++
223
317
 
224
318
  # Returns the current version of the process definition tree. If no
225
319
  # manipulation (gardening) was performed on the tree, this method yields
@@ -230,15 +324,21 @@ module Ruote
230
324
  h = Ruote.decompose_tree(original_tree)
231
325
 
232
326
  @expressions.sort { |e0, e1|
327
+
233
328
  e0.fei.expid <=> e1.fei.expid
329
+
234
330
  }.each { |e|
235
- tree = if v = e.tree[1]['_triggered']
331
+
332
+ trigger = e.tree[1]['_triggered']
333
+
334
+ tree = if trigger && trigger != 'on_re_apply'
236
335
  t = original_tree_from_parent(e).dup
237
- t[1]['_triggered'] = v
336
+ t[1]['_triggered'] = trigger
238
337
  t
239
338
  else
240
339
  e.tree
241
340
  end
341
+
242
342
  h.merge!(Ruote.decompose_tree(tree, e.fei.expid))
243
343
  }
244
344
 
@@ -269,7 +369,6 @@ module Ruote
269
369
  return nil unless t
270
370
 
271
371
  t << []
272
-
273
372
  i = 0
274
373
 
275
374
  loop do