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