ruote-maestrodev 2.2.1

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 (265) hide show
  1. data/CHANGELOG.txt +290 -0
  2. data/CREDITS.txt +99 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.rdoc +88 -0
  5. data/Rakefile +108 -0
  6. data/TODO.txt +488 -0
  7. data/lib/ruote.rb +7 -0
  8. data/lib/ruote/context.rb +194 -0
  9. data/lib/ruote/engine.rb +1062 -0
  10. data/lib/ruote/engine/process_error.rb +122 -0
  11. data/lib/ruote/engine/process_status.rb +448 -0
  12. data/lib/ruote/exp/command.rb +87 -0
  13. data/lib/ruote/exp/commanded.rb +69 -0
  14. data/lib/ruote/exp/condition.rb +227 -0
  15. data/lib/ruote/exp/fe_add_branches.rb +138 -0
  16. data/lib/ruote/exp/fe_apply.rb +154 -0
  17. data/lib/ruote/exp/fe_cancel_process.rb +78 -0
  18. data/lib/ruote/exp/fe_command.rb +156 -0
  19. data/lib/ruote/exp/fe_concurrence.rb +321 -0
  20. data/lib/ruote/exp/fe_concurrent_iterator.rb +219 -0
  21. data/lib/ruote/exp/fe_cron.rb +141 -0
  22. data/lib/ruote/exp/fe_cursor.rb +324 -0
  23. data/lib/ruote/exp/fe_define.rb +112 -0
  24. data/lib/ruote/exp/fe_echo.rb +60 -0
  25. data/lib/ruote/exp/fe_equals.rb +115 -0
  26. data/lib/ruote/exp/fe_error.rb +82 -0
  27. data/lib/ruote/exp/fe_filter.rb +648 -0
  28. data/lib/ruote/exp/fe_forget.rb +88 -0
  29. data/lib/ruote/exp/fe_given.rb +154 -0
  30. data/lib/ruote/exp/fe_if.rb +127 -0
  31. data/lib/ruote/exp/fe_inc.rb +205 -0
  32. data/lib/ruote/exp/fe_iterator.rb +234 -0
  33. data/lib/ruote/exp/fe_let.rb +75 -0
  34. data/lib/ruote/exp/fe_listen.rb +304 -0
  35. data/lib/ruote/exp/fe_lose.rb +110 -0
  36. data/lib/ruote/exp/fe_noop.rb +45 -0
  37. data/lib/ruote/exp/fe_once.rb +215 -0
  38. data/lib/ruote/exp/fe_participant.rb +287 -0
  39. data/lib/ruote/exp/fe_read.rb +69 -0
  40. data/lib/ruote/exp/fe_redo.rb +82 -0
  41. data/lib/ruote/exp/fe_ref.rb +152 -0
  42. data/lib/ruote/exp/fe_registerp.rb +110 -0
  43. data/lib/ruote/exp/fe_reserve.rb +126 -0
  44. data/lib/ruote/exp/fe_restore.rb +102 -0
  45. data/lib/ruote/exp/fe_save.rb +72 -0
  46. data/lib/ruote/exp/fe_sequence.rb +59 -0
  47. data/lib/ruote/exp/fe_set.rb +154 -0
  48. data/lib/ruote/exp/fe_subprocess.rb +211 -0
  49. data/lib/ruote/exp/fe_that.rb +92 -0
  50. data/lib/ruote/exp/fe_undo.rb +67 -0
  51. data/lib/ruote/exp/fe_unregisterp.rb +69 -0
  52. data/lib/ruote/exp/fe_wait.rb +95 -0
  53. data/lib/ruote/exp/flowexpression.rb +886 -0
  54. data/lib/ruote/exp/iterator.rb +81 -0
  55. data/lib/ruote/exp/merge.rb +118 -0
  56. data/lib/ruote/exp/ro_attributes.rb +212 -0
  57. data/lib/ruote/exp/ro_filters.rb +136 -0
  58. data/lib/ruote/exp/ro_persist.rb +154 -0
  59. data/lib/ruote/exp/ro_variables.rb +189 -0
  60. data/lib/ruote/exp/ro_vf.rb +68 -0
  61. data/lib/ruote/fei.rb +260 -0
  62. data/lib/ruote/id/mnemo_wfid_generator.rb +43 -0
  63. data/lib/ruote/id/wfid_generator.rb +81 -0
  64. data/lib/ruote/log/default_history.rb +122 -0
  65. data/lib/ruote/log/pretty.rb +176 -0
  66. data/lib/ruote/log/storage_history.rb +159 -0
  67. data/lib/ruote/log/test_logger.rb +208 -0
  68. data/lib/ruote/log/wait_logger.rb +64 -0
  69. data/lib/ruote/part/block_participant.rb +137 -0
  70. data/lib/ruote/part/code_participant.rb +81 -0
  71. data/lib/ruote/part/engine_participant.rb +189 -0
  72. data/lib/ruote/part/local_participant.rb +138 -0
  73. data/lib/ruote/part/no_op_participant.rb +60 -0
  74. data/lib/ruote/part/null_participant.rb +54 -0
  75. data/lib/ruote/part/rev_participant.rb +169 -0
  76. data/lib/ruote/part/smtp_participant.rb +116 -0
  77. data/lib/ruote/part/storage_participant.rb +392 -0
  78. data/lib/ruote/part/template.rb +84 -0
  79. data/lib/ruote/participant.rb +7 -0
  80. data/lib/ruote/reader.rb +278 -0
  81. data/lib/ruote/reader/json.rb +49 -0
  82. data/lib/ruote/reader/radial.rb +290 -0
  83. data/lib/ruote/reader/ruby_dsl.rb +186 -0
  84. data/lib/ruote/reader/xml.rb +99 -0
  85. data/lib/ruote/receiver/base.rb +212 -0
  86. data/lib/ruote/storage/base.rb +364 -0
  87. data/lib/ruote/storage/composite_storage.rb +121 -0
  88. data/lib/ruote/storage/fs_storage.rb +139 -0
  89. data/lib/ruote/storage/hash_storage.rb +211 -0
  90. data/lib/ruote/svc/dispatch_pool.rb +158 -0
  91. data/lib/ruote/svc/dollar_sub.rb +298 -0
  92. data/lib/ruote/svc/error_handler.rb +138 -0
  93. data/lib/ruote/svc/expression_map.rb +97 -0
  94. data/lib/ruote/svc/participant_list.rb +397 -0
  95. data/lib/ruote/svc/tracker.rb +172 -0
  96. data/lib/ruote/svc/treechecker.rb +141 -0
  97. data/lib/ruote/tree_dot.rb +85 -0
  98. data/lib/ruote/util/filter.rb +525 -0
  99. data/lib/ruote/util/hashdot.rb +79 -0
  100. data/lib/ruote/util/look.rb +128 -0
  101. data/lib/ruote/util/lookup.rb +127 -0
  102. data/lib/ruote/util/misc.rb +167 -0
  103. data/lib/ruote/util/ometa.rb +71 -0
  104. data/lib/ruote/util/serializer.rb +103 -0
  105. data/lib/ruote/util/subprocess.rb +88 -0
  106. data/lib/ruote/util/time.rb +100 -0
  107. data/lib/ruote/util/tree.rb +58 -0
  108. data/lib/ruote/version.rb +29 -0
  109. data/lib/ruote/worker.rb +386 -0
  110. data/lib/ruote/workitem.rb +394 -0
  111. data/phil.txt +14 -0
  112. data/ruote.gemspec +44 -0
  113. data/test/bm/ci.rb +55 -0
  114. data/test/bm/ici.rb +71 -0
  115. data/test/bm/juuman.rb +54 -0
  116. data/test/bm/launch_bench.rb +37 -0
  117. data/test/bm/load_26c.rb +97 -0
  118. data/test/bm/mega.rb +64 -0
  119. data/test/bm/seq_thousand.rb +31 -0
  120. data/test/bm/t.rb +35 -0
  121. data/test/functional/base.rb +247 -0
  122. data/test/functional/concurrent_base.rb +98 -0
  123. data/test/functional/crunner.rb +31 -0
  124. data/test/functional/ct_0_concurrence.rb +65 -0
  125. data/test/functional/ct_1_iterator.rb +67 -0
  126. data/test/functional/ct_2_cancel.rb +81 -0
  127. data/test/functional/eft_0_process_definition.rb +65 -0
  128. data/test/functional/eft_10_cancel_process.rb +46 -0
  129. data/test/functional/eft_11_wait.rb +109 -0
  130. data/test/functional/eft_12_listen.rb +500 -0
  131. data/test/functional/eft_13_iterator.rb +342 -0
  132. data/test/functional/eft_14_cursor.rb +456 -0
  133. data/test/functional/eft_15_loop.rb +69 -0
  134. data/test/functional/eft_16_if.rb +183 -0
  135. data/test/functional/eft_17_equals.rb +55 -0
  136. data/test/functional/eft_18_concurrent_iterator.rb +410 -0
  137. data/test/functional/eft_19_reserve.rb +136 -0
  138. data/test/functional/eft_1_echo.rb +68 -0
  139. data/test/functional/eft_20_save.rb +116 -0
  140. data/test/functional/eft_21_restore.rb +61 -0
  141. data/test/functional/eft_22_noop.rb +28 -0
  142. data/test/functional/eft_23_apply.rb +168 -0
  143. data/test/functional/eft_24_add_branches.rb +98 -0
  144. data/test/functional/eft_25_command.rb +28 -0
  145. data/test/functional/eft_26_error.rb +77 -0
  146. data/test/functional/eft_27_inc.rb +280 -0
  147. data/test/functional/eft_28_once.rb +135 -0
  148. data/test/functional/eft_29_cron.rb +64 -0
  149. data/test/functional/eft_2_sequence.rb +58 -0
  150. data/test/functional/eft_30_ref.rb +155 -0
  151. data/test/functional/eft_31_registerp.rb +130 -0
  152. data/test/functional/eft_32_lose.rb +93 -0
  153. data/test/functional/eft_33_let.rb +31 -0
  154. data/test/functional/eft_34_given.rb +123 -0
  155. data/test/functional/eft_35_filter.rb +375 -0
  156. data/test/functional/eft_36_read.rb +95 -0
  157. data/test/functional/eft_3_participant.rb +149 -0
  158. data/test/functional/eft_4_set.rb +296 -0
  159. data/test/functional/eft_5_subprocess.rb +163 -0
  160. data/test/functional/eft_6_concurrence.rb +304 -0
  161. data/test/functional/eft_7_forget.rb +61 -0
  162. data/test/functional/eft_8_undo.rb +114 -0
  163. data/test/functional/eft_9_redo.rb +138 -0
  164. data/test/functional/ft_0_worker.rb +65 -0
  165. data/test/functional/ft_10_dollar.rb +304 -0
  166. data/test/functional/ft_11_recursion.rb +109 -0
  167. data/test/functional/ft_12_launchitem.rb +43 -0
  168. data/test/functional/ft_13_variables.rb +151 -0
  169. data/test/functional/ft_14_re_apply.rb +324 -0
  170. data/test/functional/ft_15_timeout.rb +226 -0
  171. data/test/functional/ft_16_participant_params.rb +98 -0
  172. data/test/functional/ft_17_conditional.rb +102 -0
  173. data/test/functional/ft_18_kill.rb +138 -0
  174. data/test/functional/ft_19_participant_code.rb +67 -0
  175. data/test/functional/ft_1_process_status.rb +796 -0
  176. data/test/functional/ft_20_storage_participant.rb +543 -0
  177. data/test/functional/ft_21_forget.rb +153 -0
  178. data/test/functional/ft_22_process_definitions.rb +90 -0
  179. data/test/functional/ft_23_load_defs.rb +79 -0
  180. data/test/functional/ft_24_block_participant.rb +235 -0
  181. data/test/functional/ft_25_receiver.rb +207 -0
  182. data/test/functional/ft_26_participant_rtimeout.rb +179 -0
  183. data/test/functional/ft_27_var_indirection.rb +128 -0
  184. data/test/functional/ft_28_null_noop_participants.rb +51 -0
  185. data/test/functional/ft_29_part_template.rb +60 -0
  186. data/test/functional/ft_2_errors.rb +380 -0
  187. data/test/functional/ft_30_smtp_participant.rb +122 -0
  188. data/test/functional/ft_31_part_blocking.rb +72 -0
  189. data/test/functional/ft_33_participant_subprocess_priority.rb +32 -0
  190. data/test/functional/ft_34_cursor_rewind.rb +101 -0
  191. data/test/functional/ft_35_add_service.rb +56 -0
  192. data/test/functional/ft_36_storage_history.rb +150 -0
  193. data/test/functional/ft_37_default_history.rb +109 -0
  194. data/test/functional/ft_38_participant_more.rb +193 -0
  195. data/test/functional/ft_39_wait_for.rb +136 -0
  196. data/test/functional/ft_3_participant_registration.rb +574 -0
  197. data/test/functional/ft_40_wait_logger.rb +62 -0
  198. data/test/functional/ft_41_participants.rb +91 -0
  199. data/test/functional/ft_42_storage_copy.rb +71 -0
  200. data/test/functional/ft_43_participant_on_reply.rb +87 -0
  201. data/test/functional/ft_44_var_participant.rb +35 -0
  202. data/test/functional/ft_45_participant_accept.rb +64 -0
  203. data/test/functional/ft_46_launch_single.rb +83 -0
  204. data/test/functional/ft_47_wfid_generator.rb +54 -0
  205. data/test/functional/ft_48_lose.rb +112 -0
  206. data/test/functional/ft_49_engine_on_error.rb +201 -0
  207. data/test/functional/ft_4_cancel.rb +132 -0
  208. data/test/functional/ft_50_engine_config.rb +22 -0
  209. data/test/functional/ft_51_misc.rb +67 -0
  210. data/test/functional/ft_52_case.rb +134 -0
  211. data/test/functional/ft_53_engine_on_terminate.rb +95 -0
  212. data/test/functional/ft_54_patterns.rb +104 -0
  213. data/test/functional/ft_55_engine_participant.rb +303 -0
  214. data/test/functional/ft_56_filter_attribute.rb +259 -0
  215. data/test/functional/ft_57_rev_participant.rb +252 -0
  216. data/test/functional/ft_58_workitem.rb +69 -0
  217. data/test/functional/ft_59_pause.rb +343 -0
  218. data/test/functional/ft_5_on_error.rb +384 -0
  219. data/test/functional/ft_60_code_participant.rb +45 -0
  220. data/test/functional/ft_61_trailing_fields.rb +34 -0
  221. data/test/functional/ft_62_exp_name_and_dollar_substitution.rb +35 -0
  222. data/test/functional/ft_6_on_cancel.rb +221 -0
  223. data/test/functional/ft_7_tags.rb +177 -0
  224. data/test/functional/ft_8_participant_consumption.rb +124 -0
  225. data/test/functional/ft_9_subprocesses.rb +146 -0
  226. data/test/functional/restart_base.rb +34 -0
  227. data/test/functional/rt_0_wait.rb +55 -0
  228. data/test/functional/rt_1_listen.rb +56 -0
  229. data/test/functional/rt_2_errors.rb +56 -0
  230. data/test/functional/rt_3_once.rb +70 -0
  231. data/test/functional/rt_4_cron.rb +64 -0
  232. data/test/functional/rt_5_timeout.rb +60 -0
  233. data/test/functional/rtest.rb +8 -0
  234. data/test/functional/storage_helper.rb +93 -0
  235. data/test/functional/test.rb +44 -0
  236. data/test/functional/vertical.rb +46 -0
  237. data/test/path_helper.rb +15 -0
  238. data/test/test.rb +13 -0
  239. data/test/test_helper.rb +28 -0
  240. data/test/unit/storage.rb +428 -0
  241. data/test/unit/storages.rb +37 -0
  242. data/test/unit/test.rb +28 -0
  243. data/test/unit/ut_0_ruby_reader.rb +223 -0
  244. data/test/unit/ut_11_lookup.rb +122 -0
  245. data/test/unit/ut_13_serializer.rb +65 -0
  246. data/test/unit/ut_14_is_uri.rb +28 -0
  247. data/test/unit/ut_15_util.rb +57 -0
  248. data/test/unit/ut_16_reader.rb +225 -0
  249. data/test/unit/ut_18_engine.rb +47 -0
  250. data/test/unit/ut_19_part_template.rb +86 -0
  251. data/test/unit/ut_1_fei.rb +165 -0
  252. data/test/unit/ut_20_composite_storage.rb +74 -0
  253. data/test/unit/ut_21_svc_participant_list.rb +46 -0
  254. data/test/unit/ut_22_filter.rb +1094 -0
  255. data/test/unit/ut_23_svc_tracker.rb +48 -0
  256. data/test/unit/ut_24_radial_reader.rb +332 -0
  257. data/test/unit/ut_25_merge.rb +113 -0
  258. data/test/unit/ut_3_wait_logger.rb +39 -0
  259. data/test/unit/ut_4_expmap.rb +20 -0
  260. data/test/unit/ut_5_tree.rb +54 -0
  261. data/test/unit/ut_6_condition.rb +303 -0
  262. data/test/unit/ut_7_workitem.rb +99 -0
  263. data/test/unit/ut_8_tree_to_dot.rb +72 -0
  264. data/test/unit/ut_9_xml_reader.rb +61 -0
  265. metadata +504 -0
