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
@@ -0,0 +1,178 @@
1
+ #--
2
+ # Copyright (c) 2005-2012, John Mettraux, jmettraux@gmail.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+ # Made in Japan.
23
+ #++
24
+
25
+
26
+ require 'open-uri'
27
+
28
+ require 'ruote/part/local_participant'
29
+
30
+
31
+ module Ruote
32
+
33
+ #
34
+ # This participant was born out of a suggestion from Jan Topiński in
35
+ # http://groups.google.com/group/openwferu-users/browse_thread/thread/be20a5d861556fd8
36
+ #
37
+ # This participant is a gateway to code placed in a directory.
38
+ #
39
+ # engine.register do
40
+ # toto, Ruote::RevParticipant, :dir => 'participants/toto/'
41
+ # end
42
+ #
43
+ # Then in the participants/toto/ dir :
44
+ #
45
+ # /my_workflow__0.1__toto_0.6.rb
46
+ # # participant toto, workflow 'my_workflow' with revision '0.1'
47
+ # /my_workflow__toto.rb
48
+ # # participant toto, workflow 'my_workflow' any revision
49
+ # /toto_0.6.rb
50
+ # # participant toto with rev '0.6', any workflow
51
+ # /toto.rb
52
+ # # participant toto, any rev, any workflow
53
+ # # ...
54
+ #
55
+ # The scheme goes like :
56
+ #
57
+ # /wf-name__wf-revision__participant-name__p-revision.rb
58
+ #
59
+ # The files themselves look like :
60
+ #
61
+ # def consume(workitem)
62
+ # workitem.fields['kilroy'] = 'was here'
63
+ # reply_to_engine(workitem)
64
+ # end
65
+ #
66
+ # The file directly contains the classical participant methods defined at the
67
+ # top level. #cancel, #accept?, #on_reply and of course #consume are OK.
68
+ #
69
+ #
70
+ # Maybe, look at the tests for more clues :
71
+ #
72
+ # https://github.com/jmettraux/ruote/blob/master/test/functional/ft_57_rev_participant.rb
73
+ #
74
+ # *Note* : It's probably not the best participant in a distributed context, it
75
+ # grabs the code to execute from a directory. If you use it in a distributed
76
+ # context, you'll have to make sure to synchronize the directory to each host
77
+ # running a worker.
78
+ #
79
+ # *Warning* : this participant trusts the code it deals with, there is no
80
+ # security check.
81
+ #
82
+ class RevParticipant
83
+
84
+ include LocalParticipant
85
+
86
+ # TODO : how to deal with >= and ~> ?
87
+
88
+ def initialize(opts=nil)
89
+
90
+ @dir = opts['dir']
91
+
92
+ raise ArgumentError.new(
93
+ "missing option :dir for #{self.class}"
94
+ ) unless @dir
95
+ end
96
+
97
+ def on_workitem
98
+
99
+ Ruote.participant_send(
100
+ lookup_code,
101
+ [ :on_workitem, :consume ],
102
+ 'workitem' => workitem)
103
+ end
104
+
105
+ def on_cancel
106
+
107
+ Ruote.participant_send(
108
+ lookup_code,
109
+ [ :on_cancel, :cancel ],
110
+ 'fei' => fei, 'flavour' => flavour)
111
+ end
112
+
113
+ #--
114
+ #def accept?(workitem)
115
+ # part = lookup_code
116
+ # part.respond_to?(:accept?) ? part.accept?(workitem) : true
117
+ #end
118
+ #
119
+ # Can't do this at this level, since it isn't the rev_participant's
120
+ # own accept?, it has to go in lookup_code
121
+ #++
122
+
123
+ def on_reply
124
+
125
+ part = lookup_code
126
+
127
+ if part.respond_to?(:on_reply)
128
+ Ruote.participant_send(part, :on_reply, 'workitem' => workitem)
129
+ end
130
+ end
131
+
132
+ def rtimeout(workitem)
133
+
134
+ part = lookup_code
135
+
136
+ part.respond_to?(:rtimeout) ? part.rtimeout(workitem) : nil
137
+ end
138
+
139
+ protected
140
+
141
+ # Maybe "lookup_real_participant_code" would be a better name...
142
+ #
143
+ def lookup_code
144
+
145
+ wi = workitem
146
+
147
+ rev = wi.params['revision'] || wi.params['rev']
148
+
149
+ [
150
+ [ wi.wf_name, wi.wf_revision, wi.participant_name, rev ],
151
+ [ wi.wf_name, wi.wf_revision, wi.participant_name ],
152
+ [ wi.wf_name, '', wi.participant_name ],
153
+ [ wi.participant_name, rev ],
154
+ [ wi.participant_name ],
155
+ ].each do |fname|
156
+
157
+ fname = File.join(@dir, "#{fname.compact.join('__')}.rb")
158
+ next unless File.exist?(fname)
159
+
160
+ cpart = Class.new
161
+ cpart.send(:include, Ruote::LocalParticipant)
162
+ cpart.module_eval(File.read(fname))
163
+ part = cpart.new
164
+ part.context = @context
165
+
166
+ next if part.respond_to?(:accept?) and (not part.accept?(wi))
167
+
168
+ return part
169
+ end
170
+
171
+ raise ArgumentError.new(
172
+ "couldn't find code for participant #{wi.participant_name} " +
173
+ "in dir #{@dir}"
174
+ )
175
+ end
176
+ end
177
+ end
178
+
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2005-2011, Alain Hoang and John Mettraux.
2
+ # Copyright (c) 2005-2012, Alain Hoang and John Mettraux.
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
@@ -82,7 +82,7 @@ module Ruote
82
82
 
