ruote 2.2.0 → 2.3.0

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 (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