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,219 @@
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
+
26
+ require 'ruote/exp/fe_concurrence'
27
+ require 'ruote/exp/iterator'
28
+
29
+
30
+ module Ruote::Exp
31
+
32
+ #
33
+ # This expression is a cross between 'concurrence' and 'iterator'.
34
+ #
35
+ # Please look at the documentation of 'iterator' to learn more about the
36
+ # common options between 'iterator' and 'concurrent-iterator'.
37
+ #
38
+ # pdef = Ruote.process_definition :name => 'test' do
39
+ # concurrent_iterator :on_val => 'alice, bob, charly', :to_var => 'v' do
40
+ # participant '${v:v}'
41
+ # end
42
+ # end
43
+ #
44
+ # will be equivalent to
45
+ #
46
+ # pdef = Ruote.process_definition :name => 'test' do
47
+ # concurrence do
48
+ # participant 'alice'
49
+ # participant 'bob'
50
+ # participant 'charly'
51
+ # end
52
+ # end
53
+ #
54
+ # The 'on' and the 'to' attributes follow exactly the ones for the iterator
55
+ # expression.
56
+ #
57
+ # When there is no 'to', the current iterated value is placed in the variable
58
+ # named 'i'.
59
+ #
60
+ # The variable named 'ii' contains the current iterated index (an int bigger
61
+ # or egal to 0).
62
+ #
63
+ # 'concurrent_iterator' does not understand commands like
64
+ # rewind/break/jump/... like 'iterator' does, since it fires all its
65
+ # branches when applied.
66
+ #
67
+ # == :times and :branches
68
+ #
69
+ # Similarly to the iterator expression, the :times or the :branches attribute
70
+ # may be used in stead of the 'on' attribute.
71
+ #
72
+ # pdef = Ruote.process_definition :name => 'test' do
73
+ # concurrent_iterator :times => 3 do
74
+ # participant 'user${v:i}'
75
+ # end
76
+ # end
77
+ #
78
+ # is equivalent to
79
+ #
80
+ # pdef = Ruote.process_definition :name => 'test' do
81
+ # concurrence do
82
+ # participant 'user0'
83
+ # participant 'user1'
84
+ # participant 'user2'
85
+ # end
86
+ # end
87
+ #
88
+ #
89
+ # == options
90
+ #
91
+ # the concurrent_iterator accepts the same options for merging as its bigger
92
+ # brother, the concurrence expression.
93
+ #
94
+ # :count, :merge (override, mix, isolate), remaining (cancel, forget) and
95
+ # :over.
96
+ #
97
+ #
98
+ # == add branches
99
+ #
100
+ # The 'add_branches'/'add_branch' expression can be used to add branches
101
+ # to a concurrent-iterator while it is running.
102
+ #
103
+ # concurrent_iterator :on => 'a, b, c' do
104
+ # sequence do
105
+ # participant :ref => 'worker_${v:i}'
106
+ # add_branches 'd, e', :if => '${v:/not_sufficient}'
107
+ # end
108
+ # end
109
+ #
110
+ # In this example, if the process level variable 'not_sufficient' is set to
111
+ # true, workers d and e will be added to the iterated elements.
112
+ #
113
+ # Read more at the 'add_branches' expression description.
114
+ #
115
+ #
116
+ # == 'citerator'
117
+ #
118
+ # 'citerator' is an alias for 'concurrent_iterator'.
119
+ #
120
+ # pdef = Ruote.process_definition :name => 'test' do
121
+ # citerator :on_val => 'alice, bob, charly', :to_var => 'v' do
122
+ # participant '${v:v}'
123
+ # end
124
+ # end
125
+ #
126
+ class ConcurrentIteratorExpression < ConcurrenceExpression
127
+
128
+ include IteratorMixin
129
+
130
+ names :concurrent_iterator, :citerator
131
+
132
+ ADD_BRANCHES_FIELD = '__add_branches__'
133
+
134
+ # Overrides FlowExpression#register_child to make sure that persist is
135
+ # not called.
136
+ #
137
+ def register_child(fei)
138
+
139
+ h.children << fei
140
+ end
141
+
142
+ def add_branches(list)
143
+
144
+ if h.times_iterator && list.size == 1
145
+
146
+ count = (list.first.to_i rescue nil)
147
+
148
+ list = (h.list_size + 1..h.list_size + count) if count
149
+ end
150
+
151
+ list.each do |val|
152
+
153
+ h.list_size += 1
154
+
155
+ workitem = Ruote.fulldup(h.applied_workitem)
156
+ #workitem = Rufus::Json.dup(h.applied_workitem)
157
+
158
+ variables = { 'ii' => h.list_size - 1 }
159
+
160
+ if h.to_v
161
+ variables[h.to_v] = val
162
+ else #if to_f
163
+ workitem['fields'][h.to_f] = val
164
+ end
165
+
166
+ launch_sub(
167
+ "#{h.fei['expid']}_0",
168
+ tree_children[0],
169
+ :workitem => workitem,
170
+ :variables => variables)
171
+ end
172
+ end
173
+
174
+ def reply(workitem)
175
+
176
+ if ab = workitem['fields'].delete(ADD_BRANCHES_FIELD)
177
+
178
+ add_branches(ab)
179
+
180
+ if h.fei['wfid'] != workitem['fei']['wfid'] ||
181
+ ( ! workitem['fei']['expid'].match(/^#{h.fei['expid']}_\d+$/))
182
+
183
+ do_persist
184
+ return
185
+ end
186
+ end
187
+
188
+ super(workitem)
189
+ end
190
+
191
+ protected
192
+
193
+ def apply_children
194
+
195
+ return reply_to_parent(h.applied_workitem) unless tree_children[0]
196
+
197
+ list = determine_list
198
+
199
+ return reply_to_parent(h.applied_workitem) if list.empty?
200
+
201
+ h.to_v, h.to_f = determine_tos
202
+ h.to_v = 'i' if h.to_v.nil? && h.to_f.nil?
203
+
204
+ h.list_size = 0
205
+
206
+ add_branches(list)
207
+
208
+ persist_or_raise
209
+ end
210
+
211
+ # Overrides the implementation found in ConcurrenceExpression
212
+ #
213
+ def expected_count
214
+
215
+ h.ccount ? [ h.ccount, h.list_size ].min : h.list_size
216
+ end
217
+ end
218
+ end
219
+
@@ -0,0 +1,141 @@
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
+
26
+ module Ruote::Exp
27
+
28
+ #
29
+ # This expression executes its children expression according to a cron
30
+ # schedule or at a given frequency.
31
+ #
32
+ # cron '15 4 * * sun' do # every sunday at 0415
33
+ # subprocess :ref => 'refill_the_acid_baths'
34
+ # end
35
+ #
36
+ # or
37
+ #
38
+ # every '10m' do
39
+ # send_reminder # subprocess or participant
40
+ # end
41
+ #
42
+ # The 'tab' or 'interval' attributes may be used, this is a bit more verbose,
43
+ # but, for instance, in XML, it is quite necessary :
44
+ #
45
+ # <cron tab="15 4 * * sun">
46
+ # <subprocess ref="refill_the_acid_baths" />
47
+ # <cron>
48
+ #
49
+ # Triggered children subprocesses are 'forgotten'. This implies they
50
+ # will never reply to the cron/every expression and they won't get cancelled
51
+ # when the cron/every expression gets cancelled (the cron/every schedule
52
+ # gets cancelled though, no new children will get cancelled).
53
+ #
54
+ # "man 5 crontab" in the command line of your favourite unix system might
55
+ # help you with the semantics of the string expected by the cron expression.
56
+ #
57
+ #
58
+ # == an example use case
59
+ #
60
+ # The cron/every expression appears often in scenarii like :
61
+ #
62
+ # concurrence :count => 1 do
63
+ #
64
+ # participant 'operator'
65
+ #
66
+ # cron '0 9 * * 1-5' do # send a reminder every weekday at 0900
67
+ # notify 'operator'
68
+ # end
69
+ # end
70
+ #
71
+ # With a subprocess, this could become a bit more reusable :
72
+ #
73
+ # Ruote.process_defintion :name => 'sample' do
74
+ #
75
+ # sequence do
76
+ # with_reminder :participant => 'operator1'
77
+ # with_reminder :participant => 'operator2'
78
+ # end
79
+ #
80
+ # define 'with_reminder' do
81
+ # concurrence :count => 1 do
82
+ # participant '${v:participant}'
83
+ # cron '0 9 * * 1-5' do # send a reminder every weekday at 0900
84
+ # notify '${v:participant}'
85
+ # end
86
+ # end
87
+ # end
88
+ # end
89
+ #
90
+ class CronExpression < FlowExpression
91
+
92
+ names :cron, :every
93
+
94
+ def apply
95
+
96
+ h.schedule = attribute(:tab) || attribute(:interval) || attribute_text
97
+ h.job_id = nil
98
+
99
+ reschedule
100
+ end
101
+
102
+ def reply(workitem)
103
+
104
+ launch_sub(
105
+ "#{h.fei['expid']}_0",
106
+ tree_children[0],
107
+ :workitem => Ruote.fulldup(h.applied_workitem),
108
+ :forget => true)
109
+
110
+ reschedule
111
+ end
112
+
113
+ def cancel(flavour)
114
+
115
+ @context.storage.delete_schedule(h.job_id)
116
+ reply_to_parent(h.applied_workitem)
117
+ end
118
+
119
+ # Note : this method has to be public.
120
+ #
121
+ def reschedule
122
+
123
+ h.job_id = @context.storage.put_schedule(
124
+ 'cron',
125
+ h.fei,
126
+ h.schedule,
127
+ 'action' => 'reply',
128
+ 'fei' => h.fei,
129
+ 'workitem' => h.applied_workitem)
130
+
131
+ @context.storage.delete_schedule(h.job_id) if try_persist
132
+ #
133
+ # if the persist failed, immediately unschedule
134
+ # the just scheduled job
135
+ #
136
+ # this is meant to cope with cases where one worker reschedules
137
+ # while another just cancelled
138
+ end
139
+ end
140
+ end
141
+
@@ -0,0 +1,324 @@
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
+
26
+ require 'ruote/exp/commanded'
27
+
28
+
29
+ module Ruote::Exp
30
+
31
+ #
32
+ # This class implements the 'cursor' and the 'repeat' (loop) expressions.
33
+ #
34
+ # The cursor expression is a kind of enhanced 'sequence'. Like a sequence
35
+ # it will execute its child expression one by one, sequentially. Unlike a
36
+ # sequence though, it will obey 'commands'.
37
+ #
38
+ # cursor do
39
+ # author
40
+ # reviewer
41
+ # rewind :if => '${f:not_ok}'
42
+ # publisher
43
+ # end
44
+ #
45
+ # In this simplistic example, the process will flow from author to reviewer
46
+ # and back until the reviewer sets the workitem field 'not_ok' to something
47
+ # else than the value 'true'.
48
+ #
49
+ # There are two ways to pass commands to a cursor either directly from
50
+ # the process definition with a cursor command expression, either via
51
+ # the workitem '__command__' [special] field.
52
+ #
53
+ # == cursor commands
54
+ #
55
+ # The commands that a cursor understands are listed here. The most powerful
56
+ # ones are 'rewind' and 'jump'.
57
+ #
58
+ # === rewind
59
+ #
60
+ # Rewinds the cursor up to its first child expression.
61
+ #
62
+ # cursor do
63
+ # author
64
+ # reviewer
65
+ # rewind :if => '${f:not_ok}'
66
+ # publisher
67
+ # end
68
+ #
69
+ # === reset
70
+ #
71
+ # Whereas 'rewind' places the cursor back to the initial step with the current
72
+ # workitem, 'reset' will rewind it and start again but with the workitem
73
+ # as it was when it reached the cursor/repeat.
74
+ #
75
+ # === stop, over & break
76
+ #
77
+ # Exits the cursor.
78
+ #
79
+ # cursor do
80
+ # author
81
+ # reviewer
82
+ # rewind :if => '${f:review} == fix'
83
+ # stop :if => '${f:review} == abort'
84
+ # publisher
85
+ # end
86
+ #
87
+ # '_break' or 'over' can be used instead of 'stop'.
88
+ #
89
+ # === skip & back
90
+ #
91
+ # Those two commands jump forth and back respectively. By default, they
92
+ # skip 1 child, but they accept a numeric parameter holding the number
93
+ # of children to skip.
94
+ #
95
+ # cursor do
96
+ # author
97
+ # reviewer
98
+ # rewind :if => '${f:review} == fix'
99
+ # skip 2 :if => '${f:review} == publish'
100
+ # reviewer2
101
+ # rewind :if => '${f:review} == fix'
102
+ # publisher
103
+ # end
104
+ #
105
+ # === jump
106
+ #
107
+ # Jump is probably the most powerful of the cursor commands. It allows to
108
+ # jump to a specified expression that is a direct child of the cursor.
109
+ #
110
+ # cursor do
111
+ # author
112
+ # reviewer
113
+ # jump :to => 'author', :if => '${f:review} == fix'
114
+ # jump :to => 'publisher', :if => '${f:review} == publish'
115
+ # reviewer2
116
+ # jump :to => 'author', :if => '${f:review} == fix'
117
+ # publisher
118
+ # end
119
+ #
120
+ # Note that the :to accepts the name of an expression or the value of
121
+ # its :ref attribute or the value of its :tag attribute.
122
+ #
123
+ # cursor do
124
+ # participant :ref => 'author'
125
+ # participant :ref => 'reviewer'
126
+ # jump :to => 'author', :if => '${f:review} == fix'
127
+ # participant :ref => 'publisher'
128
+ # end
129
+ #
130
+ # == cursor command with :ref
131
+ #
132
+ # It's OK to tag a cursor/repeat/loop with the :tag attribute and then
133
+ # point a command to it via :ref :
134
+ #
135
+ # concurrence do
136
+ #
137
+ # cursor :tag => 'main' do
138
+ # author
139
+ # editor
140
+ # publisher
141
+ # end
142
+ #
143
+ # # meanwhile ...
144
+ #
145
+ # sequence do
146
+ # sponsor
147
+ # rewind :ref => 'main', :if => '${f:stop}'
148
+ # end
149
+ # end
150
+ #
151
+ # This :ref technique may also be used with nested cursor/loop/iterator
152
+ # constructs :
153
+ #
154
+ # cursor :tag => 'main' do
155
+ # cursor do
156
+ # author
157
+ # editor
158
+ # rewind :if => '${f:not_ok}'
159
+ # _break :ref => 'main', :if => '${f:abort_everything}'
160
+ # end
161
+ # head_of_edition
162
+ # rewind :if => '${f:not_ok}'
163
+ # publisher
164
+ # end
165
+ #
166
+ # this example features two nested cursors. There is a "_break" in the inner
167
+ # cursor, but it will break the main 'cursor' (and thus break the whole
168
+ # review process).
169
+ #
170
+ # == cursor command in the workitem
171
+ #
172
+ # The command expressions are merely setting the workitem field '__command__'
173
+ # with an array value [ {command}, {arg} ].
174
+ #
175
+ # For example,
176
+ #
177
+ # jump :to => 'author'
178
+ # # is equivalent to
179
+ # set 'field:__command__' => 'author'
180
+ #
181
+ # It is entirely OK to have a participant implementation that sets __command__
182
+ # by itself.
183
+ #
184
+ # class Reviewer
185
+ # include Ruote::LocalParticipant
186
+ #
187
+ # def consume(workitem)
188
+ # # somehow review the book
189
+ # if review == 'bad'
190
+ # #workitem.fields['__command__'] = [ 'rewind' ] # old style
191
+ # workitem.command = 'rewind' # new style
192
+ # else
193
+ # # let it go
194
+ # end
195
+ # reply_to_engine(workitem)
196
+ # end
197
+ #
198
+ # def cancel(fei, flavour)
199
+ # # cancel if review is still going on...
200
+ # end
201
+ # end
202
+ #
203
+ # This example uses the Ruote::Workitem#command= method which can be fed
204
+ # strings like 'rewind', 'skip 2', 'jump to author' or the equivalent arrays
205
+ # [ 'rewind' ], [ 'skip', 2 ], [ 'jump', 'author' ].
206
+ #
207
+ #
208
+ # == :break_if / :rewind_if
209
+ #
210
+ # As an attribute of the cursor/repeat expression, you can set a :break_if.
211
+ # It tells the cursor (loop) if it has to break.
212
+ #
213
+ # cursor :break_if => '${f:completed}' do
214
+ # participant 'alpha'
215
+ # participant 'bravo'
216
+ # participant 'charly'
217
+ # end
218
+ #
219
+ # If alpha or bravo replies and the field 'completed' is set to true, this
220
+ # cursor will break.
221
+ #
222
+ # :break_unless is accepted. :over_if and :over_unless are synonyms for
223
+ # :break_if and :break_unless respectively.
224
+ #
225
+ # :rewind_if / :rewind_unless behave the same, but the cursor/loop, instead
226
+ # of breaking, is put back in its first step.
227
+ #
228
+ #
229
+ # = repeat (loop)
230
+ #
231
+ # A 'cursor' expression exits implicitely as soon as its last child replies
232
+ # to it.
233
+ # a 'repeat' expression will apply (again) the first child after the last
234
+ # child replied. A 'break' cursor command might be necessary to exit the loop
235
+ # (or a cancel_process, but that exits the whole process instance).
236
+ #
237
+ # sequence do
238
+ # repeat do
239
+ # author
240
+ # reviewer
241
+ # _break :if => '${f:review} == ok'
242
+ # end
243
+ # publisher
244
+ # end
245
+ #
246
+ class CursorExpression < CommandedExpression
247
+
248
+ names :cursor, :loop, :repeat
249
+
250
+ def apply
251
+
252
+ move_on
253
+ end
254
+
255
+ protected
256
+
257
+ # Determines which child expression of the cursor is to be applied next.
258
+ #
259
+ def move_on(workitem=h.applied_workitem)
260
+
261
+ position = workitem['fei'] == h.fei ?
262
+ -1 : Ruote::FlowExpressionId.child_id(workitem['fei'])
263
+
264
+ position += 1
265
+
266
+ com, arg = get_command(workitem)
267
+
268
+ return reply_to_parent(workitem) if com == 'break'
269
+
270
+ case com
271
+ when 'rewind', 'continue', 'reset' then position = 0
272
+ when 'skip' then position += arg
273
+ when 'jump' then position = jump_to(workitem, position, arg)
274
+ end
275
+
276
+ position = 0 if position >= tree_children.size && is_loop?
277
+
278
+ if position < tree_children.size
279
+
280
+ workitem = h.applied_workitem if com == 'reset'
281
+ apply_child(position, workitem)
282
+
283
+ else
284
+
285
+ reply_to_parent(workitem)
286
+ end
287
+ end
288
+
289
+ # Will return true if this instance is about a 'loop' or a 'repeat'.
290
+ #
291
+ def is_loop?
292
+
293
+ name == 'loop' || name == 'repeat'
294
+ end
295
+
296
+ # Jumps to an integer position, or the name of an expression
297
+ # or a tag name of a ref name.
298
+ #
299
+ def jump_to(workitem, position, arg)
300
+
301
+ pos = Integer(arg) rescue nil
302
+
303
+ return pos if pos != nil
304
+
305
+ tree_children.each_with_index do |c, i|
306
+
307
+ exp_name = c[0]
308
+ ref = c[1]['ref']
309
+ tag = c[1]['tag']
310
+
311
+ ref = @context.dollar_sub.s(ref, self, workitem) if ref
312
+ tag = @context.dollar_sub.s(tag, self, workitem) if tag
313
+
314
+ next if exp_name != arg && ref != arg && tag != arg
315
+
316
+ pos = i
317
+ break
318
+ end
319
+
320
+ pos ? pos : position
321
+ end
322
+ end
323
+ end
324
+