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
@@ -183,7 +183,13 @@ module Ruote::Exp
183
183
 
184
184
  pos, subtree = Ruote.lookup_subprocess(self, ref)
185
185
 
186
- vars = compile_atts
186
+ fs, vs = compile_atts.partition { |k, v| k.match(/^f(ield)?:./) }
187
+
188
+ fields = h.applied_workitem['fields']
189
+ fs.each { |k, v| Ruote.set(fields, k.split(':', 2).last, v) }
190
+
191
+ vars = Hash[vs.collect { |k, v| [ k.split(':', 2).last, v ] }]
192
+
187
193
  vars.merge!('tree' => tree_children.first)
188
194
  # NOTE : we're taking the first child here...
189
195
 
@@ -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
@@ -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
@@ -44,16 +44,52 @@ module Ruote::Exp
44
44
  #
45
45
  # cancel :ref => 'invoicing_stage'
46
46
  #
47
+ # == a bit shorter
48
+ #
49
+ # It's OK to shorten
50
+ #
51
+ # cancel :ref => 'invoicing_stage'
52
+ #
53
+ # to
54
+ #
55
+ # cancel 'invoicing_stage'
56
+ #
57
+ # == kill
58
+ #
59
+ # kill :ref => 'invoicing stage'
60
+ #
61
+ # will cancel the target expression and bypass any on_cancel handler set for
62
+ # it.
63
+ #
64
+ # concurrence do
65
+ # sequence :tag => 'x', :on_cancel => 'y' do
66
+ # # ...
67
+ # end
68
+ # sequence do
69
+ # # ...
70
+ # kill 'x'
71
+ # end
72
+ # end
73
+ #
74
+ # In this example the :on_cancel => 'y' will get ignored if kill 'x' kicks
75
+ # in.
76
+ #
47
77
  class UndoExpression < FlowExpression
48
78
 
49
- names :undo, :cancel
79
+ names :undo, :cancel, :kill
50
80
 
51
81
  def apply
52
82
 
53
83
  ref = attribute(:ref) || attribute_text
54
- tag = ref ? lookup_variable(ref) : nil
84
+ ref = ref.strip if ref
85
+
86
+ tag = (ref && ref != '') ? lookup_variable(ref) : nil
55
87
 
56
- @context.storage.put_msg('cancel', 'fei' => tag) if Ruote.is_a_fei?(tag)
88
+ @context.storage.put_msg(
89
+ 'cancel',
90
+ 'fei' => tag,
91
+ 'flavour' => self.name == 'kill' ? 'kill' : nil
92
+ ) if Ruote.is_a_fei?(tag)
57
93
 
58
94
  reply_to_parent(h.applied_workitem)
59
95
  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
@@ -27,8 +27,6 @@ require 'ruote/exp/fe_registerp'
27
27
 
28
28
  module Ruote::Exp
29
29
 
30
- #
31
- # (Since ruote 2.1.12)
32
30
  #
33
31
  # Unregisters a participant.
34
32
  #
@@ -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
@@ -56,27 +56,20 @@ module Ruote::Exp
56
56
  h.for = attribute(:for) || attribute_text
57
57
  h.until = attribute(:until)
58
58
 
59
- s = h.for
60
- s = h.until if s == ''
59
+ h.at = h.for
60
+ h.at = h.until if h.at == ''
61
61
 
62
- h.at = s
62
+ return reply_to_parent(h.applied_workitem) unless h.at
63
63
 
64
- if h.at
64
+ h.schedule_id = @context.storage.put_schedule(
65
+ 'at',
66
+ h.fei,
67
+ h.at,
68
+ 'action' => 'reply',
69
+ 'fei' => h.fei,
70
+ 'workitem' => h.applied_workitem)
65
71
 
66
- h.schedule_id = @context.storage.put_schedule(
67
- 'at',
68
- h.fei,
69
- h.at,
70
- 'action' => 'reply',
71
- 'fei' => h.fei,
72
- 'workitem' => h.applied_workitem)
73
-
74
- persist_or_raise
75
-
76
- else
77
-
78
- reply_to_parent(h.applied_workitem)
79
- end
72
+ persist_or_raise
80
73
  end
81
74
 
82
75
  #--
@@ -84,12 +77,6 @@ module Ruote::Exp
84
77
  #def reply (workitem)
85
78
  #end
86
79
  #++
87
-
88
- def cancel(flavour)
89
-
90
- @context.storage.delete_schedule(h.schedule_id)
91
- reply_to_parent(h.applied_workitem)
92
- end
93
80
  end
