ruote-maestrodev 2.2.1

Sign up to get free protection for your applications and to get access to all the features.
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
+