83
83
  def initialize(opts)
84
84
 
85
- @opts = opts.inject({}) { |h, (k, v)| h[k.to_s] = v; h }
85
+ @opts = Ruote.keys_to_s(opts)
86
86
  end
87
87
 
88
88
  def consume(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
@@ -55,8 +55,6 @@ module Ruote
55
55
  include LocalParticipant
56
56
  include Enumerable
57
57
 
58
- attr_accessor :context
59
-
60
58
  def initialize(engine_or_options={}, options=nil)
61
59
 
62
60
  if engine_or_options.respond_to?(:context)
@@ -76,7 +74,10 @@ module Ruote
76
74
  #
77
75
  def do_not_thread; true; end
78
76
 
79
- def consume(workitem)
77
+ # This is the method called by ruote when passing a workitem to
78
+ # this participant.
79
+ #
80
+ def on_workitem
80
81
 
81
82
  doc = workitem.to_h
82
83
 
@@ -90,19 +91,39 @@ module Ruote
90
91
 
91
92
  @context.storage.put(doc)
92
93
  end
93
- alias :update :consume
94
94
 
95
- # Removes the document/workitem from the storage
95
+ # Used by client code when "saving" a workitem (fields may have changed).
96
+ # Calling #update won't proceed the workitem.
97
+ #
98
+ # Returns nil in case of success, true if the workitem is already gone and
99
+ # the newer version of the workitem if the workitem changed in the mean
100
+ # time.
101
+ #
102
+ def update(workitem)
103
+
104
+ r = @context.storage.put(workitem.h)
105
+
106
+ r.is_a?(Hash) ? Ruote::Workitem.new(r) : r
107
+ end
108
+
109
+ # Removes the document/workitem from the storage.
110
+ #
111
+ # Warning: this method is called by the engine (worker), i.e. not by you.
96
112
  #
97
- def cancel(fei, flavour)
113
+ def on_cancel
98
114
 
99
115
  doc = fetch(fei)
100
116
 
117
+ return unless doc
118
+
101
119
  r = @context.storage.delete(doc)
102
120
 
103
- cancel(fei, flavour) if r != nil
121
+ on_cancel(fei, flavour) if r != nil
104
122
  end
105
123
 
124
+ # Given a fei (or its string version, a sid), returns the corresponding
125
+ # workitem (or nil).
126
+ #
106
127
  def [](fei)
107
128
 
108
129
  doc = fetch(fei)
@@ -110,31 +131,46 @@ module Ruote
110
131
  doc ? Ruote::Workitem.new(doc) : nil
111
132
  end
112
133
 
113
- def fetch(fei)
134
+ alias by_fei []
114
135
 
115
- hfei = Ruote::FlowExpressionId.extract_h(fei)
136
+ # Removes the workitem from the storage and replies to the engine.
137
+ #
138
+ def proceed(workitem)
116
139
 
117
- @context.storage.get('workitems', to_id(hfei))
140
+ r = remove_workitem('proceed', workitem)
141
+
142
+ return proceed(workitem) if r != nil
143
+
144
+ workitem.h.delete('_rev')
145
+
146
+ reply_to_engine(workitem)
118
147
  end
119
148
 
120
- # Removes the workitem from the in-memory hash and replies to the engine.
149
+ # Removes the workitem and hands it back to the flow with an error to
150
+ # raise for the participant expression that emitted the workitem.
121
151
  #
122
- # TODO : should it raise if the workitem can't be found ?
123
- # TODO : should it accept just the fei ?
124
- #
125
- def reply(workitem)
152
+ def flunk(workitem, err_class_or_instance, *err_arguments)
126
153
 
127
- # TODO: change method name (receiver mess cleanup)
154
+ r = remove_workitem('reject', workitem)
128
155
 
129
- doc = fetch(Ruote::FlowExpressionId.extract_h(workitem))
156
+ return flunk(workitem) if r != nil
130
157
 
131
- r = @context.storage.delete(doc)
158
+ workitem.h.delete('_rev')
132
159
 
133
- return reply(workitem) if r != nil
160
+ super(workitem, err_class_or_instance, *err_arguments)
161
+ end
134
162
 
135
- workitem.h.delete('_rev')
163
+ # (soon to be removed)
164
+ #
165
+ def reply(workitem)
136
166
 
137
- reply_to_engine(workitem)
167
+ puts '-' * 80
168
+ puts '*** WARNING : please use the Ruote::StorageParticipant#proceed(wi)'
169
+ puts ' instead of #reply(wi) which is deprecated'
170
+ #caller.each { |l| puts l }
171
+ puts '-' * 80
172
+
173
+ proceed(workitem)
138
174
  end
139
175
 
140
176
  # Returns the count of workitems stored in this participant.
@@ -155,7 +191,9 @@ module Ruote
155
191
  #
156
192
  def all(opts={})
157
193
 
158
- fetch_all(opts).map { |hwi| Ruote::Workitem.new(hwi) }
194
+ res = fetch_all(opts)
195
+
196
+ res.is_a?(Array) ? res.map { |hwi| Ruote::Workitem.new(hwi) } : res
159
197
  end
160
198
 
161
199
  # A convenience method (especially when testing), returns the first
@@ -163,36 +201,31 @@ module Ruote
163
201
  #
164
202
  def first
165
203
 
166
- hwi = fetch_all.first
167
-
168
- hwi ? Ruote::Workitem.new(hwi) : nil
204
+ wi(fetch_all({}).first)
169
205
  end
170
206
 
171
207
  # Return all workitems for the specified wfid
172
208
  #
173
- def by_wfid(wfid)
209
+ def by_wfid(wfid, opts={})
174
210
 
175
- @context.storage.get_many('workitems', wfid).collect { |hwi|
176
- Ruote::Workitem.new(hwi)
177
- }
211
+ if @context.storage.respond_to?(:by_wfid)
212
+ return @context.storage.by_wfid('workitems', wfid, opts)
213
+ end
214
+
215
+ wis(@context.storage.get_many('workitems', wfid, opts))
178
216
  end
179
217
 
180
218
  # Returns all workitems for the specified participant name
181
219
  #
182
220
  def by_participant(participant_name, opts={})
183
221
 
184
- hwis = if @context.storage.respond_to?(:by_participant)
222
+ return @context.storage.by_participant(
223
+ 'workitems', participant_name, opts
224
+ ) if @context.storage.respond_to?(:by_participant)
185
225
 
186
- @context.storage.by_participant('workitems', participant_name, opts)
187
-
188
- else
189
-
190
- fetch_all(opts).select { |wi|
191
- wi['participant_name'] == participant_name
192
- }
226
+ do_select(opts) do |hwi|
227
+ hwi['participant_name'] == participant_name
193
228
  end
194
-
195
- hwis.collect { |hwi| Ruote::Workitem.new(hwi) }
196
229
  end
197
230
 
198
231
  # field : returns all the workitems with the given field name present.
@@ -204,21 +237,18 @@ module Ruote
204
237
  # CouchStorage), the others will load all the workitems and then filter
205
238
  # them.
206
239
  #
207
- def by_field(field, value=nil)
208
-
209
- hwis = if @context.storage.respond_to?(:by_field)
240
+ def by_field(field, value=nil, opts={})
210
241
 
211
- @context.storage.by_field('workitems', field, value)
242
+ (value, opts = nil, value) if value.is_a?(Hash)
212
243
 
213
- else
214
-
215
- fetch_all.select { |hwi|
216
- hwi['fields'].keys.include?(field) &&
217
- (value.nil? || hwi['fields'][field] == value)
218
- }
244
+ if @context.storage.respond_to?(:by_field)
245
+ return @context.storage.by_field('workitems', field, value, opts)
219
246
  end
220
247
 
221
- hwis.collect { |hwi| Ruote::Workitem.new(hwi) }
248
+ do_select(opts) do |hwi|
249
+ hwi['fields'].keys.include?(field) &&
250
+ (value.nil? || hwi['fields'][field] == value)
251
+ end
222
252
  end
223
253
 
224
254
  # Queries the store participant for workitems.
@@ -242,7 +272,7 @@ module Ruote
242
272
  #
243
273
  def query(criteria)
244
274
 
245
- cr = criteria.inject({}) { |h, (k, v)| h[k.to_s] = v; h }
275
+ cr = Ruote.keys_to_s(criteria)
246
276
 
247
277
  if @context.storage.respond_to?(:query_workitems)
248
278
  return @context.storage.query_workitems(cr)
@@ -254,25 +284,29 @@ module Ruote
254
284
  opts[:count] = cr.delete('count')
255
285
 
256
286
  wfid = cr.delete('wfid')
287
+
288
+ count = opts[:count]
289
+
257
290
  pname = cr.delete('participant_name') || cr.delete('participant')
291
+ opts.delete(:count) if pname
258
292
 
259
293
  hwis = wfid ?
260
294
  @context.storage.get_many('workitems', wfid, opts) : fetch_all(opts)
261
295
 
262
- return hwis if opts[:count]
296
+ return hwis unless hwis.is_a?(Array)
263
297
 
264
- hwis.select { |hwi|
298
+ hwis = hwis.select { |hwi|
265
299
  Ruote::StorageParticipant.matches?(hwi, pname, cr)
266
- }.collect { |hwi|
267
- Ruote::Workitem.new(hwi)
268
300
  }
301
+
302
+ count ? hwis.size : wis(hwis)
269
303
  end
270
304
 
271
305
  # Cleans this participant out completely
272
306
  #
273
307
  def purge!
274
308
 
275
- fetch_all.each { |hwi| @context.storage.delete(hwi) }
309
+ fetch_all({}).each { |hwi| @context.storage.delete(hwi) }
276
310
  end
277
311
 
278
312
  # Used by #query when filtering workitems.
@@ -295,7 +329,7 @@ module Ruote
295
329
  #
296
330
  def per_participant
297
331
 
298
- inject({}) { |h, wi| (h[wi.participant_name] ||= []) << wi; h }
332
+ each_with_object({}) { |wi, h| (h[wi.participant_name] ||= []) << wi }
299
333
  end
300
334
 
301
335
  # Mostly a test method. Returns a Hash were keys are participant names
@@ -304,15 +338,95 @@ module Ruote
304
338
  #
305
339
  def per_participant_count
306
340
 
307
- per_participant.inject({}) { |h, (k, v)| h[k] = v.size; h }
341
+ per_participant.remap { |(k, v), h| h[k] = v.size }
342
+ end
343
+
344
+ # Claims a workitem. Returns the [updated] workitem if successful.
345
+ #
346
+ # Returns nil if the workitem is already reserved.
347
+ #
348
+ # Fails if the workitem can't be found, is gone, or got modified
349
+ # elsewhere.
350
+ #
351
+ # Here is a mini-diagram explaining the reserve/delegate/proceed flow:
352
+ #
353
+ # in delegate(nil) delegate(other)
354
+ # | +---------------+ +------+
355
+ # v v | | v
356
+ # +-------+ reserve +----------+ proceed
357
+ # | ready | ---------> | reserved | ---------> out
358
+ # +-------+ +----------+
359
+ #
360
+ def reserve(workitem_or_fei, owner)
361
+
362
+ hwi = fetch(workitem_or_fei)
363
+
364
+ fail ArgumentError.new("workitem not found") if hwi.nil?
365
+
366
+ return nil if hwi['owner'] && hwi['owner'] != owner
367
+
368
+ hwi['owner'] = owner
369
+
370
+ r = @context.storage.put(hwi, :update_rev => true)
371
+
372
+ fail ArgumentError.new("workitem is gone") if r == true
373
+ fail ArgumentError.new("workitem got modified meanwhile") if r != nil
374
+
375
+ Workitem.new(hwi)
376
+ end
377
+
378
+ # Delegates a currently owned workitem to a new owner.
379
+ #
380
+ # Fails if the workitem can't be found, belongs to noone, or if the
381
+ # workitem passed as argument is out of date (got modified in the mean
382
+ # time).
383
+ #
384
+ # It's OK to delegate to nil, thus freeing the workitem.
385
+ #
386
+ # See #reserve for an an explanation of the reserve/delegate/proceed flow.
387
+ #
388
+ def delegate(workitem, new_owner)
389
+
390
+ hwi = fetch(workitem)
391
+
392
+ fail ArgumentError.new(
393
+ "workitem not found"
394
+ ) if hwi == nil
395
+
396
+ fail ArgumentError.new(
397
+ "cannot delegate, workitem doesn't belong to anyone"
398
+ ) if hwi['owner'] == nil
399
+
400
+ fail ArgumentError.new(
401
+ "cannot delegate, " +
402
+ "workitem owned by '#{hwi['owner']}', not '#{workitem.owner}'"
403
+ ) if hwi['owner'] != workitem.owner
404
+
405
+ hwi['owner'] = new_owner
406
+
407
+ r = @context.storage.put(hwi, :update_rev => true)
408
+
409
+ fail ArgumentError.new("workitem is gone") if r == true
410
+ fail ArgumentError.new("workitem got modified meanwhile") if r != nil
411
+
412
+ Workitem.new(hwi)
308
413
  end
309
414
 
310
415
  protected
311
416
 
417
+ # Fetches a workitem in its raw form (Hash).
418
+ #
419
+ def fetch(workitem_or_fei)
420
+
421
+ hfei = Ruote::FlowExpressionId.extract_h(workitem_or_fei)
422
+
423
+ @context.storage.get('workitems', to_id(hfei))
424
+ end
425
+
312
426
  # Fetches all the workitems. If there is a @store_name, will only fetch
313
427
  # the workitems in that store.
314
428
  #
315
- def fetch_all(opts={})
429
+ def fetch_all(opts)
316
430
 
317
431
  @context.storage.get_many(
318
432
  'workitems',
@@ -320,6 +434,26 @@ module Ruote
320
434
  opts)
321
435
  end
322
436
 
437
+ # Given a few options and a block, returns all the workitems that match
438
+ # the block
439
+ #
440
+ def do_select(opts, &block)
441
+
442
+ skip = opts[:offset] || opts[:skip]
443
+ limit = opts[:limit]
444
+ count = opts[:count]
445
+
446
+ hwis = fetch_all({})
447
+ hwis = hwis.select(&block)
448
+
449
+ hwis = hwis[skip..-1] if skip
450
+ hwis = hwis[0, limit] if limit
451
+
452
+ return hwis.size if count
453
+
454
+ hwis.collect { |hwi| Ruote::Workitem.new(hwi) }
455
+ end
456
+
323
457
  # Computes the id for the document representing the document in the storage.
324
458
  #
325
459
  def to_id(fei)
@@ -332,6 +466,40 @@ module Ruote
332
466
 
333
467
  a.join('!')
334
468
  end
469
+
470
+ def wi(hwi)
471
+
472
+ hwi ? Ruote::Workitem.new(hwi) : nil
473
+ end
474
+
475
+ def wis(workitems_or_count)
476
+
477
+ workitems_or_count.is_a?(Array) ?
478
+ workitems_or_count.collect { |wi| Ruote::Workitem.new(wi) } :
479
+ workitems_or_count
480
+ end
481
+
482
+ def remove_workitem(action, workitem)
483
+
484
+ hwi = fetch(workitem)
485
+
486
+ fail ArgumentError.new(
487
+ "cannot #{action}, workitem not found"
488
+ ) if hwi == nil
489
+
490
+ fail ArgumentError.new(
491
+ "cannot #{action}, " +
492
+ "workitem is owned by '#{hwi['owner']}', not '#{workitem.owner}'"
493
+ ) if hwi['owner'] && hwi['owner'] != workitem.owner
494
+
495
+ r = @context.storage.delete(hwi)
496
+
497
+ fail ArgumentError.new(
498
+ "cannot #{action}, workitem is gone"
499
+ ) if r == true
500
+
501
+ r
502
+ end
335
503
  end
336
504
  end
337
505