94
81
  end
95
82
 
@@ -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,20 +52,50 @@ module Ruote::Exp
52
52
  #
53
53
  # Each node is an expression...
54
54
  #
55
+ #
56
+ # == the states of an expression
57
+ #
58
+ # === nil
59
+ # the normal state
60
+ #
61
+ # === 'cancelling'
62
+ # the expression and its children are getting cancelled
63
+ #
64
+ # === 'dying'
65
+ # the expression and its children are getting killed
66
+ #
67
+ # === 'failed'
68
+ # the expression has finishing
69
+ #
70
+ # === 'failing'
71
+ # the expression just failed and it's cancelling its children
72
+ #
73
+ # === 'timing_out'
74
+ # the expression just timedout and it's cancelling its children
75
+ #
76
+ # === 'paused'
77
+ # the expression is paused, it will store downstream messages and play
78
+ # them only when a 'resume' message comes from upstream.
79
+ #
55
80
  class FlowExpression
56
81
 
57
82
  include Ruote::WithH
58
83
  include Ruote::WithMeta
59
84
 
60
- require 'ruote/exp/ro_persist'
61
85
  require 'ruote/exp/ro_attributes'
62
- require 'ruote/exp/ro_variables'
63
86
  require 'ruote/exp/ro_filters'
87
+ require 'ruote/exp/ro_on_x'
88
+ require 'ruote/exp/ro_persist'
89
+ require 'ruote/exp/ro_timers'
90
+ require 'ruote/exp/ro_variables'
64
91
 
65
92
  COMMON_ATT_KEYS = %w[
66
- if unless forget timeout on_error on_cancel on_timeout ]
93
+ if unless
94
+ forget lose flank
95
+ timeout timers
96
+ on_error on_cancel on_timeout
97
+ ]
67
98
 
68
- attr_reader :context
69
99
  attr_reader :h
70
100
 
71
101
  h_reader :variables
@@ -79,6 +109,17 @@ module Ruote::Exp
79
109
  h_reader :on_error
80
110
  h_reader :on_cancel
81
111
  h_reader :on_timeout
112
+ h_reader :on_terminate
113
+
114
+ attr_reader :context
115
+
116
+ # Mostly used when the expression is returned via Ruote::Engine#ps(wfid) or
117
+ # Ruote::Engine#processes(). If an error occurred for this flow expression,
118
+ # #ps will set this error field so that it yields the ProcessError.
119
+ #
120
+ # So, for short, usually, this attribute yields nil.
121
+ #
122
+ attr_accessor :error
82
123
 
83
124
  def initialize(context, h)
84
125
 
@@ -99,13 +140,13 @@ module Ruote::Exp
99
140
  h.on_cancel ||= attribute(:on_cancel)
100
141
  h.on_error ||= attribute(:on_error)
101
142
  h.on_timeout ||= attribute(:on_timeout)
143
+ h.on_terminate ||= attribute(:on_terminate)
102
144
  end
103
145
 
104
146
  def h=(hash)
147
+
105
148
  @h = hash
106
- class << h
107
- include Ruote::HashDot
108
- end
149
+ class << @h; include Ruote::HashDot; end
109
150
  end
110
151
 
111
152
  # Returns the Ruote::FlowExpressionId for this expression.
@@ -115,12 +156,22 @@ module Ruote::Exp
115
156
  Ruote::FlowExpressionId.new(h.fei)
116
157
  end
117
158
 
159
+ # Returns the workflow instance id of the workflow this expression
160
+ # belongs to.
161
+ #
162
+ def wfid
163
+
164
+ h.fei['wfid']
165
+ end
166
+
118
167
  # Returns the Ruote::FlowExpressionIf of the parent expression, or nil
119
168
  # if there is no parent expression.
120
169
  #
121
170
  def parent_id
122
171
 
123
- h.parent_id ? Ruote::FlowExpressionId.new(h.parent_id) : nil
172
+ h.parent_id ?
173
+ Ruote::FlowExpressionId.new(h.parent_id) :
174
+ nil
124
175
  end
125
176
 
126
177
  # Fetches the parent expression, or returns nil if there is no parent
@@ -128,7 +179,34 @@ module Ruote::Exp
128
179
  #
129
180
  def parent
130
181
 
