ruote 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (305) hide show
  1. data/CHANGELOG.txt +166 -1
  2. data/CREDITS.txt +36 -17
  3. data/LICENSE.txt +1 -1
  4. data/README.rdoc +1 -7
  5. data/Rakefile +38 -29
  6. data/TODO.txt +93 -52
  7. data/lib/ruote-fs.rb +3 -0
  8. data/lib/ruote.rb +5 -1
  9. data/lib/ruote/context.rb +140 -35
  10. data/lib/ruote/dashboard.rb +1247 -0
  11. data/lib/ruote/{engine → dboard}/process_error.rb +22 -2
  12. data/lib/ruote/dboard/process_status.rb +587 -0
  13. data/lib/ruote/engine.rb +6 -871
  14. data/lib/ruote/exp/command.rb +7 -2
  15. data/lib/ruote/exp/commanded.rb +2 -2
  16. data/lib/ruote/exp/condition.rb +38 -13
  17. data/lib/ruote/exp/fe_add_branches.rb +1 -1
  18. data/lib/ruote/exp/fe_apply.rb +1 -1
  19. data/lib/ruote/exp/fe_await.rb +357 -0
  20. data/lib/ruote/exp/fe_cancel_process.rb +17 -3
  21. data/lib/ruote/exp/fe_command.rb +8 -4
  22. data/lib/ruote/exp/fe_concurrence.rb +218 -18
  23. data/lib/ruote/exp/fe_concurrent_iterator.rb +71 -10
  24. data/lib/ruote/exp/fe_cron.rb +3 -10
  25. data/lib/ruote/exp/fe_cursor.rb +14 -4
  26. data/lib/ruote/exp/fe_define.rb +3 -1
  27. data/lib/ruote/exp/fe_echo.rb +1 -1
  28. data/lib/ruote/exp/fe_equals.rb +1 -1
  29. data/lib/ruote/exp/fe_error.rb +1 -1
  30. data/lib/ruote/exp/fe_filter.rb +163 -4
  31. data/lib/ruote/exp/fe_forget.rb +21 -4
  32. data/lib/ruote/exp/fe_given.rb +1 -1
  33. data/lib/ruote/exp/fe_if.rb +1 -1
  34. data/lib/ruote/exp/fe_inc.rb +102 -35
  35. data/lib/ruote/exp/fe_iterator.rb +47 -12
  36. data/lib/ruote/exp/fe_listen.rb +96 -11
  37. data/lib/ruote/exp/fe_lose.rb +31 -4
  38. data/lib/ruote/exp/fe_noop.rb +1 -1
  39. data/lib/ruote/exp/fe_on_error.rb +109 -0
  40. data/lib/ruote/exp/fe_once.rb +10 -19
  41. data/lib/ruote/exp/fe_participant.rb +90 -28
  42. data/lib/ruote/exp/fe_read.rb +69 -0
  43. data/lib/ruote/exp/fe_redo.rb +3 -2
  44. data/lib/ruote/exp/fe_ref.rb +57 -27
  45. data/lib/ruote/exp/fe_registerp.rb +1 -3
  46. data/lib/ruote/exp/fe_reserve.rb +1 -1
  47. data/lib/ruote/exp/fe_restore.rb +6 -6
  48. data/lib/ruote/exp/fe_save.rb +12 -19
  49. data/lib/ruote/exp/fe_sequence.rb +38 -2
  50. data/lib/ruote/exp/fe_set.rb +143 -40
  51. data/lib/ruote/exp/{fe_let.rb → fe_stall.rb} +7 -38
  52. data/lib/ruote/exp/fe_subprocess.rb +8 -2
  53. data/lib/ruote/exp/fe_that.rb +1 -1
  54. data/lib/ruote/exp/fe_undo.rb +40 -4
  55. data/lib/ruote/exp/fe_unregisterp.rb +1 -3
  56. data/lib/ruote/exp/fe_wait.rb +12 -25
  57. data/lib/ruote/exp/{flowexpression.rb → flow_expression.rb} +375 -229
  58. data/lib/ruote/exp/iterator.rb +2 -2
  59. data/lib/ruote/exp/merge.rb +78 -17
  60. data/lib/ruote/exp/ro_attributes.rb +46 -36
  61. data/lib/ruote/exp/ro_filters.rb +34 -8
  62. data/lib/ruote/exp/ro_on_x.rb +431 -0
  63. data/lib/ruote/exp/ro_persist.rb +19 -7
  64. data/lib/ruote/exp/ro_timers.rb +123 -0
  65. data/lib/ruote/exp/ro_variables.rb +90 -29
  66. data/lib/ruote/fei.rb +57 -3
  67. data/lib/ruote/fs.rb +3 -0
  68. data/lib/ruote/id/mnemo_wfid_generator.rb +30 -7
  69. data/lib/ruote/id/wfid_generator.rb +17 -38
  70. data/lib/ruote/log/default_history.rb +23 -9
  71. data/lib/ruote/log/fancy_printing.rb +265 -0
  72. data/lib/ruote/log/storage_history.rb +23 -13
  73. data/lib/ruote/log/wait_logger.rb +224 -17
  74. data/lib/ruote/observer.rb +82 -0
  75. data/lib/ruote/part/block_participant.rb +65 -28
  76. data/lib/ruote/part/code_participant.rb +81 -0
  77. data/lib/ruote/part/engine_participant.rb +7 -2
  78. data/lib/ruote/part/local_participant.rb +221 -21
  79. data/lib/ruote/part/no_op_participant.rb +1 -1
  80. data/lib/ruote/part/null_participant.rb +1 -1
  81. data/lib/ruote/part/participant.rb +50 -0
  82. data/lib/ruote/part/rev_participant.rb +178 -0
  83. data/lib/ruote/part/smtp_participant.rb +2 -2
  84. data/lib/ruote/part/storage_participant.rb +228 -60
  85. data/lib/ruote/part/template.rb +1 -1
  86. data/lib/ruote/participant.rb +2 -0
  87. data/lib/ruote/reader.rb +205 -68
  88. data/lib/ruote/reader/json.rb +49 -0
  89. data/lib/ruote/reader/radial.rb +303 -0
  90. data/lib/ruote/reader/ruby_dsl.rb +44 -9
  91. data/lib/ruote/reader/xml.rb +11 -8
  92. data/lib/ruote/receiver/base.rb +98 -45
  93. data/lib/ruote/storage/base.rb +104 -35
  94. data/lib/ruote/storage/composite_storage.rb +50 -60
  95. data/lib/ruote/storage/fs_storage.rb +25 -34
  96. data/lib/ruote/storage/hash_storage.rb +38 -36
  97. data/lib/ruote/svc/dispatch_pool.rb +104 -35
  98. data/lib/ruote/svc/dollar_sub.rb +10 -8
  99. data/lib/ruote/svc/error_handler.rb +108 -52
  100. data/lib/ruote/svc/expression_map.rb +3 -3
  101. data/lib/ruote/svc/participant_list.rb +160 -55
  102. data/lib/ruote/svc/tracker.rb +31 -31
  103. data/lib/ruote/svc/treechecker.rb +28 -16
  104. data/lib/ruote/tree_dot.rb +1 -1
  105. data/lib/ruote/util/deep.rb +143 -0
  106. data/lib/ruote/util/filter.rb +125 -18
  107. data/lib/ruote/util/hashdot.rb +15 -13
  108. data/lib/ruote/util/look.rb +1 -1
  109. data/lib/ruote/util/lookup.rb +60 -22
  110. data/lib/ruote/util/misc.rb +63 -18
  111. data/lib/ruote/util/mpatch.rb +53 -0
  112. data/lib/ruote/util/ometa.rb +1 -2
  113. data/lib/ruote/util/process_observer.rb +177 -0
  114. data/lib/ruote/util/subprocess.rb +1 -1
  115. data/lib/ruote/util/time.rb +2 -2
  116. data/lib/ruote/util/tree.rb +64 -2
  117. data/lib/ruote/version.rb +3 -2
  118. data/lib/ruote/worker.rb +421 -92
  119. data/lib/ruote/workitem.rb +157 -22
  120. data/ruote.gemspec +15 -9
  121. data/test/bm/ci.rb +0 -2
  122. data/test/bm/ici.rb +0 -2
  123. data/test/bm/load_26c.rb +0 -3
  124. data/test/bm/mega.rb +0 -2
  125. data/test/functional/base.rb +57 -43
  126. data/test/functional/concurrent_base.rb +16 -13
  127. data/test/functional/ct_0_concurrence.rb +7 -11
  128. data/test/functional/ct_1_iterator.rb +9 -11
  129. data/test/functional/ct_2_cancel.rb +28 -17
  130. data/test/functional/eft_0_flow_expression.rb +35 -0
  131. data/test/functional/eft_10_cancel_process.rb +1 -1
  132. data/test/functional/eft_11_wait.rb +13 -13
  133. data/test/functional/eft_12_listen.rb +199 -66
  134. data/test/functional/eft_13_iterator.rb +95 -29
  135. data/test/functional/eft_14_cursor.rb +74 -24
  136. data/test/functional/eft_15_loop.rb +7 -7
  137. data/test/functional/eft_16_if.rb +1 -1
  138. data/test/functional/eft_17_equals.rb +1 -1
  139. data/test/functional/eft_18_concurrent_iterator.rb +156 -68
  140. data/test/functional/eft_19_reserve.rb +15 -15
  141. data/test/functional/eft_1_echo.rb +1 -1
  142. data/test/functional/eft_20_save.rb +51 -9
  143. data/test/functional/eft_21_restore.rb +1 -1
  144. data/test/functional/eft_22_noop.rb +1 -1
  145. data/test/functional/eft_23_apply.rb +1 -1
  146. data/test/functional/eft_24_add_branches.rb +7 -8
  147. data/test/functional/eft_25_command.rb +1 -1
  148. data/test/functional/eft_26_error.rb +11 -11
  149. data/test/functional/eft_27_inc.rb +111 -67
  150. data/test/functional/eft_28_once.rb +16 -16
  151. data/test/functional/eft_29_cron.rb +9 -9
  152. data/test/functional/eft_2_sequence.rb +23 -4
  153. data/test/functional/eft_30_ref.rb +36 -24
  154. data/test/functional/eft_31_registerp.rb +24 -24
  155. data/test/functional/eft_32_lose.rb +46 -20
  156. data/test/functional/eft_34_given.rb +1 -1
  157. data/test/functional/eft_35_filter.rb +161 -7
  158. data/test/functional/eft_36_read.rb +97 -0
  159. data/test/functional/{eft_0_process_definition.rb → eft_37_process_definition.rb} +4 -4
  160. data/test/functional/eft_38_on_error.rb +195 -0
  161. data/test/functional/eft_39_stall.rb +35 -0
  162. data/test/functional/eft_3_participant.rb +77 -22
  163. data/test/functional/eft_40_await.rb +297 -0
  164. data/test/functional/eft_4_set.rb +110 -11
  165. data/test/functional/eft_5_subprocess.rb +27 -5
  166. data/test/functional/eft_6_concurrence.rb +299 -60
  167. data/test/functional/eft_7_forget.rb +24 -22
  168. data/test/functional/eft_8_undo.rb +52 -15
  169. data/test/functional/eft_9_redo.rb +18 -20
  170. data/test/functional/ft_0_worker.rb +122 -13
  171. data/test/functional/ft_10_dollar.rb +77 -16
  172. data/test/functional/ft_11_recursion.rb +9 -9
  173. data/test/functional/ft_12_launchitem.rb +7 -9
  174. data/test/functional/ft_13_variables.rb +125 -22
  175. data/test/functional/ft_14_re_apply.rb +112 -56
  176. data/test/functional/ft_15_timeout.rb +64 -33
  177. data/test/functional/ft_16_participant_params.rb +59 -6
  178. data/test/functional/ft_17_conditional.rb +68 -2
  179. data/test/functional/ft_18_kill.rb +48 -30
  180. data/test/functional/ft_19_participant_code.rb +67 -0
  181. data/test/functional/ft_1_process_status.rb +222 -150
  182. data/test/functional/ft_20_storage_participant.rb +445 -44
  183. data/test/functional/ft_21_forget.rb +21 -26
  184. data/test/functional/ft_22_process_definitions.rb +8 -6
  185. data/test/functional/ft_23_load_defs.rb +29 -5
  186. data/test/functional/ft_24_block_participant.rb +199 -20
  187. data/test/functional/ft_25_receiver.rb +98 -46
  188. data/test/functional/ft_26_participant_rtimeout.rb +34 -26
  189. data/test/functional/ft_27_var_indirection.rb +40 -5
  190. data/test/functional/ft_28_null_noop_participants.rb +5 -5
  191. data/test/functional/ft_29_part_template.rb +2 -2
  192. data/test/functional/ft_2_errors.rb +106 -74
  193. data/test/functional/ft_30_smtp_participant.rb +7 -7
  194. data/test/functional/ft_31_part_blocking.rb +11 -11
  195. data/test/functional/ft_32_scope.rb +50 -0
  196. data/test/functional/ft_33_participant_subprocess_priority.rb +3 -3
  197. data/test/functional/ft_34_cursor_rewind.rb +14 -14
  198. data/test/functional/ft_35_add_service.rb +67 -9
  199. data/test/functional/ft_36_storage_history.rb +92 -24
  200. data/test/functional/ft_37_default_history.rb +35 -23
  201. data/test/functional/ft_38_participant_more.rb +189 -32
  202. data/test/functional/ft_39_wait_for.rb +25 -25
  203. data/test/functional/ft_3_participant_registration.rb +235 -107
  204. data/test/functional/ft_40_wait_logger.rb +105 -18
  205. data/test/functional/ft_41_participants.rb +13 -12
  206. data/test/functional/ft_42_storage_copy.rb +12 -12
  207. data/test/functional/ft_43_participant_on_reply.rb +85 -11
  208. data/test/functional/ft_44_var_participant.rb +5 -5
  209. data/test/functional/ft_45_participant_accept.rb +3 -3
  210. data/test/functional/ft_46_launch_single.rb +17 -17
  211. data/test/functional/ft_47_wfids.rb +41 -0
  212. data/test/functional/ft_48_lose.rb +19 -25
  213. data/test/functional/ft_49_engine_on_error.rb +54 -70
  214. data/test/functional/ft_4_cancel.rb +84 -26
  215. data/test/functional/ft_50_engine_config.rb +4 -4
  216. data/test/functional/ft_51_misc.rb +12 -12
  217. data/test/functional/ft_52_case.rb +17 -17
  218. data/test/functional/ft_53_engine_on_terminate.rb +18 -21
  219. data/test/functional/ft_54_patterns.rb +18 -16
  220. data/test/functional/ft_55_engine_participant.rb +55 -55
  221. data/test/functional/ft_56_filter_attribute.rb +90 -52
  222. data/test/functional/ft_57_rev_participant.rb +252 -0
  223. data/test/functional/ft_58_workitem.rb +150 -0
  224. data/test/functional/ft_59_pause.rb +329 -0
  225. data/test/functional/ft_5_on_error.rb +430 -77
  226. data/test/functional/ft_60_code_participant.rb +65 -0
  227. data/test/functional/ft_61_trailing_fields.rb +34 -0
  228. data/test/functional/ft_62_exp_name_and_dollar_substitution.rb +35 -0
  229. data/test/functional/ft_63_participants_221.rb +458 -0
  230. data/test/functional/ft_64_stash.rb +41 -0
  231. data/test/functional/ft_65_timers.rb +313 -0
  232. data/test/functional/ft_66_flank.rb +133 -0
  233. data/test/functional/ft_67_radial_misc.rb +34 -0
  234. data/test/functional/ft_68_reput.rb +72 -0
  235. data/test/functional/ft_69_worker_info.rb +56 -0
  236. data/test/functional/ft_6_on_cancel.rb +189 -36
  237. data/test/functional/ft_70_take_and_discard_attributes.rb +94 -0
  238. data/test/functional/ft_71_retries.rb +144 -0
  239. data/test/functional/ft_72_on_terminate.rb +60 -0
  240. data/test/functional/ft_73_raise_msg.rb +107 -0
  241. data/test/functional/ft_74_respark.rb +106 -0
  242. data/test/functional/ft_75_context.rb +66 -0
  243. data/test/functional/ft_76_observer.rb +53 -0
  244. data/test/functional/ft_77_process_observer.rb +157 -0
  245. data/test/functional/ft_78_part_participant.rb +37 -0
  246. data/test/functional/ft_7_tags.rb +238 -50
  247. data/test/functional/ft_8_participant_consumption.rb +27 -21
  248. data/test/functional/ft_9_subprocesses.rb +48 -18
  249. data/test/functional/restart_base.rb +4 -6
  250. data/test/functional/rt_0_wait.rb +10 -10
  251. data/test/functional/rt_1_listen.rb +6 -6
  252. data/test/functional/rt_2_errors.rb +12 -12
  253. data/test/functional/rt_3_once.rb +17 -12
  254. data/test/functional/rt_4_cron.rb +17 -17
  255. data/test/functional/rt_5_timeout.rb +13 -13
  256. data/test/functional/signals.rb +103 -0
  257. data/test/functional/storage.rb +730 -0
  258. data/test/functional/storage_helper.rb +48 -35
  259. data/test/functional/test.rb +6 -2
  260. data/test/misc/idle.rb +21 -0
  261. data/test/misc/light.rb +29 -0
  262. data/test/path_helper.rb +1 -1
  263. data/test/test.rb +2 -5
  264. data/test/test_helper.rb +13 -0
  265. data/test/unit/test.rb +1 -4
  266. data/test/unit/ut_0_ruby_reader.rb +25 -9
  267. data/test/unit/ut_10_participants.rb +47 -0
  268. data/test/unit/ut_11_lookup.rb +59 -2
  269. data/test/unit/ut_12_wait_logger.rb +123 -0
  270. data/test/unit/ut_14_is_uri.rb +1 -1
  271. data/test/unit/ut_15_util.rb +1 -1
  272. data/test/unit/ut_16_reader.rb +136 -14
  273. data/test/unit/ut_17_merge.rb +155 -0
  274. data/test/unit/ut_19_part_template.rb +1 -1
  275. data/test/unit/ut_1_fei.rb +11 -2
  276. data/test/unit/ut_20_composite_storage.rb +27 -1
  277. data/test/unit/{ut_21_participant_list.rb → ut_21_svc_participant_list.rb} +2 -3
  278. data/test/unit/ut_22_filter.rb +231 -10
  279. data/test/unit/ut_23_svc_tracker.rb +48 -0
  280. data/test/unit/ut_24_radial_reader.rb +458 -0
  281. data/test/unit/ut_25_process_status.rb +143 -0
  282. data/test/unit/ut_26_deep.rb +131 -0
  283. data/test/unit/ut_2_dashboard.rb +114 -0
  284. data/test/unit/ut_3_worker.rb +54 -0
  285. data/test/unit/ut_4_expmap.rb +1 -1
  286. data/test/unit/ut_5_tree.rb +23 -23
  287. data/test/unit/ut_6_condition.rb +71 -29
  288. data/test/unit/ut_7_workitem.rb +18 -4
  289. data/test/unit/ut_8_tree_to_dot.rb +1 -1
  290. data/test/unit/ut_9_xml_reader.rb +1 -1
  291. metadata +142 -63
  292. data/jruby_issue.txt +0 -32
  293. data/lib/ruote/engine/process_status.rb +0 -403
  294. data/lib/ruote/log/pretty.rb +0 -165
  295. data/lib/ruote/log/test_logger.rb +0 -204
  296. data/lib/ruote/util/serializer.rb +0 -103
  297. data/phil.txt +0 -14
  298. data/test/functional/eft_33_let.rb +0 -31
  299. data/test/functional/ft_19_alias.rb +0 -33
  300. data/test/functional/ft_47_wfid_generator.rb +0 -54
  301. data/test/unit/storage.rb +0 -403
  302. data/test/unit/storages.rb +0 -37
  303. data/test/unit/ut_13_serializer.rb +0 -65
  304. data/test/unit/ut_18_engine.rb +0 -47
  305. data/test/unit/ut_3_wait_logger.rb +0 -39
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
2
+ # Copyright (c) 2005-2012, John Mettraux, jmettraux@gmail.com
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  # of this software and associated documentation files (the "Software"), to deal
@@ -49,13 +49,27 @@ module Ruote::Exp
49
49
  # If the goal is to cancel only a segment of a process instance, the
