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,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
+