131
- Ruote::Exp::FlowExpression.fetch(@context, h.parent_id)
182
+ h.parent_id ?
183
+ Ruote::Exp::FlowExpression.fetch(@context, h.parent_id) :
184
+ nil
185
+ end
186
+
187
+ # Returns the root expression of this expression.
188
+ #
189
+ # The result is an instance of Ruote::FlowExpression or nil if the
190
+ # parent cannot be found.
191
+ #
192
+ def root
193
+
194
+ current = @h
195
+ exps = @context.storage.find_expressions(h.fei['wfid'])
196
+
197
+ while current && current['parent_id']
198
+ current = exps.find { |e| e['fei'] == current['parent_id'] }
199
+ end
200
+
201
+ current ? Ruote::Exp::FlowExpression.from_h(@context, current) : nil
202
+ end
203
+
204
+ # Returns the fei of the root expression of this expression.
205
+ # The result is an instance of Ruote::FlowExpressionId.
206
+ #
207
+ def root_id
208
+
209
+ root.fei
132
210
  end
133
211
 
134
212
  # Turns this FlowExpression instance into a Hash (well, just hands back
@@ -139,10 +217,19 @@ module Ruote::Exp
139
217
  @h
140
218
  end
141
219
 
220
+ # Returns a one-off Ruote::Workitem instance (the applied workitem).
221
+ #
222
+ def applied_workitem
223
+
224
+ @awi ||= Ruote::Workitem.new(h.applied_workitem)
225
+ end
226
+
142
227
  # Instantiates expression back from hash.
143
228
  #
144
229
  def self.from_h(context, h)
145
230
 
231
+ return self.new(nil, h) unless context
232
+
146
233
  exp_class = context.expmap.expression_class(h['name'])
147
234
 
148
235
  exp_class.new(context, h)
@@ -182,8 +269,6 @@ module Ruote::Exp
182
269
  fei = msg['fei']
183
270
  action = msg['action']
184
271
 
185
- p msg unless fei
186
-
187
272
  if action == 'reply' && fei['engine_id'] != context.engine_id
188
273
  #
189
274
  # the reply has to go to another engine, let's locate the
@@ -206,14 +291,26 @@ module Ruote::Exp
206
291
 
207
292
  fexp = nil
208
293
 
209
- 3.times do
210
- fexp = fetch(context, msg['fei'])
211
- break if fexp
212
- sleep 0.028
294
+ n = context.storage.class.name.match(/Couch/) ? 3 : 1
295
+ #
296
+ n.times do |i|
297
+ if fexp = fetch(context, msg['fei']); break; end
298
+ sleep 0.028 unless i == (n - 1)
213
299
  end
214
- # this retry system is only useful with ruote-couch
300
+ #
301
+ # Simplify that once ruote-couch behaves
302
+
303
+ fexp.do(action, msg) if fexp
304
+ end
305
+
306
+ # Wraps a call to "apply", "reply", etc... Makes sure to set @msg
307
+ # with a deep copy of the msg before.
308
+ #
309
+ def do(action, msg)
310
+
311
+ @msg = Ruote.fulldup(msg)
215
312
 
216
- fexp.send("do_#{action}", msg) if fexp
313
+ send("do_#{action}", msg)
217
314
  end
218
315
 
219
316
  # Called by the worker when it has just created this FlowExpression and
@@ -221,34 +318,51 @@ module Ruote::Exp
221
318
  #
222
319
  def do_apply(msg)
223
320
 
224
- @msg = Ruote.fulldup(msg)
321
+ unless Condition.apply?(attribute(:if), attribute(:unless))
322
+
323
+ return do_reply_to_parent(h.applied_workitem)
324
+ end
325
+
326
+ pi = h.parent_id
327
+ reply_immediately = false
225
328
 
226
- if not Condition.apply?(attribute(:if), attribute(:unless))
329
+ if attribute(:scope).to_s == 'true'
227
330
 
228
- return reply_to_parent(h.applied_workitem)
331
+ h.variables ||= {}
229
332
  end
230
333
 
231
334
  if attribute(:forget).to_s == 'true'
232
335
 
233
- pi = h.parent_id
234
- wi = Ruote.fulldup(h.applied_workitem)
235
-
236
336
  h.variables = compile_variables
237
337
  h.parent_id = nil
238
338
  h.forgotten = true
239
339
 
240
- @context.storage.put_msg('reply', 'fei' => pi, 'workitem' => wi) if pi
241
- # reply to parent immediately (if there is a parent)
340
+ reply_immediately = true
242
341
 
243
342
  elsif attribute(:lose).to_s == 'true'
244
343
 
245
344
  h.lost = true