50
50
  # expression 'undo' (Ruote::Exp::UndoExpression) is better suited.
51
51
  #
52
+ # == 'terminate'
53
+ #
54
+ # Sometimes 'terminate' reads better than 'cancel_process'
55
+ #
56
+ # Ruote.process_definition do
57
+ # alice :task => 'do this'
58
+ # terminate :if => '${no_need_for_bob}'
59
+ # bob :task => 'do that'
60
+ # charly :task => 'just do it'
61
+ # end
62
+ #
52
63
  class CancelProcessExpression < FlowExpression
53
64
 
54
- names :cancel_process, :terminate
65
+ names :cancel_process, :terminate, :kill_process
55
66
 
56
67
  def apply
57
68
 
58
- @context.storage.put_msg('cancel_process', 'wfid' => h.fei['wfid'])
69
+ @context.storage.put_msg(
70
+ 'cancel_process',
71
+ 'wfid' => h.fei['wfid'],
72
+ 'flavour' => name == 'kill_process' ? 'kill' : nil)
59
73
  end
60
74
 
61
75
  def reply(workitem)
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
2
+ # Copyright (c) 2005-2012, John Mettraux, jmettraux@gmail.com
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  # of this software and associated documentation files (the "Software"), to deal
@@ -29,8 +29,8 @@ require 'ruote/exp/command'
29
29
  module Ruote::Exp