@@ -0,0 +1,116 @@
1
+ #--
2
+ # Copyright (c) 2005-2011, Alain Hoang and John Mettraux.
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
+ require 'net/smtp'
26
+
27
+ require 'ruote/participant'
28
+ require 'ruote/part/template'
29
+
30
+
31
+ module Ruote
32
+
33
+ #
34
+ # A very stupid SMTP participant.
35
+ #
36
+ # == options
37
+ #
38
+ # * :server - the IP address or hostname of the SMTP server/gateway (defaults to '127.0.0.1')
39
+ # * :port - the port of the SMTP server/gateway (defaults to 25)
40
+ # * :from - the from mail address (mandatory)
41
+ # * :to - the to mail address[es]
42
+ # * :template - a String template for the mail message
43
+ # * :notification - when set to true, the flow will resume immediately after having sent the email
44
+ #
45
+ #
46
+ # == :template
47
+ #
48
+ # @engine.register_participant(
49
+ # :no_good_notification,
50
+ # Ruote::SmtpParticipant,
51
+ # :server => 'smtp.example.com'
52
+ # :port => 25,
53
+ # :to => 'toto@example.com',
54
+ # :from => 'john@example.com',
55
+ # :notification => true,
56
+ # :template => "Subject: ${f:email_subject}\n\nno good.")
57
+ #
58
+ # Process variable / workitem field substitution works the same as in
59
+ # process definitions (in this example, the workitem field email_subject will
60
+ # be used as the subject of the email...)
61
+ #
62
+ #
63
+ # == :to or workitem.fields['email_target']
64
+ #
65
+ # The target of the email is either given via the workitem field
66
+ # 'email_target', either by the option :to. The workitem field takes
67
+ # precedence if both are present.
68
+ #
69
+ # This parameter/option may be either a single (string) email address, either
70
+ # an array of (string) email addresses.
71
+ #
72
+ #
73
+ # == final note : mail listener
74
+ #
75
+ # This participant cannot read POP/IMAP accounts for you. You have to
76
+ # use a mail listener or get a web reply by placing a link in the message...
77
+ #
78
+ class SmtpParticipant
79
+
80
+ include LocalParticipant
81
+ include TemplateMixin
82
+
83
+ def initialize(opts)
84
+
85
+ @opts = Ruote.keys_to_s(opts)
86
+ end
87
+
88
+ def consume(workitem)
89
+
90
+ to = workitem.fields['email_target'] || @opts['to']
91
+ to = Array(to)
92
+
93
+ text = render_template(
94
+ @opts['template'],
95
+ Ruote::Exp::FlowExpression.fetch(@context, workitem.fei.to_h),
96
+ workitem)
97
+
98
+ server = @opts['server'] || '127.0.0.1'
99
+ port = @opts['port'] || 25
100
+
101
+ Net::SMTP.start(server, port) do |smtp|
102
+ smtp.send_message(text, @opts['from'] || 'ruote@example.org', *to)
103
+ end
104
+
105
+ reply_to_engine(workitem) if @opts['notification']
106
+ end
107
+
108
+ def cancel(fei, flavour)
109
+
110
+ # does nothing
111
+ #
112
+ # one variant could send a "cancellation email"
113
+ end
114
+ end
115
+ end
116
+
@@ -0,0 +1,392 @@
1
+ #--
2
+ # Copyright (c) 2005-2011, 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
+ require 'ruote/part/local_participant'
26
+
27
+
28
+ module Ruote
29
+
30
+ #
31
+ # A participant that stores the workitem in the same storage used by the
32
+ # engine and the worker(s).
33
+ #
34
+ # part = engine.register_participant 'alfred', Ruote::StorageParticipant
35
+ #
36
+ # # ... a bit later
37
+ #
38
+ # puts "workitems still open : "
39
+ # part.each do |workitem|
40
+ # puts "#{workitem.fei.wfid} - #{workitem.fields['params']['task']}"
41
+ # end
42
+ #
43
+ # # ... when done with a workitem
44
+ #
45
+ # part.reply(workitem)
46
+ # # this will remove the workitem from the storage and hand it back
47
+ # # to the engine
48
+ #
49
+ # Does not thread by default (the engine will not spawn a dedicated thread
50
+ # to handle the delivery to this participant, the workitem will get stored
51
+ # via the main engine thread and basta).
52
+ #
53
+ class StorageParticipant
54
+
55
+ include LocalParticipant
56
+ include Enumerable
57
+
58
+ def initialize(engine_or_options={}, options=nil)
59
+
60
+ if engine_or_options.respond_to?(:context)
61
+ @context = engine_or_options.context
62
+ elsif engine_or_options.is_a?(Ruote::Context)
63
+ @context = engine_or_options
64
+ else
65
+ @options = engine_or_options
66
+ end
67
+
68
+ @options ||= {}
69
+
70
+ @store_name = @options['store_name']
71
+ end
72
+
73
+ # No need for a separate thread when delivering to this participant.
74
+ #
75
+ def do_not_thread; true; end
76
+
77
+ # This is the method called by ruote when passing a workitem to
78
+ # this participant.
79
+ #
80
+ def consume(workitem)
81
+
82
+ doc = workitem.to_h
83
+
84
+ doc.merge!(
85
+ 'type' => 'workitems',
86
+ '_id' => to_id(doc['fei']),
87
+ 'participant_name' => doc['participant_name'],
88
+ 'wfid' => doc['fei']['wfid'])
89
+
90
+ doc['store_name'] = @store_name if @store_name
91
+
92
+ @context.storage.put(doc)
93
+ end
94
+
95
+ # Though #update is an alias to #consume, it is meant to be used by
96
+ # client code when "saving" a workitem (fields may have changed). Calling
97
+ # update won't proceed the workitem.
98
+ #
99
+ alias update consume
100
+
101
+ # Removes the document/workitem from the storage
102
+ #
103
+ def cancel(fei, flavour)
104
+
105
+ doc = fetch(fei)
106
+
107
+ r = @context.storage.delete(doc)
108
+
109
+ cancel(fei, flavour) if r != nil
110
+ end
111
+
112
+ # Given a fei (or its string version, a sid), returns the corresponding
113
+ # workitem (or nil).
114
+ #
115
+ def [](fei)
116
+
117
+ doc = fetch(fei)
118
+
119
+ doc ? Ruote::Workitem.new(doc) : nil
120
+ end
121
+
122
+ alias by_fei []
123
+
124
+ # Removes the workitem from the storage and replies to the engine.
125
+ #
126
+ # TODO : should it raise if the workitem can't be found ?
127
+ # TODO : should it accept just the fei ?
128
+ #
129
+ def proceed(workitem)
130
+
131
+ doc = fetch(Ruote::FlowExpressionId.extract_h(workitem))
132
+
133
+ r = @context.storage.delete(doc)
134
+
135
+ raise ArgumentError.new('cannot proceed, workitem is gone') if r == true
136
+ return proceed(workitem) if r != nil
137
+
138
+ workitem.h.delete('_rev')
139
+
140
+ reply_to_engine(workitem)
141
+ end
142
+
143
+ # (soon to be removed)
144
+ #
145
+ def reply(workitem)
146
+
147
+ puts '-' * 80
148
+ puts '*** WARNING : please use the Ruote::StorageParticipant#proceed(wi)'
149
+ puts ' instead of #reply(wi) which is deprecated'
150
+ #caller.each { |l| puts l }
151
+ puts '-' * 80
152
+
153
+ proceed(workitem)
154
+ end
155
+
156
+ # Returns the count of workitems stored in this participant.
157
+ #
158
+ def size
159
+
160
+ fetch_all(:count => true)
161
+ end
162
+
163
+ # Iterates over the workitems stored in here.
164
+ #
165
+ def each(&block)
166
+
167
+ all.each { |wi| block.call(wi) }
168
+ end
169
+
170
+ # Returns all the workitems stored in here.
171
+ #
172
+ def all(opts={})
173
+
174
+ res = fetch_all(opts)
175
+
176
+ res.is_a?(Array) ? res.map { |hwi| Ruote::Workitem.new(hwi) } : res
177
+ end
178
+
179
+ # A convenience method (especially when testing), returns the first
180
+ # (only ?) workitem in the participant.
181
+ #
182
+ def first
183
+
184
+ wi(fetch_all({}).first)
185
+ end
186
+
187
+ # Return all workitems for the specified wfid
188
+ #
189
+ def by_wfid(wfid, opts={})
190
+
191
+ if @context.storage.respond_to?(:by_wfid)
192
+ return @context.storage.by_wfid('workitems', wfid, opts)
193
+ end
194
+
195
+ wis(@context.storage.get_many('workitems', wfid, opts))
196
+ end
197
+
198
+ # Returns all workitems for the specified participant name
199
+ #
200
+ def by_participant(participant_name, opts={})
201
+
202
+ return @context.storage.by_participant(
203
+ 'workitems', participant_name, opts
204
+ ) if @context.storage.respond_to?(:by_participant)
205
+
206
+ do_select(opts) do |hwi|
207
+ hwi['participant_name'] == participant_name
208
+ end
209
+ end
210
+
211
+ # field : returns all the workitems with the given field name present.
212
+ #
213
+ # field and value : returns all the workitems with the given field name
214
+ # and the given value for that field.
215
+ #
216
+ # Warning : only some storages are optimized for such queries (like
217
+ # CouchStorage), the others will load all the workitems and then filter
218
+ # them.
219
+ #
220
+ def by_field(field, value=nil, opts={})
221
+
222
+ (value, opts = nil, value) if value.is_a?(Hash)
223
+
224
+ if @context.storage.respond_to?(:by_field)
225
+ return @context.storage.by_field('workitems', field, value, opts)
226
+ end
227
+
228
+ do_select(opts) do |hwi|
229
+ hwi['fields'].keys.include?(field) &&
230
+ (value.nil? || hwi['fields'][field] == value)
231
+ end
232
+ end
233
+
234
+ # Queries the store participant for workitems.
235
+ #
236
+ # Some examples :
237
+ #
238
+ # part.query(:wfid => @wfid).size
239
+ # part.query('place' => 'nara').size
240
+ # part.query('place' => 'heiankyou').size
241
+ # part.query(:wfid => @wfid, :place => 'heiankyou').size
242
+ #
243
+ # There are two 'reserved' criterion : 'wfid' and 'participant'
244
+ # ('participant_name' as well). The rest of the criteria are considered
245
+ # constraints for fields.
246
+ #
247
+ # 'offset' and 'limit' are reserved as well. They should prove useful
248
+ # for pagination. 'skip' can be used instead of 'offset'.
249
+ #
250
+ # Note : the criteria is AND only, you'll have to do ORs (aggregation)
251
+ # by yourself.
252
+ #
253
+ def query(criteria)
254
+
255
+ cr = Ruote.keys_to_s(criteria)
256
+
257
+ if @context.storage.respond_to?(:query_workitems)
258
+ return @context.storage.query_workitems(cr)
259
+ end
260
+
261
+ opts = {}
262
+ opts[:skip] = cr.delete('offset') || cr.delete('skip')
263
+ opts[:limit] = cr.delete('limit')
264
+ opts[:count] = cr.delete('count')
265
+
266
+ wfid = cr.delete('wfid')
267
+
268
+ count = opts[:count]
269
+
270
+ pname = cr.delete('participant_name') || cr.delete('participant')
271
+ opts.delete(:count) if pname
272
+
273
+ hwis = wfid ?
274
+ @context.storage.get_many('workitems', wfid, opts) : fetch_all(opts)
275
+
276
+ return hwis unless hwis.is_a?(Array)
277
+
278
+ hwis = hwis.select { |hwi|
279
+ Ruote::StorageParticipant.matches?(hwi, pname, cr)
280
+ }
281
+
282
+ count ? hwis.size : wis(hwis)
283
+ end
284
+
285
+ # Cleans this participant out completely
286
+ #
287
+ def purge!
288
+
289
+ fetch_all({}).each { |hwi| @context.storage.delete(hwi) }
290
+ end
291
+
292
+ # Used by #query when filtering workitems.
293
+ #
294
+ def self.matches?(hwi, pname, criteria)
295
+
296
+ return false if pname && hwi['participant_name'] != pname
297
+
298
+ fields = hwi['fields']
299
+
300
+ criteria.each do |fname, fvalue|
301
+ return false if fields[fname] != fvalue
302
+ end
303
+
304
+ true
305
+ end
306
+
307
+ # Mostly a test method. Returns a Hash were keys are participant names
308
+ # and values are lists of workitems.
309
+ #
310
+ def per_participant
311
+
312
+ inject({}) { |h, wi| (h[wi.participant_name] ||= []) << wi; h }
313
+ end
314
+
315
+ # Mostly a test method. Returns a Hash were keys are participant names
316
+ # and values are integers, the count of workitems for a given participant
317
+ # name.
318
+ #
319
+ def per_participant_count
320
+
321
+ per_participant.inject({}) { |h, (k, v)| h[k] = v.size; h }
322
+ end
323
+
324
+ protected
325
+
326
+ # Fetches a workitem in its raw form (Hash).
327
+ #
328
+ def fetch(fei)
329
+
330
+ hfei = Ruote::FlowExpressionId.extract_h(fei)
331
+
332
+ @context.storage.get('workitems', to_id(hfei))
333
+ end
334
+
335
+ # Fetches all the workitems. If there is a @store_name, will only fetch
336
+ # the workitems in that store.
337
+ #
338
+ def fetch_all(opts)
339
+
340
+ @context.storage.get_many(
341
+ 'workitems',
342
+ @store_name ? /^wi!#{@store_name}::/ : nil,
343
+ opts)
344
+ end
345
+
346
+ # Given a few options and a block, returns all the workitems that match
347
+ # the block
348
+ #
349
+ def do_select(opts, &block)
350
+
351
+ skip = opts[:offset] || opts[:skip]
352
+ limit = opts[:limit]
353
+ count = opts[:count]
354
+
355
+ hwis = fetch_all({})
356
+ hwis = hwis.select(&block)
357
+
358
+ hwis = hwis[skip..-1] if skip
359
+ hwis = hwis[0, limit] if limit
360
+
361
+ return hwis.size if count
362
+
363
+ hwis.collect { |hwi| Ruote::Workitem.new(hwi) }
364
+ end
365
+
366
+ # Computes the id for the document representing the document in the storage.
367
+ #
368
+ def to_id(fei)
369
+
370
+ a = [ Ruote.to_storage_id(fei) ]
371
+
372
+ a.unshift(@store_name) if @store_name
373
+
374
+ a.unshift('wi')
375
+
376
+ a.join('!')
377
+ end
378
+
379
+ def wi(hwi)
380
+
381
+ hwi ? Ruote::Workitem.new(hwi) : nil
382
+ end
383
+
384
+ def wis(workitems_or_count)
385
+
386
+ workitems_or_count.is_a?(Array) ?
387
+ workitems_or_count.collect { |wi| Ruote::Workitem.new(wi) } :
388
+ workitems_or_count
389
+ end
390
+ end
391
+ end
392
+