345
+
346
+ elsif msg['flanking'] or (attribute(:flank).to_s == 'true')
347
+
348
+ h.flanking = true
349
+
350
+ reply_immediately = true
351
+ end
352
+
353
+ if reply_immediately and pi
354
+
355
+ @context.storage.put_msg(
356
+ 'reply',
357
+ 'fei' => pi,
358
+ 'workitem' => Ruote.fulldup(h.applied_workitem),
359
+ 'flanking' => h.flanking)
246
360
  end
247
361
 
248
362
  filter
249
363
 
250
364
  consider_tag
251
- consider_timeout
365
+ consider_timers
252
366
 
253
367
  apply
254
368
  end
@@ -257,50 +371,97 @@ module Ruote::Exp
257
371
  # parent expression to take over (it will end up calling the #reply of
258
372
  # the parent expression).
259
373
  #
374
+ # Expression implementations are free to override this method.
375
+ # The common behaviour is in #do_reply_to_parent.
376
+ #
260
377
  def reply_to_parent(workitem, delete=true)
261
378
 
262
- filter(workitem)
263
-
264
- if h.tagname
379
+ do_reply_to_parent(workitem, delete)
380
+ end
265
381
 
266
- unset_variable(h.tagname)
382
+ # The essence of the reply_to_parent job...
383
+ #
384
+ def do_reply_to_parent(workitem, delete=true)
267
385
 
268
- Ruote::Workitem.remove_tag(workitem, h.tagname)
386
+ # propagate the cancel "flavour" back, so that one can know
387
+ # why a branch got cancelled.
269
388
 
270
- @context.storage.put_msg(
271
- 'left_tag',
272
- 'tag' => h.tagname,
273
- 'fei' => h.fei,
274
- 'workitem' => workitem)
389
+ flavour = if @msg.nil?
390
+ nil
391
+ elsif @msg['action'] == 'cancel'
392
+ @msg['flavour'] || 'cancel'
393
+ elsif h.state.nil?
394
+ nil
395
+ else
396
+ @msg['flavour']
275
397
  end
276
398
 
277
- if h.timeout_schedule_id && h.state != 'timing_out'
399
+ # deal with the timers and the schedules
278
400
 
279
- @context.storage.delete_schedule(h.timeout_schedule_id)
401
+ %w[ timeout_schedule_id job_id ].each do |sid|
402
+ @context.storage.delete_schedule(h[sid]) if h[sid]
280
403
  end
404
+ #
405
+ # legacy schedule ids, to be removed for ruote 2.4.0
406
+
407
+ @context.storage.delete_schedule(h.schedule_id) if h.schedule_id
408
+ #
409
+ # time-driven exps like cron, wait and once now all use h.schedule_id
410
+
411
+ h.timers.each do |schedule_id, action|
412
+ @context.storage.delete_schedule(schedule_id)
413
+ end if h.timers
414
+
415
+ # cancel flanking expressions if any
416
+
417
+ cancel_flanks(h.state == 'dying' ? 'kill' : nil)
281
418
 
282
- if h.state == 'failing' # on_error is implicit (#fail got called)
419
+ # trigger or vanilla reply
420
+
421
+ if h.state == 'failing' # on_error is implicit (#do_fail got called)
283
422
 
284
423
  trigger('on_error', workitem)
285
424
 
286
- elsif h.state == 'cancelling' and h.on_cancel
425
+ elsif h.state == 'cancelling' && h.on_cancel
287
426
 
288
427
  trigger('on_cancel', workitem)
289
428
 
290
- elsif h.state == 'cancelling' and h.on_re_apply
429
+ elsif h.state == 'cancelling' && h.on_re_apply
291
430
 
292
431
  trigger('on_re_apply', workitem)
293
432
 
294
- elsif h.state == 'timing_out' and h.on_timeout
433
+ elsif h.state == 'timing_out' && h.on_timeout
295
434
 
296
435
  trigger('on_timeout', workitem)
297
436
 
298
- elsif h.lost and h.state == nil
437
+ elsif h.state == nil && h.on_reply
438
+
439
+ trigger('on_reply', workitem)
299
440
 
441
+ elsif (h.lost || h.flanking) && h.state.nil?
442
+ #
300
443
  # do not reply, sit here (and wait for cancellation probably)
301
444
 
445
+ do_persist
446
+
447
+ elsif h.trigger && workitem['fields']["__#{h.trigger}__"]
448
+ #
449
+ # the "second take"
450
+
451
+ trigger(h.trigger, workitem)
452
+
302
453
  else # vanilla reply
303
454
 