30
30
 
31
31
  #
32
- # This class gathers the 'skip', 'back', 'jump', 'rewind', 'continue' and
33
- # 'break' expressions which are used inside of the 'cursor' and 'repeat'
32
+ # This class gathers the 'skip', 'back', 'jump', 'rewind', 'continue', 'reset'
33
+ # and 'break' expressions which are used inside of the 'cursor' and 'repeat'
34
34
  # (loop) expressions.
35
35
  #
36
36
  # Look at the 'cursor' expression Ruote::Exp::Cursor for a discussion of
@@ -87,7 +87,11 @@ module Ruote::Exp
87
87
 
88
88
  include CommandMixin
89
89
 
90
- names :skip, :back, :jump, :rewind, :continue, :break, :stop, :over
90
+ names :skip, :back, :jump, :rewind, :continue, :break, :stop, :over, :reset
91
+
92
+ # Used by FlowExpression when dealing with :on_error or :on_timeout
93
+ #
94
+ REGEXP = Regexp.new("^(#{expression_names.join('|')})( .+)?$")
91
95
 
92
96
  def apply
93
97
 
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
2
+ # Copyright (c) 2005-2012, John Mettraux, jmettraux@gmail.com
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  # of this software and associated documentation files (the "Software"), to deal
@@ -52,6 +52,48 @@ module Ruote::Exp
52
52
  # in that example, the concurrence will terminate as soon as 1 (count) of
