ruote 2.1.10 → 2.1.11

Sign up to get free protection for your applications and to get access to all the features.
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