455
+ filter(workitem) if h.state.nil?
456
+
457
+ f = h.state.nil? && attribute(:vars_to_f)
458
+ Ruote.set(workitem['fields'], f, h.variables) if f
459
+
460
+ workitem['sub_wf_name'] = @h.applied_workitem['sub_wf_name']
461
+ workitem['sub_wf_revision'] = @h.applied_workitem['sub_wf_revision']
462
+
463
+ leave_tag(workitem) if h.tagname
464
+
304
465
  (do_unpersist || return) if delete
305
466
  # remove expression from storage
306
467
 
@@ -310,14 +471,30 @@ module Ruote::Exp
310
471
  'reply',
311
472
  'fei' => h.parent_id,
312
473
  'workitem' => workitem.merge!('fei' => h.fei),
313
- 'updated_tree' => h.updated_tree) # nil most of the time
474
+ 'updated_tree' => h.updated_tree, # nil most of the time
475
+ 'flavour' => flavour)
476
+
314
477
  else
315
478
 
316
479
  @context.storage.put_msg(
317
480
  h.forgotten ? 'ceased' : 'terminated',
318
481
  'wfid' => h.fei['wfid'],
319
482
  'fei' => h.fei,
320
- 'workitem' => workitem)
483
+ 'workitem' => workitem,
484
+ 'variables' => h.variables,
485
+ 'flavour' => flavour)
486
+
487
+ if h.state.nil? && h.on_terminate == 'regenerate' && ! h.forgotten
488
+
489
+ @context.storage.put_msg(
490
+ 'regenerate',
491
+ 'wfid' => h.fei['wfid'],
492
+ 'tree' => h.original_tree,
493
+ 'workitem' => workitem,
494
+ 'variables' => h.variables,
495
+ 'flavour' => flavour)
496
+ #'stash' =>
497
+ end
321
498
  end
322
499
  end
323
500
  end
@@ -326,22 +503,37 @@ module Ruote::Exp
326
503
  #
327
504
  def do_reply(msg)
328
505
 
329
- @msg = Ruote.fulldup(msg)
330
- # keeping the message, for 'retry' in collision cases
331
-
332
506
  workitem = msg['workitem']
333
507
  fei = workitem['fei']
334
508
 
509
+ removed = h.children.delete(fei)
510
+ # accept without any check ?
511
+
512
+ if msg['flanking']
513
+
514
+ (h.flanks ||= []) << fei
515
+
516
+ if (not removed) # then it's a timer
517
+
518
+ do_persist
519
+ return
520
+ end
521
+ end
522
+
335
523
  if ut = msg['updated_tree']
524
+
336
525
  ct = tree.dup
337
526
  ct.last[Ruote::FlowExpressionId.child_id(fei)] = ut
338
527
  update_tree(ct)
339
528
  end
340
529
 
341
- h.children.delete(fei)
342
- # accept without any check ?
530
+ if h.state == 'paused'
343
531
 
344
- if h.state != nil # failing or timing out ...
532
+ (h['paused_replies'] ||= []) << msg
533
+
534
+ do_persist
535
+
536
+ elsif h.state != nil # failing or timing out ...
345
537
 
346
538
  if h.children.size < 1
347
539
  reply_to_parent(workitem)
@@ -371,8 +563,6 @@ module Ruote::Exp
371
563
  #
372
564
  def do_cancel(msg)
373
565
 
374
- @msg = Ruote.fulldup(msg)
375
-
376
566
  flavour = msg['flavour']
377
567
 
378
568
  return if h.state == 'cancelling' && flavour != 'kill'
@@ -381,42 +571,53 @@ module Ruote::Exp
381
571
  return if h.state == 'failed' && flavour == 'timeout'
382
572
  # do not timeout expressions that are "in error" (failed)
383
573
 
384
- @msg = Ruote.fulldup(msg)
385
-
386
574
  h.state = case flavour
387
575
  when 'kill' then 'dying'
388
576
  when 'timeout' then 'timing_out'
389
577
  else 'cancelling'
390
578
  end
391
579
 
392
- h.applied_workitem['fields']['__timed_out__'] = [
393
- h.fei, Ruote.now_to_utc_s
394
- ] if h.state == 'timing_out'
580
+ if h.state == 'timing_out'
581
+
582
+ h.applied_workitem['fields']['__timed_out__'] = [
583
+ h.fei, Ruote.now_to_utc_s, tree.first, compile_atts
584
+ ]
395
585
 
396
- if h.state == 'cancelling'
586
+ elsif h.state == 'cancelling'
397
587
 