53
53
  # the branches replies. The other branch will get cancelled.
54
54
  #
55
+ # :count and :wait_for may point to a negative integer, meaning "all but
56
+ # x".
57
+ #
58
+ # concurrence :count => -2 do # all the branches replied but 2
59
+ # # ...
60
+ # end
61
+ #
62
+ # :count can be shortened to :c.
63
+ #
64
+ # === :wait_for
65
+ #
66
+ # This attribute accepts either an integer, either a list of tags.
67
+ #
68
+ # When used with the integer, it's equivalent to the :count attribute:
69
+ #
70
+ # concurrence :wait_for => 1 do
71
+ # # ...
72
+ # end
73
+ #
74
+ # It waits for 1 branch to respond and then moves on (concurrence over).
75
+ #
76
+ # When used with a string (or an array), it extracts a list of tags and waits
77
+ # for the branches with those tags. Once all the tags have replied,
78
+ # the concurrence is over.
79
+ #
80
+ # concurrence :wait_for => 'alpha, bravo' do
81
+ # sequence :tag => 'alpha' do
82
+ # # ...
83
+ # end
84
+ # sequence :tag => 'bravo' do
85
+ # # ...
86
+ # end
87
+ # sequence :tag => 'charly' do
88
+ # # ...
89
+ # end
90
+ # end
91
+ #
92
+ # This concurrence will be over when the branches alpha and bravo have
93
+ # replied. The charly branch may have replied or not, it doesn't matter.
94
+ #
95
+ # :wait_for can be shortened to :wf.
96
+ #
55
97
  # === :remaining
