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,397 @@
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 'sourcify'
27
+ require 'ruote/part/local_participant'
28
+ require 'ruote/part/block_participant'
29
+ require 'ruote/part/code_participant'
30
+
31
+
32
+ module Ruote
33
+
34
+ #
35
+ # Tracking participants to [business] processes.
36
+ #
37
+ # The methods here are mostly called via the engine (registering /
38
+ # unregistering participants) and via the dispatch_pool (when handing
39
+ # workitems to participants).
40
+ #
41
+ class ParticipantList
42
+
43
+ def initialize(context)
44
+
45
+ @context = context
46
+ end
47
+
48
+ # Registers a participant. Called by Engine#register_participant.
49
+ #
50
+ def register(name, participant, options, block)
51
+
52
+ raise(
53
+ ArgumentError.new(
54
+ "can only accept strings (classnames) or classes as participant arg")
55
+ ) unless [ String, Class, NilClass ].include?(participant.class)
56
+
57
+ klass = (participant || Ruote::BlockParticipant).to_s
58
+
59
+ options = options.inject({}) { |h, (k, v)|
60
+
61
+ h[k.to_s] = case v
62
+ when Symbol then v.to_s
63
+ when Proc then v.to_raw_source
64
+ else v
65
+ end
66
+
67
+ h
68
+ }
69
+
70
+ if block
71
+ options['on_workitem'] = block.to_raw_source
72
+ @context.treechecker.block_check(options['on_workitem'])
73
+ end
74
+
75
+ key = (name.is_a?(Regexp) ? name : Regexp.new("^#{name}$")).source
76
+ entry = [ key, [ klass, options ] ]
77
+
78
+ list = get_list
79
+
80
+ position = options['position'] || options['pos'] || 'last'
81
+
82
+ if position == 'before'
83
+
84
+ position = list['list'].index { |e| e.first == key } || -1
85
+
86
+ elsif position == 'after'
87
+
88
+ position = (list['list'].rindex { |e| e.first == key } || -2) + 1
89
+
90
+ elsif position == 'over'
91
+
92
+ position = list['list'].index { |e| e.first == key } || -1
93
+ list['list'].delete_at(position) unless position == -1
94
+
95
+ elsif options.delete('override') != false
96
+
97
+ list['list'].delete_if { |e| e.first == key }
98
+ # enforces only one instance of a participant per key/regex
99
+ end
100
+
101
+ case position
102
+ when 'last' then list['list'] << entry
103
+ when 'first' then list['list'].unshift(entry)
104
+ when Fixnum then list['list'].insert(position, entry)
105
+ else raise "cannot insert participant at position '#{position}'"
106
+ end
107
+
108
+ if r = @context.storage.put(list)
109
+ #
110
+ # if put returns something it means the put failed, have to redo the
111
+ # work...
112
+ #
113
+ return register(name, participant, options, block)
114
+ end
115
+
116
+ if entry.last.first == 'Ruote::StorageParticipant'
117
+ Ruote::StorageParticipant.new(@context)
118
+ else
119
+ nil
120
+ end
121
+ end
122
+
123
+ # Removes a participant, given via its name or directly from this
124
+ # participant list.
125
+ #
126
+ # Called usually by Engine#unregister_participant.
127
+ #
128
+ def unregister(name_or_participant)
129
+
130
+ code = nil
131
+ entry = nil
132
+ list = get_list
133
+
134
+ name_or_participant = name_or_participant.to_s
135
+
136
+ entry = list['list'].find { |re, pa| name_or_participant.match(re) }
137
+
138
+ return nil unless entry
139
+
140
+ code = entry.last if entry.last.is_a?(String)
141
+
142
+ list['list'].delete(entry)
143
+
144
+ if r = @context.storage.put(list)
145
+ #
146
+ # put failed, have to redo it
147
+ #
148
+ return unregister(name_or_participant)
149
+ end
150
+
151
+ entry.first
152
+ end
153
+
154
+ # Returns a participant instance, or nil if there is no participant
155
+ # for the given participant name.
156
+ #
157
+ # Mostly a combination of #lookup_info and #instantiate.
158
+ #
159
+ def lookup(participant_name, workitem, opts={})
160
+
161
+ pinfo = participant_name.is_a?(String) ?
162
+ lookup_info(participant_name, workitem) : participant_name
163
+
164
+ pinfo ?
165
+ instantiate(pinfo, opts) : nil
166
+ end
167
+
168
+ # Given a participant name, returns
169
+ #
170
+ # Returns nil if there is no participant registered that covers the given
171
+ # participant name.
172
+ #
173
+ def lookup_info(pname, workitem)
174
+
175
+ get_list['list'].each do |regex, pinfo|
176
+
177
+ next unless pname.match(regex)
178
+
179
+ return pinfo if workitem.nil?
180
+
181
+ pa = instantiate(pinfo, :if_respond_to? => :accept?)
182
+
183
+ return pinfo if pa.nil?
184
+
185
+ return pinfo if pa.accept?(
186
+ Ruote::Workitem.new(workitem.merge('participant_name' => pname))
187
+ )
188
+ end
189
+
190
+ # nothing found...
191
+
192
+ nil
193
+ end
194
+
195
+ # Returns an instance of a participant
196
+ #
197
+ def instantiate(pinfo, opts={})
198
+
199
+ pa_class_name, options = pinfo
200
+
201
+ if rp = options['require_path']
202
+ require(rp)
203
+ end
204
+ if lp = options['load_path']
205
+ load(lp)
206
+ end
207
+
208
+ pa_class = Ruote.constantize(pa_class_name)
209
+ pa_m = pa_class.instance_methods
210
+
211
+ irt = opts[:if_respond_to?]
212
+
213
+ if irt && ! (pa_m.include?(irt.to_s) || pa_m.include?(irt.to_sym))
214
+ return nil
215
+ end
216
+
217
+ initialize_participant(pa_class, options)
218
+ end
219
+
220
+ def initialize_participant(klass, options)
221
+
222
+ participant = if klass.instance_method(:initialize).arity == 0
223
+ klass.new
224
+ else
225
+ klass.new(options)
226
+ end
227
+
228
+ participant.context = @context if participant.respond_to?(:context=)
229
+
230
+ participant
231
+ end
232
+
233
+ # Return a list of names (regex) for the registered participants
234
+ #
235
+ def names
236
+
237
+ get_list['list'].collect { |re, pa| re }
238
+ end
239
+
240
+ # Calls #shutdown on any participant that sports this method.
241
+ #
242
+ def shutdown
243
+
244
+ get_list['list'].each do |re, (kl, op)|
245
+
246
+ kl = (Ruote.constantize(kl) rescue nil)
247
+
248
+ if (kl.instance_method(:shutdown) rescue false)
249
+ initialize_participant(kl, op).shutdown
250
+ end
251
+ end
252
+ end
253
+
254
+ # Used by Engine#participant_list
255
+ #
256
+ # Returns a representation of this participant list as an array of
257
+ # ParticipantEntry instances.
258
+ #
259
+ def list
260
+
261
+ get_list['list'].collect { |e| ParticipantEntry.new(e) }
262
+ end
263
+
264
+ # Used by Engine#participant_list=
265
+ #
266
+ # Takes as input an array of ParticipantEntry instances and updates
267
+ # this participant list with it.
268
+ #
269
+ # See ParticipantList#list
270
+ #
271
+ def list=(pl)
272
+
273
+ list = get_list
274
+
275
+ list['list'] = pl.collect { |e|
276
+ ParticipantEntry.read(e)
277
+ }.collect { |e|
278
+ e[0] = e[0].source if e[0].is_a?(Regexp)
279
+ e
280
+ }
281
+
282
+ if r = @context.storage.put(list)
283
+ #
284
+ # put failed, have to redo it
285
+ #
286
+ self.list=(pl)
287
+ end
288
+ end
289
+
290
+ # Clears this participant list.
291
+ #
292
+ # Used by Engine#register(&block)
293
+ #
294
+ def clear
295
+
296
+ self.list=([])
297
+ end
298
+
299
+ protected
300
+
301
+ # Fetches and returns the participant list in the storage.
302
+ #
303
+ def get_list
304
+
305
+ @context.storage.get_configuration('participant_list') ||
306
+ { 'type' => 'configurations',
307
+ '_id' => 'participant_list',
308
+ 'list' => [] }
309
+ end
310
+
311
+ #--
312
+ # Returns an array of all the classes in the ObjectSpace that include the
313
+ # Ruote::LocalParticipant module.
314
+ #
315
+ #def local_participant_classes
316
+ # ObjectSpace.each_object(Class).inject([]) { |a, c|
317
+ # a << c if c.include?(Ruote::LocalParticipant)
318
+ # a
319
+ # }
320
+ #end
321
+ #++
322
+ end
323
+
324
+ #
325
+ # A helper class, for ParticipantList#list, which returns a list (order
326
+ # matters) of ParticipantEntry instances.
327
+ #
328
+ # See Engine#participant_list
329
+ #
330
+ class ParticipantEntry
331
+
332
+ attr_accessor :regex, :classname, :options
333
+
334
+ def initialize(a)
335
+ @regex = a.first
336
+ if a.last.is_a?(Array)
337
+ @classname = a.last.first
338
+ @options = a.last.last
339
+ else
340
+ @classname = a.last
341
+ @options = nil
342
+ end
343
+ end
344
+
345
+ def to_a
346
+
347
+ [ @regex, [ @classname, @options ] ]
348
+ end
349
+
350
+ def to_s
351
+
352
+ "/#{@regex}/ ==> #{@classname} #{@options.inspect}"
353
+ end
354
+
355
+ # Makes sense of whatever is given to it, returns something like
356
+ #
357
+ # [ regex, [ klass, opts ] ]
358
+ #
359
+ def self.read(elt)
360
+
361
+ return elt.to_a if elt.is_a?(ParticipantEntry)
362
+
363
+ if elt.is_a?(Hash)
364
+
365
+ options = elt['options'] || elt.reject { |k, v|
366
+ %w[ name regex regexp class classname ].include?(k)
367
+ }
368
+
369
+ name = elt.find { |k, v| v == nil }
370
+ if name
371
+ name = name.first
372
+ elt.delete(name)
373
+ options.delete(name)
374
+ end
375
+ name = name || elt['name']
376
+ name = Ruote.regex_or_s(name)
377
+
378
+ regex = name
379
+ unless name
380
+ regex = Ruote.regex_or_s(elt['regex'] || elt['regexp'])
381
+ regex = regex.is_a?(String) ? Regexp.new(regex) : regex
382
+ end
383
+
384
+ klass = elt['classname'] || elt['class']
385
+
386
+ return [ regex, [ klass, options ] ]
387
+ end
388
+
389
+ # else elt is a Array
390
+
391
+ return [ Ruote.regex_or_s(elt[0]), [ elt[1], elt[2] ] ] if elt.size == 3
392
+
393
+ [ Ruote.regex_or_s(elt[0]), elt[1] ]
394
+ end
395
+ end
396
+ end
397
+
@@ -0,0 +1,172 @@
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
27
+
28
+ #
29
+ # The tracker service is used by the "listen" expression. This services
30
+ # sees all the msg processed by a worker and triggers any
31
+ # listener interested in a particular msg.
32
+ #
33
+ # Look at the ListenExpression for more details.
34
+ #
35
+ class Tracker
36
+
37
+ def initialize(context)
38
+
39
+ @context = context
40
+
41
+ if @context.worker
42
+ #
43
+ # this is a worker context, DO log
44
+ #
45
+ @context.worker.subscribe(:all, self)
46
+ #else
47
+ #
48
+ # this is not a worker context, no notifications. BUT
49
+ # honour calls to add_tracker/remove_tracker
50
+ #
51
+ end
52
+ end
53
+
54
+ # The worker passes all the messages it has to process to the tracker via
55
+ # this method.
56
+ #
57
+ def notify(message)
58
+
59
+ m_error = message['error']
60
+ m_wfid = message['wfid'] || (message['fei']['wfid'] rescue nil)
61
+ m_action = message['action']
62
+
63
+ msg = m_action == 'error_intercepted' ? message['msg'] : message
64
+
65
+ @context.storage.get_trackers['trackers'].each do |tracker_id, tracker|
66
+
67
+ # filter msgs
68
+
69
+ t_wfid = tracker['wfid']
70
+ t_action = tracker['action']
71
+
72
+ next if t_wfid && t_wfid != m_wfid
73
+ next if t_action && t_action != m_action
74
+
75
+ next unless does_match?(message, tracker['conditions'])
76
+
77
+ if tracker_id == 'on_error' || tracker_id == 'on_terminate'
78
+
79
+ fields = msg['workitem']['fields']
80
+
81
+ next if m_action == 'error_intercepted' && fields['__error__']
82
+ next if m_action == 'terminated' && (fields['__error__'] || fields['__terminate__'])
83
+ end
84
+
85
+ # prepare and emit/put 'reaction' message
86
+
87
+ m = tracker['msg']
88
+
89
+ action = m.delete('action')
90
+
91
+ m['wfid'] = m_wfid if m['wfid'] == 'replace'
92
+ m['wfid'] ||= @context.wfidgen.generate
93
+
94
+ m['workitem'] = msg['workitem'] if m['workitem'] == 'replace'
95
+
96
+ if t_action == 'error_intercepted'
97
+ m['workitem']['fields']['__error__'] = m_error
98
+ elsif tracker_id == 'on_error' && m_action == 'error_intercepted'
99
+ m['workitem']['fields']['__error__'] = m_error
100
+ elsif tracker_id == 'on_terminate' && m_action == 'terminated'
101
+ m['workitem']['fields']['__terminate__'] = { 'wfid' => m_wfid }
102
+ end
103
+
104
+ if m['variables'] == 'compile'
105
+ fexp = Ruote::Exp::FlowExpression.fetch(@context, msg['fei'])
106
+ m['variables'] = fexp ? fexp.compile_variables : {}
107
+ end
108
+
109
+ @context.storage.put_msg(action, m)
110
+ end
111
+ end
112
+
113
+ # Adds a tracker (usually when a 'listen' expression gets applied).
114
+ #
115
+ def add_tracker(wfid, action, id, conditions, msg)
116
+
117
+ doc = @context.storage.get_trackers
118
+
119
+ doc['trackers'][id] =
120
+ { 'wfid' => wfid,
121
+ 'action' => action,
122
+ 'id' => id,
123
+ 'conditions' => conditions,
124
+ 'msg' => msg }
125
+
126
+ r = @context.storage.put(doc)
127
+
128
+ add_tracker(wfid, action, id, conditions, msg) if r
129
+ # the put failed, have to redo the work
130
+ end
131
+
132
+ # Removes a tracker (usually when a 'listen' expression replies to its
133
+ # parent expression or is cancelled).
134
+ #
135
+ def remove_tracker(fei, doc=nil)
136
+
137
+ doc ||= @context.storage.get_trackers
138
+
139
+ doc['trackers'].delete(Ruote.to_storage_id(fei))
140
+
141
+ r = @context.storage.put(doc)
142
+
143
+ remove_tracker(fei, r) if r
144
+ # the put failed, have to redo the work
145
+ end
146
+
147
+ protected
148
+
149
+ def does_match?(msg, conditions)
150
+
151
+ return true unless conditions
152
+
153
+ conditions.each do |k, v|
154
+
155
+ if k == 'class'
156
+ return false unless v.include?(msg['error']['class'])
157
+ next
158
+ end
159
+
160
+ v = Ruote.regex_or_s(v)
161
+
162
+ val = msg[k]
163
+ val = msg['error']['message'] if k == 'message'
164
+
165
+ return false unless val && v.is_a?(String) ? (v == val) : v.match(val)
166
+ end
167
+
168
+ true
169
+ end
170
+ end
171
+ end
172
+