398
588
  if t = msg['on_cancel']
399
589
 
400
590
  h.on_cancel = t
401
591
 
402
- elsif hra = msg['re_apply']
592
+ elsif ra_opts = msg['re_apply']
403
593
 
404
- hra = {} if hra == true
594
+ ra_opts = {} if ra_opts == true
595
+ ra_opts['tree'] ||= tree
405
596
 
406
- h.on_re_apply = hra['tree'] || tree
407
-
408
- if fs = hra['fields']
409
- h.applied_workitem['fields'] = fs
410
- end
411
- if mfs = hra['merge_in_fields']
412
- h.applied_workitem['fields'].merge!(mfs)
413
- end
597
+ h.on_re_apply = ra_opts
414
598
  end
415
599
  end
416
600
 
417
601
  cancel(flavour)
418
602
  end
419
603
 
604
+ # Emits a cancel message for each flanking expression (if any).
605
+ #
606
+ def cancel_flanks(flavour)
607
+
608
+ return unless h.flanks
609
+
610
+ h.flanks.each do |flank_fei|
611
+
612
+ @context.storage.put_msg(
613
+ 'cancel',
614
+ 'fei' => flank_fei,
615
+ 'parent_id' => h.fei,
616
+ # indicating that this is a "cancel child", well...
617
+ 'flavour' => flavour)
618
+ end
619
+ end
620
+
420
621
  # This default implementation cancels all the [registered] children
421
622
  # of this expression.
422
623
  #
@@ -434,56 +635,84 @@ module Ruote::Exp
434
635
  # if the do_persist returns false, it means it failed, implying this
435
636
  # expression is stale, let's return, thus discarding this cancel message
436
637
 
437
- children.each do |cfei|
638
+ children.each do |child_fei|
438
639
  #
439
640
  # let's send a cancel message to each of the children
440
641
  #
441
642
  # maybe some of them are gone or have not yet been applied, anyway,
442
- # the message are sent
643
+ # the messages are sent
443
644
 
444
645
  @context.storage.put_msg(
445
646
  'cancel',
446
- 'fei' => cfei,
647
+ 'fei' => child_fei,
447
648
  'parent_id' => h.fei, # indicating that this is a "cancel child"
448
649
  'flavour' => flavour)
449
650
  end
450
-
451
- #if ! children.find { |i| Ruote::Exp::FlowExpression.fetch(@context, i) }
452
- # #
453
- # # since none of the children could be found in the storage right now,
454
- # # it could mean that all children are already done or it could mean
455
- # # that they are not yet applied...
456
- # #
457
- # # just to be sure let's send a new cancel message to this expression
458
- # #
459
- # # it's very important, since if there is no child to cancel the parent
460
- # # the flow might get stuck here
461
- # @context.storage.put_msg(
462
- # 'cancel',
463
- # 'fei' => h.fei,
464
- # 'flavour' => flavour)
465
- #end
466
651
  end
467
652
 
468
653
  # Called when handling an on_error, will place itself in a 'failing' state
469
654
  # and cancel the children (when the reply from the children comes back,
470
- # the on_reply will get triggered).
655
+ # the on_error will get triggered).
471
656
  #
472
657
  def do_fail(msg)
473
658
 
474
- @msg = Ruote.fulldup(msg)
475
-
476
659
  @h['state'] = 'failing'
477
660
  @h['applied_workitem'] = msg['workitem']
478
661
 
479
662
  if h.children.size < 1
663
+
480
664
  reply_to_parent(@h['applied_workitem'])
665
+
481
666
  else
667
+
668
+ flavour = msg['immediate'] ? 'kill' : nil
669
+
482
670
  persist_or_raise
483
- h.children.each { |i| @context.storage.put_msg('cancel', 'fei' => i) }
671
+
672
+ h.children.each do |i|
673
+ @context.storage.put_msg('cancel', 'fei' => i, 'flavour' => flavour)
674
+ end
484
675
  end
485
676
  end
486
677
 