56
98
  #
57
99
  # As said for :count, the remaining branches get cancelled. By setting
@@ -63,6 +105,15 @@ module Ruote::Exp
63
105
  # bravo
64
106
  # end
65
107
  #
108
+ # :remaining can be shortened to :rem or :r.
109
+ #
110
+ # The default is 'cancel', where all the remaining branches are cancelled
111
+ # while the hand is given back to the main flow.
112
+ #
113
+ # There is a third setting, 'wait'. It behaves like 'cancel', but the
114
+ # concurrence waits for the cancelled children to reply. The workitems
115
+ # from cancelled branches are merged in as well.
116
+ #
66
117
  # === :merge
67
118
  #
68
119
  # By default, the workitems override each others. By default, the first
@@ -97,6 +148,8 @@ module Ruote::Exp
97
148
  #
98
149
  # makes sure that alpha's version of the workitem wins.
99
150
  #
151
+ # :merge can be shortened to :m.
152
+ #
100
153
  # === :merge_type
101
154
  #
102
155
  # ==== :override
@@ -141,8 +194,60 @@ module Ruote::Exp
141
194
  # This could prove useful for participant having to deal with multiple merge
142
195
  # strategy results.
143
196
  #
197
+ # ==== :union
198
+ #
199
+ # (Available from ruote 2.3.0)
200
+ #
201
+ # Will override atomic fields, concat arrays and merge hashes...
202
+ #
203
+ # The union of those two workitems
204
+ #
205
+ # { 'a' => 0, 'b' => [ 'x', 'y' ], 'c' => { 'aa' => 'bb' }
206
+ # { 'a' => 1, 'b' => [ 'y', 'z' ], 'c' => { 'cc' => 'dd' }
207
+ #
208
+ # will be
209
+ #
210
+ # { 'a' => 1,
211
+ # 'b' => [ 'x', 'y', 'z' ],
212
+ # 'c' => { 'aa' => 'bb', 'cc' => 'dd' } }
213
+ #
214
+ # Warning: duplicates in arrays present _before_ the merge will be removed
215
+ # as well.
216
+ #
217
+ # ==== :concat
218
+ #
219
+ # (Available from ruote 2.3.0)
220
+ #
221
+ # Much like :union, but duplicates are not removed. Thus
222
+ #
223
+ # { 'a' => 0, 'b' => [ 'x', 'y' ], 'c' => { 'aa' => 'bb' }
224
+ # { 'a' => 1, 'b' => [ 'y', 'z' ], 'c' => { 'cc' => 'dd' }
225
+ #
226
+ # will be
227
+ #
228
+ # { 'a' => 1,
229
+ # 'b' => [ 'x', 'y', 'y', 'z' ],
230
+ # 'c' => { 'aa' => 'bb', 'cc' => 'dd' } }
231
+ #
232
+ # ==== :deep
233
+ #
234
+ # (Available from ruote 2.3.0)
235
+ #
236
+ # Identical to :concat but hashes are merged with deep_merge (ActiveSupport
237
+ # flavour).
238
+ #
239
+ # ==== :ignore
144
240
  #
145
- # === :over_if (and :over_unless)
241
+ # (Available from ruote 2.3.0)
242
+ #
243
+ # A very simple merge type, the workitems given back by the branches are
244
+ # simply discarded and the workitem as passed to the concurrence expression
245
+ # is used to reply to the parent expression (of the concurrence expression).
246
+ #
247
+ # :merge_type can be shortened to :mt.
248
+ #
249
+ #
250
+ # === :over_if (and :over_unless) attribute
146
251
  #
147
252
  # Like the :count attribute controls how many branches have to reply before
148
253
  # a concurrence ends, the :over attribute is used to specify a condition
@@ -166,30 +271,70 @@ module Ruote::Exp
166
271
 
167
272
  names :concurrence
168
273
 
274
+ COUNT_R = /^-?\d+$/
275
+
169
276
  def apply
170
277
 
171
- h.ccount = attribute(:count).to_i rescue 0
172
- h.ccount = nil if h.ccount < 1
278
+ return do_reply_to_parent(h.applied_workitem) if tree_children.empty?
279
+
280
+ #
281
+ # count and wait_for
282
+
283
+ count = (attribute(:count) || attribute(:c)).to_s
284
+ count = nil unless COUNT_R.match(count)
285
+
286
+ wf = count || attribute(:wait_for) || attribute(:wf)
287
+
288
+ if COUNT_R.match(wf.to_s)
289
+ h.ccount = wf.to_i
290
+ elsif wf
291
+ h.wait_for = Ruote.comma_split(wf)
292
+ end
173
293
 
174
- h.cmerge = att(:merge, %w[ first last highest lowest ])
175
- h.cmerge_type = att(:merge_type, %w[ override mix isolate stack ])
176
- h.remaining = att(:remaining, %w[ cancel forget ])
294
+ #
295
+ # other attributes
296
+
297
+ h.cmerge = att(
298
+ [ :merge, :m ],
299
+ %w[ first last highest lowest ])
300
+ h.cmerge_type = att(
301
+ [ :merge_type, :mt ],
302
+ %w[ override mix isolate stack union ignore concat deep ])
303
+ h.remaining = att(
304
+ [ :remaining, :rem, :r ],
305
+ %w[ cancel forget wait ])
177
306
 
178
307
  h.workitems = (h.cmerge == 'first' || h.cmerge == 'last') ? [] : {}
179
308
 
180
309
  h.over = false
181
310
 
182
311
  apply_children
312
+
313
+ @context.storage.put_msg(
314
+ 'reply', 'fei' => h.fei, 'workitem' => h.applied_workitem
315
+ ) if h.ccount == 0
316
+ #
317
+ # force an immediate reply
183
318
  end
184
319
 
185
320
  def reply(workitem)
186
321
 
322
+ workitem = Ruote.fulldup(workitem)
323
+ #
324
+ # since workitem field merging might happen, better to work on
325
+ # a copy of the workitem (so that history, coming afterwards,
326
+ # doesn't see a modified version of the workitem)
327
+
187
328
  if h.cmerge == 'first' || h.cmerge == 'last'