678
+ # Expression received a "pause" message. Will put the expression in the
679
+ # "paused" state and then pass the message to the children.
680
+ #
681
+ # If the expression is in a non-nil state (failed, timed_out, ...), the
682
+ # message will be ignored.
683
+ #
684
+ def do_pause(msg)
685
+
686
+ return if h.state != nil
687
+
688
+ h['state'] = 'paused'
689
+
690
+ do_persist || return
691
+
692
+ h.children.each { |i|
693
+ @context.storage.put_msg('pause', 'fei' => i)
694
+ } unless msg['breakpoint']
695
+ end
696
+
697
+ # Will "unpause" the expression (if it was paused), and trigger any
698
+ # 'paused_replies' (replies that came while the expression was paused).
699
+ #
700
+ def do_resume(msg)
701
+
702
+ return if h.state != 'paused'
703
+
704
+ h['state'] = nil
705
+ replies = h.delete('paused_replies') || []
706
+
707
+ do_persist || return
708
+
709
+ h.children.each { |i| @context.storage.put_msg('resume', 'fei' => i) }
710
+ # resume children
711
+
712
+ replies.each { |m| @context.storage.put_msg(m.delete('action'), m) }
713
+ # trigger replies
714
+ end
715
+
487
716
  #--
488
717
  # misc
489
718
  #++
@@ -524,77 +753,14 @@ module Ruote::Exp
524
753
  #
525
754
  def ancestor?(fei)
526
755
 
756
+ fei = fei.to_h if fei.respond_to?(:to_h)
757
+
527
758
  return false unless h.parent_id
528
759
  return true if h.parent_id == fei
529
760
 
530
761
  parent.ancestor?(fei)
531
762
  end
532
763
 
533
- # Looks up "on_error" attribute
534
- #
535
- def lookup_on_error
536
-
537
- if h.on_error
538
-
539
- self
540
-
541
- elsif h.parent_id
542
-
543
- par = parent
544
- # :( get_parent would probably be a better name for #parent
545
-
546
- #if par.nil? && ($DEBUG || ARGV.include?('-d'))
547
- # puts "~~"
548
- # puts "parent gone for"
549
- # puts "fei #{Ruote.sid(h.fei)}"
550
- # puts "tree #{tree.inspect}"
551
- # puts "replying to #{Ruote.sid(h.parent_id)}"
552
- # puts "~~"
553
- #end
554
- # is sometimes helpful during debug sessions
555
-
556
- par ? par.lookup_on_error : nil
557
-
558
- else
559
-
560
- nil
561
- end
562
- end
563
-
564
- # Looks up parent with on_error attribute and triggers it
565
- #
566
- def handle_on_error(msg, error)
567
-
568
- return false if h.state == 'failing'
569
-
570
- oe_parent = lookup_on_error
571
-
572
- return false unless oe_parent
573
- # no parent with on_error attribute found
574
-
575
- handler = oe_parent.on_error.to_s
576
-
577
- return false if handler == ''
578
- # empty on_error handler nullifies ancestor's on_error
579
-
580
- workitem = msg['workitem']
581
-
582
- workitem['fields']['__error__'] = {
583
- 'fei' => fei,
584
- 'at' => Ruote.now_to_utc_s,
585
- 'class' => error.class.to_s,
586
- 'message' => error.message,
587
- 'trace' => error.backtrace
588
- }
589
-
590
- @context.storage.put_msg(
591
- 'fail',
592
- 'fei' => oe_parent.h.fei,
593
- 'workitem' => workitem)
594
-
595
- true # yes, error is being handled.
596
- end
597
-
598
764
  #--
599
765
  # TREE
600
766
  #++
@@ -603,6 +769,7 @@ module Ruote::Exp
603
769
  # if it got updated.
604
770
  #
605
771
  def tree
772
+
606
773
  h.updated_tree || h.original_tree
607
774
  end
608
775
 
@@ -623,6 +790,7 @@ module Ruote::Exp
623
790
  # seq.do_persist
624
791
  #
625
792
  def update_tree(t=nil)
793
+
626
794
  h.updated_tree = t || Ruote.fulldup(h.original_tree)
627
795
  end
628
796
 
@@ -728,102 +896,80 @@ module Ruote::Exp
728
896
  #
729
897
  def consider_tag
730
898
 
731
- if h.tagname = attribute(:tag)
732
-
733
- set_variable(h.tagname, h.fei)
899
+ tag = attribute(:tag)
734
900
 
735
- Ruote::Workitem.add_tag(h.applied_workitem, h.tagname)
901
+ return unless tag
736
902
 
737
- @context.storage.put_msg(
738
- 'entered_tag',
739
- 'tag' => h.tagname,
740
- 'fei' => h.fei,
741
- 'workitem' => h.applied_workitem)
742
- end
743
- end
744
-
745
- # Called by do_apply. Overriden in ParticipantExpression and RefExpression.
746
- #
747
- def consider_timeout
903
+ h.tagname = tag
904
+ h.full_tagname = applied_workitem.tags.join('/')
748
905
 