188
329
  h.workitems << workitem
189
330
  else
190
331
  h.workitems[workitem['fei']['expid']] = workitem
191
332
  end
192
333
 
334
+ if h.wait_for && tag = workitem['fields']['__left_tag__']
335
+ h.wait_for.delete(tag)
336
+ end
337
+
193
338
  over = h.over
194
339
  h.over = over || over?(workitem)
195
340
 
@@ -198,6 +343,10 @@ module Ruote::Exp
198
343
 
199
344
  reply_to_parent(nil)
200
345
 
346
+ elsif h.over && h.remaining == 'wait'
347
+
348
+ reply_to_parent(nil)
349
+
201
350
  elsif h.children.empty?
202
351
 
203
352
  do_unpersist || return
@@ -236,9 +385,13 @@ module Ruote::Exp
236
385
  over_unless = attribute(:over_unless, workitem)
237
386
 
238
387
  if over_if && Condition.true?(over_if)
388
+ workitem['winner'] = true
239
389
  true
240
390
  elsif over_unless && (not Condition.true?(over_unless))
391
+ workitem['winner'] = true
241
392
  true
393
+ elsif h.wait_for
394
+ h.wait_for.empty?
242
395
  else
243
396
  (h.workitems.size >= expected_count)
244
397
  end
@@ -246,30 +399,70 @@ module Ruote::Exp
246
399
 
247
400
  # How many branch replies are expected before the concurrence is over ?
248
401
  #
402
+ def expected_count
403
+
404
+ if h.ccount.nil?
405
+ count_list_size
406
+ elsif h.ccount >= 0
407
+ [ h.ccount, count_list_size ].min
408
+ else # all but 1, 2, ...
409
+ i = count_list_size + h.ccount
410
+ i < 1 ? 1 : i
411
+ end
412
+ end
413
+
249
414
  # (note : concurrent_iterator overrides it)
250
415
  #
251
- def expected_count
416
+ def count_list_size
252
417
 
253
- h.ccount ? [ h.ccount, tree_children.size ].min : tree_children.size
418
+ tree_children.size
254
419
  end
255
420
 
256
421
  def reply_to_parent(_workitem)
257
422
 
423
+ #
424
+ # remaining 'wait' case first
425
+
426
+ if h.remaining == 'wait'
427
+
428
+ if h.workitems.size >= count_list_size
429
+ #
430
+ # all children have replied
431
+
432
+ workitem = merge_all_workitems
433
+
434
+ do_unpersist && super(workitem, false)
435
+
436
+ elsif h.children_cancelled == nil
437
+ #
438
+ # the concurrence is over, let's cancel all children and then
439
+ # wait for them
440
+
441
+ h.children_cancelled = true
442
+ do_persist
443
+
444
+ h.children.each { |i| @context.storage.put_msg('cancel', 'fei' => i) }
445
+ end
446
+
447
+ return
448
+ end
449
+
450
+ #
451
+ # remaining 'forget' and 'cancel' cases
452
+
258
453
  workitem = merge_all_workitems
259
454
 
260
- if h.ccount == nil || h.children.empty?
455
+ if h.children.empty?
261
456
 
262
457
  do_unpersist && super(workitem, false)
263
458
 
264
459
  elsif h.remaining == 'cancel'
265
460
 
266
- if r = do_unpersist
461
+ if do_unpersist
267
462
 
268
463
  super(workitem, false)
269
464
 
270
- h.children.each do |i|
271
- @context.storage.put_msg('cancel', 'fei' => i) #unless replied?(i)
272
- end
465
+ h.children.each { |i| @context.storage.put_msg('cancel', 'fei' => i) }
273
466
  end
274
467
 
275
468
  else # h.remaining == 'forget'
@@ -281,9 +474,13 @@ module Ruote::Exp
281
474
  end
282
475
  end
283
476
 
477
+ # Called by #reply_to_parent, returns the unique, merged, workitem that
478
+ # will be fed back to the parent expression.
479
+ #
284
480
  def merge_all_workitems
285
481
 
286
482
  return h.applied_workitem if h.workitems.size < 1
483
+ return h.applied_workitem if h.cmerge_type == 'ignore'
287
484
 
288
485
  wis = case h.cmerge
289
486
  when 'first'
@@ -294,11 +491,14 @@ module Ruote::Exp
294
491
  is = h.workitems.keys.sort.collect { |k| h.workitems[k] }
295
492
  h.cmerge == 'highest' ? is.reverse : is
296
493
  end
297
- rwis = wis.reverse
298
494
 
299
- wis.inject(nil) { |t, wi|
300
- merge_workitems(rwis.index(wi), t, wi, h.cmerge_type)
301
- }
495
+ as, bs = wis.partition { |wi| wi.delete('winner') }
496
+ wis = bs + as
497
+ #
498
+ # the 'winner' is the workitem that triggered successfully the
499
+ # :over_if or :over_unless, let's take him precedence in the merge...
500
+
501
+ merge_workitems(wis, h.cmerge_type)
302
502
  end
303
503
  end
304
504
  end
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
2
+ # Copyright (c) 2005-2012, John Mettraux, jmettraux@gmail.com
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  # of this software and associated documentation files (the "Software"), to deal
@@ -64,6 +64,43 @@ module Ruote::Exp
64
64
  # rewind/break/jump/... like 'iterator' does, since it fires all its
65
65
  # branches when applied.
66
66
  #
67
+ # == :on and arrays
68
+ #
69
+ # Given a workitem field named 'x' containing the array value
70
+ # [ 'a', 'b', 'c' ] and a workitem field 'y' containing the string 'a, b, c',
71
+ # this:
72
+ #
73
+ # concurrent_iterator :on_field => 'x', :to_f => 'xx' do
74
+ # # ...
75
+ # end
76
+ #
77
+ # is equivalent to
78
+ #
79
+ # concurrent_iterator :on => '$f:x', :to_f => 'xx' do
80
+ # # ...
81
+ # end
82
+ #
83
+ # is equivalent to
84
+ #
85
+ # concurrent_iterator :on => '${f:y}', :to_f => 'xx' do
86
+ # # ...
87
+ # end
88
+ #
89
+ # == :to_field and :to_f, :to_var and :to_v, :to
90
+ #
91
+ # Those 4 lines are equivalent:
92
+ #
93
+ # concurrent_iterator :on => [ 'ceo', 'cto' ], :to_field => 'a' do ...
94
+ # concurrent_iterator :on => [ 'ceo', 'cto' ], :to_f => 'a' do ...
95
+ # concurrent_iterator :on => [ 'ceo', 'cto' ], :to => 'f:a' do ...
96
+ # concurrent_iterator :on => [ 'ceo', 'cto' ], :to => 'a' do ...
97
+ #
98
+ # Those 3 lines are equivalent:
99
+ #
100
+ # concurrent_iterator :on => [ 'ceo', 'cto' ], :to_var => 'a' do ...
101
+ # concurrent_iterator :on => [ 'ceo', 'cto' ], :to_v => 'a' do ...
102
+ # concurrent_iterator :on => [ 'ceo', 'cto' ], :to => 'v:a' do ...
103
+ #
67
104
  # == :times and :branches
68
105
  #
69
106
  # Similarly to the iterator expression, the :times or the :branches attribute
@@ -86,6 +123,28 @@ module Ruote::Exp
86
123
  # end
87
124
  #
88
125
  #
126
+ # == ruote 2.3.0 and the citerator children
127
+ #
128
+ # Prior to ruote 2.3.0, the concurrent-iterator only considered one child
129
+ # expression:
130
+ #
131
+ # concurrent_iterator :times => 3 do
132
+ # participant 'al'
133
+ # participant 'bob' # 'bob' would never be reached
134
+ # end
135
+ #
136
+ # So one had to write:
137
+ #
138
+ # concurrent_iterator :times => 3 do
139
+ # sequence do
140
+ # participant 'al'
141
+ # participant 'bob' # 'bob' would never be reached
142
+ # end
143
+ # end
144
+ #
145
+ # Ruote 2.3.0 lifts that restriction.
146
+ #
147
+ #
89
148
  # == options
90
149
  #
91
150
  # the concurrent_iterator accepts the same options for merging as its bigger
@@ -145,7 +204,7 @@ module Ruote::Exp
145
204
 
146
205
  count = (list.first.to_i rescue nil)
147
206
 
148
- list = (h.list_size + 1..h.list_size + count) if count
207
+ list = (h.list_size + 0...h.list_size + count) if count
149
208
  end
150
209
 
151
210
  list.each do |val|
@@ -153,7 +212,6 @@ module Ruote::Exp
153
212
  h.list_size += 1
154
213
 
155
214
  workitem = Ruote.fulldup(h.applied_workitem)
156
- #workitem = Rufus::Json.dup(h.applied_workitem)
157
215
 
158
216
  variables = { 'ii' => h.list_size - 1 }
159
217
 
@@ -163,11 +221,14 @@ module Ruote::Exp
163
221
  workitem['fields'][h.to_f] = val
164
222
  end
165
223
 
224
+ expid, subtree = if tree_children.size > 1
225
+ [ h.fei['expid'], [ 'sequence', {}, tree_children ] ]
226
+ else
227
+ [ "#{h.fei['expid']}_0", tree_children[0] ]
228
+ end
229
+
166
230
  launch_sub(
167
- "#{h.fei['expid']}_0",
168
- tree_children[0],
169
- :workitem => workitem,
170
- :variables => variables)
231
+ expid, subtree, :workitem => workitem, :variables => variables)
171
232
  end
172
233
  end
173
234
 
@@ -199,7 +260,7 @@ module Ruote::Exp
199
260
  return reply_to_parent(h.applied_workitem) if list.empty?
200
261
 
201
262
  h.to_v, h.to_f = determine_tos
202
- h.to_v = 'i' if h.to_v.nil? && h.to_f.nil?
263
+ h.to_v = 'i' unless h.to_v or h.to_f
203
264
 
204
265
  h.list_size = 0
205
266
 
@@ -210,9 +271,9 @@ module Ruote::Exp
210
271
 
211
272
  # Overrides the implementation found in ConcurrenceExpression
212
273
  #
213
- def expected_count
274
+ def count_list_size
214
275
 
215
- h.ccount ? [ h.ccount, h.list_size ].min : h.list_size
276
+ h.list_size
216
277
  end
217
278
  end
218
279
  end