749
- do_schedule_timeout(attribute(:timeout))
750
- end
906
+ return if h.trigger
907
+ #
908
+ # do not consider tags when the tree is applied for an
909
+ # on_x trigger
751
910
 
752
- # Called by consider_timeout (FlowExpression) and schedule_timeout
753
- # (ParticipantExpression).
754
- #
755
- def do_schedule_timeout(timeout)
911
+ h.full_tagname = (applied_workitem.tags + [ tag ]).join('/')
756
912
 
757
- timeout = timeout.to_s
913
+ set_variable(h.tagname, h.fei)
914
+ set_variable('/' + h.full_tagname, h.fei)
758
915
 
759
- return if timeout.strip == ''
916
+ applied_workitem.send(:add_tag, h.tagname)
760
917
 
761
- h.timeout_schedule_id = @context.storage.put_schedule(
762
- 'at',
763
- h.fei,
764
- timeout,
765
- 'action' => 'cancel',
918
+ @context.storage.put_msg(
919
+ 'entered_tag',
920
+ 'tag' => h.tagname,
921
+ 'full_tag' => h.full_tagname,
766
922
  'fei' => h.fei,
767
- 'flavour' => 'timeout')
923
+ 'workitem' => h.applied_workitem)
768
924
  end
769
925
 
770
- # (Called by trigger_on_cancel & co)
926
+ # Called when the expression is about to reply to its parent and wants
927
+ # to get rid of its tags.
771
928
  #
772
- def supplant_with(tree, opts)
773
-
774
- # at first, nuke self
775
-
776
- r = try_unpersist
929
+ def leave_tag(workitem)
777
930
 
778
- raise(
779
- "failed to remove exp to supplant "+
780
- "#{Ruote.to_storage_id(h.fei)} #{tree.first}"
781
- ) if r.respond_to?(:keys)
931
+ unset_variable(h.tagname)
782
932
 
783
- # then re-apply
784
-
785
- if t = opts['trigger']
786
- tree[1]['_triggered'] = t.to_s
787
- end
933
+ Ruote::Workitem.new(workitem).send(:remove_tag, h.tagname)
788
934
 
789
935
  @context.storage.put_msg(
790
- 'apply',
791
- { 'fei' => h.fei,
792
- 'parent_id' => h.parent_id,
793
- 'tree' => tree,
794
- 'workitem' => h.applied_workitem,
795
- 'variables' => h.variables
796
- }.merge!(opts))
797
- end
798
-
799
- # 'on_{error|timeout|cancel|re_apply}' triggering
800
- #
801
- def trigger(on, workitem)
802
-
803
- hon = h[on]
804
-
805
- t = hon.is_a?(String) ? [ hon, {}, [] ] : hon
936
+ 'left_tag',
937
+ 'tag' => h.tagname,
938
+ 'full_tag' => h.full_tagname,
939
+ 'fei' => h.fei,
940
+ 'workitem' => workitem)
806
941
 
807
- if on == 'on_error'
942
+ return unless h.full_tagname # for backward compatibility
808
943
 
809
- if hon == 'redo' or hon == 'retry'
944
+ r = root
810
945
 
811
- t = tree
946
+ return unless r && r.variables # might happen
812
947
 
813
- elsif hon == 'undo' or hon == 'pass'
948
+ r.variables.delete(h.full_tagname)
814
949
 
815
- h.state = 'failed'
816
- reply_to_parent(workitem)
950
+ state = case (h.trigger || h.state)
817
951
 
818
- return
819
- end
952
+ when 'on_cancel' then 'cancelled'
953
+ when 'on_error' then 'failed'
954
+ when 'on_timeout' then 'timed out'
955
+ when 'on_re_apply' then nil
820
956
 
821
- elsif on == 'on_timeout'
957
+ when 'cancelling' then 'cancelled'
958
+ when 'dying' then 'killed'
822
959
 
823
- t = tree if hon == 'redo' or hon == 'retry'
960
+ else nil
824
961
  end
825
962
 
826
- supplant_with(t, 'trigger' => on)
963
+ (r.variables['__past_tags__'] ||= []) << [
964
+ h.full_tagname,
965
+ fei.sid,
966
+ state,
967
+ Ruote.now_to_utc_s,
968
+ Ruote.fulldup(h.variables)
969
+ # not fullduping here triggers a segfault at some point with YAJL
970
+ ]
971
+
972
+ r.do_persist unless r.fei == self.fei
827
973
  end
828
974
  end
829
975
  end