ruote 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (305) hide show
  1. data/CHANGELOG.txt +166 -1
  2. data/CREDITS.txt +36 -17
  3. data/LICENSE.txt +1 -1
  4. data/README.rdoc +1 -7
  5. data/Rakefile +38 -29
  6. data/TODO.txt +93 -52
  7. data/lib/ruote-fs.rb +3 -0
  8. data/lib/ruote.rb +5 -1
  9. data/lib/ruote/context.rb +140 -35
  10. data/lib/ruote/dashboard.rb +1247 -0
  11. data/lib/ruote/{engine → dboard}/process_error.rb +22 -2
  12. data/lib/ruote/dboard/process_status.rb +587 -0
  13. data/lib/ruote/engine.rb +6 -871
  14. data/lib/ruote/exp/command.rb +7 -2
  15. data/lib/ruote/exp/commanded.rb +2 -2
  16. data/lib/ruote/exp/condition.rb +38 -13
  17. data/lib/ruote/exp/fe_add_branches.rb +1 -1
  18. data/lib/ruote/exp/fe_apply.rb +1 -1
  19. data/lib/ruote/exp/fe_await.rb +357 -0
  20. data/lib/ruote/exp/fe_cancel_process.rb +17 -3
  21. data/lib/ruote/exp/fe_command.rb +8 -4
  22. data/lib/ruote/exp/fe_concurrence.rb +218 -18
  23. data/lib/ruote/exp/fe_concurrent_iterator.rb +71 -10
  24. data/lib/ruote/exp/fe_cron.rb +3 -10
  25. data/lib/ruote/exp/fe_cursor.rb +14 -4
  26. data/lib/ruote/exp/fe_define.rb +3 -1
  27. data/lib/ruote/exp/fe_echo.rb +1 -1
  28. data/lib/ruote/exp/fe_equals.rb +1 -1
  29. data/lib/ruote/exp/fe_error.rb +1 -1
  30. data/lib/ruote/exp/fe_filter.rb +163 -4
  31. data/lib/ruote/exp/fe_forget.rb +21 -4
  32. data/lib/ruote/exp/fe_given.rb +1 -1
  33. data/lib/ruote/exp/fe_if.rb +1 -1
  34. data/lib/ruote/exp/fe_inc.rb +102 -35
  35. data/lib/ruote/exp/fe_iterator.rb +47 -12
  36. data/lib/ruote/exp/fe_listen.rb +96 -11
  37. data/lib/ruote/exp/fe_lose.rb +31 -4
  38. data/lib/ruote/exp/fe_noop.rb +1 -1
  39. data/lib/ruote/exp/fe_on_error.rb +109 -0
  40. data/lib/ruote/exp/fe_once.rb +10 -19
  41. data/lib/ruote/exp/fe_participant.rb +90 -28
  42. data/lib/ruote/exp/fe_read.rb +69 -0
  43. data/lib/ruote/exp/fe_redo.rb +3 -2
  44. data/lib/ruote/exp/fe_ref.rb +57 -27
  45. data/lib/ruote/exp/fe_registerp.rb +1 -3
  46. data/lib/ruote/exp/fe_reserve.rb +1 -1
  47. data/lib/ruote/exp/fe_restore.rb +6 -6
  48. data/lib/ruote/exp/fe_save.rb +12 -19
  49. data/lib/ruote/exp/fe_sequence.rb +38 -2
  50. data/lib/ruote/exp/fe_set.rb +143 -40
  51. data/lib/ruote/exp/{fe_let.rb → fe_stall.rb} +7 -38
  52. data/lib/ruote/exp/fe_subprocess.rb +8 -2
  53. data/lib/ruote/exp/fe_that.rb +1 -1
  54. data/lib/ruote/exp/fe_undo.rb +40 -4
  55. data/lib/ruote/exp/fe_unregisterp.rb +1 -3
  56. data/lib/ruote/exp/fe_wait.rb +12 -25
  57. data/lib/ruote/exp/{flowexpression.rb → flow_expression.rb} +375 -229
  58. data/lib/ruote/exp/iterator.rb +2 -2
  59. data/lib/ruote/exp/merge.rb +78 -17
  60. data/lib/ruote/exp/ro_attributes.rb +46 -36
  61. data/lib/ruote/exp/ro_filters.rb +34 -8
  62. data/lib/ruote/exp/ro_on_x.rb +431 -0
  63. data/lib/ruote/exp/ro_persist.rb +19 -7
  64. data/lib/ruote/exp/ro_timers.rb +123 -0
  65. data/lib/ruote/exp/ro_variables.rb +90 -29
  66. data/lib/ruote/fei.rb +57 -3
  67. data/lib/ruote/fs.rb +3 -0
  68. data/lib/ruote/id/mnemo_wfid_generator.rb +30 -7
  69. data/lib/ruote/id/wfid_generator.rb +17 -38
  70. data/lib/ruote/log/default_history.rb +23 -9
  71. data/lib/ruote/log/fancy_printing.rb +265 -0
  72. data/lib/ruote/log/storage_history.rb +23 -13
  73. data/lib/ruote/log/wait_logger.rb +224 -17
  74. data/lib/ruote/observer.rb +82 -0
  75. data/lib/ruote/part/block_participant.rb +65 -28
  76. data/lib/ruote/part/code_participant.rb +81 -0
  77. data/lib/ruote/part/engine_participant.rb +7 -2
  78. data/lib/ruote/part/local_participant.rb +221 -21
  79. data/lib/ruote/part/no_op_participant.rb +1 -1
  80. data/lib/ruote/part/null_participant.rb +1 -1
  81. data/lib/ruote/part/participant.rb +50 -0
  82. data/lib/ruote/part/rev_participant.rb +178 -0
  83. data/lib/ruote/part/smtp_participant.rb +2 -2
  84. data/lib/ruote/part/storage_participant.rb +228 -60
  85. data/lib/ruote/part/template.rb +1 -1
  86. data/lib/ruote/participant.rb +2 -0
  87. data/lib/ruote/reader.rb +205 -68
  88. data/lib/ruote/reader/json.rb +49 -0
  89. data/lib/ruote/reader/radial.rb +303 -0
  90. data/lib/ruote/reader/ruby_dsl.rb +44 -9
  91. data/lib/ruote/reader/xml.rb +11 -8
  92. data/lib/ruote/receiver/base.rb +98 -45
  93. data/lib/ruote/storage/base.rb +104 -35
  94. data/lib/ruote/storage/composite_storage.rb +50 -60
  95. data/lib/ruote/storage/fs_storage.rb +25 -34
  96. data/lib/ruote/storage/hash_storage.rb +38 -36
  97. data/lib/ruote/svc/dispatch_pool.rb +104 -35
  98. data/lib/ruote/svc/dollar_sub.rb +10 -8
  99. data/lib/ruote/svc/error_handler.rb +108 -52
  100. data/lib/ruote/svc/expression_map.rb +3 -3
  101. data/lib/ruote/svc/participant_list.rb +160 -55
  102. data/lib/ruote/svc/tracker.rb +31 -31
  103. data/lib/ruote/svc/treechecker.rb +28 -16
  104. data/lib/ruote/tree_dot.rb +1 -1
  105. data/lib/ruote/util/deep.rb +143 -0
  106. data/lib/ruote/util/filter.rb +125 -18
  107. data/lib/ruote/util/hashdot.rb +15 -13
  108. data/lib/ruote/util/look.rb +1 -1
  109. data/lib/ruote/util/lookup.rb +60 -22
  110. data/lib/ruote/util/misc.rb +63 -18
  111. data/lib/ruote/util/mpatch.rb +53 -0
  112. data/lib/ruote/util/ometa.rb +1 -2
  113. data/lib/ruote/util/process_observer.rb +177 -0
  114. data/lib/ruote/util/subprocess.rb +1 -1
  115. data/lib/ruote/util/time.rb +2 -2
  116. data/lib/ruote/util/tree.rb +64 -2
  117. data/lib/ruote/version.rb +3 -2
  118. data/lib/ruote/worker.rb +421 -92
  119. data/lib/ruote/workitem.rb +157 -22
  120. data/ruote.gemspec +15 -9
  121. data/test/bm/ci.rb +0 -2
  122. data/test/bm/ici.rb +0 -2
  123. data/test/bm/load_26c.rb +0 -3
  124. data/test/bm/mega.rb +0 -2
  125. data/test/functional/base.rb +57 -43
  126. data/test/functional/concurrent_base.rb +16 -13
  127. data/test/functional/ct_0_concurrence.rb +7 -11
  128. data/test/functional/ct_1_iterator.rb +9 -11
  129. data/test/functional/ct_2_cancel.rb +28 -17
  130. data/test/functional/eft_0_flow_expression.rb +35 -0
  131. data/test/functional/eft_10_cancel_process.rb +1 -1
  132. data/test/functional/eft_11_wait.rb +13 -13
  133. data/test/functional/eft_12_listen.rb +199 -66
  134. data/test/functional/eft_13_iterator.rb +95 -29
  135. data/test/functional/eft_14_cursor.rb +74 -24
  136. data/test/functional/eft_15_loop.rb +7 -7
  137. data/test/functional/eft_16_if.rb +1 -1
  138. data/test/functional/eft_17_equals.rb +1 -1
  139. data/test/functional/eft_18_concurrent_iterator.rb +156 -68
  140. data/test/functional/eft_19_reserve.rb +15 -15
  141. data/test/functional/eft_1_echo.rb +1 -1
  142. data/test/functional/eft_20_save.rb +51 -9
  143. data/test/functional/eft_21_restore.rb +1 -1
  144. data/test/functional/eft_22_noop.rb +1 -1
  145. data/test/functional/eft_23_apply.rb +1 -1
  146. data/test/functional/eft_24_add_branches.rb +7 -8
  147. data/test/functional/eft_25_command.rb +1 -1
  148. data/test/functional/eft_26_error.rb +11 -11
  149. data/test/functional/eft_27_inc.rb +111 -67
  150. data/test/functional/eft_28_once.rb +16 -16
  151. data/test/functional/eft_29_cron.rb +9 -9
  152. data/test/functional/eft_2_sequence.rb +23 -4
  153. data/test/functional/eft_30_ref.rb +36 -24
  154. data/test/functional/eft_31_registerp.rb +24 -24
  155. data/test/functional/eft_32_lose.rb +46 -20
  156. data/test/functional/eft_34_given.rb +1 -1
  157. data/test/functional/eft_35_filter.rb +161 -7
  158. data/test/functional/eft_36_read.rb +97 -0
  159. data/test/functional/{eft_0_process_definition.rb → eft_37_process_definition.rb} +4 -4
  160. data/test/functional/eft_38_on_error.rb +195 -0
  161. data/test/functional/eft_39_stall.rb +35 -0
  162. data/test/functional/eft_3_participant.rb +77 -22
  163. data/test/functional/eft_40_await.rb +297 -0
  164. data/test/functional/eft_4_set.rb +110 -11
  165. data/test/functional/eft_5_subprocess.rb +27 -5
  166. data/test/functional/eft_6_concurrence.rb +299 -60
  167. data/test/functional/eft_7_forget.rb +24 -22
  168. data/test/functional/eft_8_undo.rb +52 -15
  169. data/test/functional/eft_9_redo.rb +18 -20
  170. data/test/functional/ft_0_worker.rb +122 -13
  171. data/test/functional/ft_10_dollar.rb +77 -16
  172. data/test/functional/ft_11_recursion.rb +9 -9
  173. data/test/functional/ft_12_launchitem.rb +7 -9
  174. data/test/functional/ft_13_variables.rb +125 -22
  175. data/test/functional/ft_14_re_apply.rb +112 -56
  176. data/test/functional/ft_15_timeout.rb +64 -33
  177. data/test/functional/ft_16_participant_params.rb +59 -6
  178. data/test/functional/ft_17_conditional.rb +68 -2
  179. data/test/functional/ft_18_kill.rb +48 -30
  180. data/test/functional/ft_19_participant_code.rb +67 -0
  181. data/test/functional/ft_1_process_status.rb +222 -150
  182. data/test/functional/ft_20_storage_participant.rb +445 -44
  183. data/test/functional/ft_21_forget.rb +21 -26
  184. data/test/functional/ft_22_process_definitions.rb +8 -6
  185. data/test/functional/ft_23_load_defs.rb +29 -5
  186. data/test/functional/ft_24_block_participant.rb +199 -20
  187. data/test/functional/ft_25_receiver.rb +98 -46
  188. data/test/functional/ft_26_participant_rtimeout.rb +34 -26
  189. data/test/functional/ft_27_var_indirection.rb +40 -5
  190. data/test/functional/ft_28_null_noop_participants.rb +5 -5
  191. data/test/functional/ft_29_part_template.rb +2 -2
  192. data/test/functional/ft_2_errors.rb +106 -74
  193. data/test/functional/ft_30_smtp_participant.rb +7 -7
  194. data/test/functional/ft_31_part_blocking.rb +11 -11
  195. data/test/functional/ft_32_scope.rb +50 -0
  196. data/test/functional/ft_33_participant_subprocess_priority.rb +3 -3
  197. data/test/functional/ft_34_cursor_rewind.rb +14 -14
  198. data/test/functional/ft_35_add_service.rb +67 -9
  199. data/test/functional/ft_36_storage_history.rb +92 -24
  200. data/test/functional/ft_37_default_history.rb +35 -23
  201. data/test/functional/ft_38_participant_more.rb +189 -32
  202. data/test/functional/ft_39_wait_for.rb +25 -25
  203. data/test/functional/ft_3_participant_registration.rb +235 -107
  204. data/test/functional/ft_40_wait_logger.rb +105 -18
  205. data/test/functional/ft_41_participants.rb +13 -12
  206. data/test/functional/ft_42_storage_copy.rb +12 -12
  207. data/test/functional/ft_43_participant_on_reply.rb +85 -11
  208. data/test/functional/ft_44_var_participant.rb +5 -5
  209. data/test/functional/ft_45_participant_accept.rb +3 -3
  210. data/test/functional/ft_46_launch_single.rb +17 -17
  211. data/test/functional/ft_47_wfids.rb +41 -0
  212. data/test/functional/ft_48_lose.rb +19 -25
  213. data/test/functional/ft_49_engine_on_error.rb +54 -70
  214. data/test/functional/ft_4_cancel.rb +84 -26
  215. data/test/functional/ft_50_engine_config.rb +4 -4
  216. data/test/functional/ft_51_misc.rb +12 -12
  217. data/test/functional/ft_52_case.rb +17 -17
  218. data/test/functional/ft_53_engine_on_terminate.rb +18 -21
  219. data/test/functional/ft_54_patterns.rb +18 -16
  220. data/test/functional/ft_55_engine_participant.rb +55 -55
  221. data/test/functional/ft_56_filter_attribute.rb +90 -52
  222. data/test/functional/ft_57_rev_participant.rb +252 -0
  223. data/test/functional/ft_58_workitem.rb +150 -0
  224. data/test/functional/ft_59_pause.rb +329 -0
  225. data/test/functional/ft_5_on_error.rb +430 -77
  226. data/test/functional/ft_60_code_participant.rb +65 -0
  227. data/test/functional/ft_61_trailing_fields.rb +34 -0
  228. data/test/functional/ft_62_exp_name_and_dollar_substitution.rb +35 -0
  229. data/test/functional/ft_63_participants_221.rb +458 -0
  230. data/test/functional/ft_64_stash.rb +41 -0
  231. data/test/functional/ft_65_timers.rb +313 -0
  232. data/test/functional/ft_66_flank.rb +133 -0
  233. data/test/functional/ft_67_radial_misc.rb +34 -0
  234. data/test/functional/ft_68_reput.rb +72 -0
  235. data/test/functional/ft_69_worker_info.rb +56 -0
  236. data/test/functional/ft_6_on_cancel.rb +189 -36
  237. data/test/functional/ft_70_take_and_discard_attributes.rb +94 -0
  238. data/test/functional/ft_71_retries.rb +144 -0
  239. data/test/functional/ft_72_on_terminate.rb +60 -0
  240. data/test/functional/ft_73_raise_msg.rb +107 -0
  241. data/test/functional/ft_74_respark.rb +106 -0
  242. data/test/functional/ft_75_context.rb +66 -0
  243. data/test/functional/ft_76_observer.rb +53 -0
  244. data/test/functional/ft_77_process_observer.rb +157 -0
  245. data/test/functional/ft_78_part_participant.rb +37 -0
  246. data/test/functional/ft_7_tags.rb +238 -50
  247. data/test/functional/ft_8_participant_consumption.rb +27 -21
  248. data/test/functional/ft_9_subprocesses.rb +48 -18
  249. data/test/functional/restart_base.rb +4 -6
  250. data/test/functional/rt_0_wait.rb +10 -10
  251. data/test/functional/rt_1_listen.rb +6 -6
  252. data/test/functional/rt_2_errors.rb +12 -12
  253. data/test/functional/rt_3_once.rb +17 -12
  254. data/test/functional/rt_4_cron.rb +17 -17
  255. data/test/functional/rt_5_timeout.rb +13 -13
  256. data/test/functional/signals.rb +103 -0
  257. data/test/functional/storage.rb +730 -0
  258. data/test/functional/storage_helper.rb +48 -35
  259. data/test/functional/test.rb +6 -2
  260. data/test/misc/idle.rb +21 -0
  261. data/test/misc/light.rb +29 -0
  262. data/test/path_helper.rb +1 -1
  263. data/test/test.rb +2 -5
  264. data/test/test_helper.rb +13 -0
  265. data/test/unit/test.rb +1 -4
  266. data/test/unit/ut_0_ruby_reader.rb +25 -9
  267. data/test/unit/ut_10_participants.rb +47 -0
  268. data/test/unit/ut_11_lookup.rb +59 -2
  269. data/test/unit/ut_12_wait_logger.rb +123 -0
  270. data/test/unit/ut_14_is_uri.rb +1 -1
  271. data/test/unit/ut_15_util.rb +1 -1
  272. data/test/unit/ut_16_reader.rb +136 -14
  273. data/test/unit/ut_17_merge.rb +155 -0
  274. data/test/unit/ut_19_part_template.rb +1 -1
  275. data/test/unit/ut_1_fei.rb +11 -2
  276. data/test/unit/ut_20_composite_storage.rb +27 -1
  277. data/test/unit/{ut_21_participant_list.rb → ut_21_svc_participant_list.rb} +2 -3
  278. data/test/unit/ut_22_filter.rb +231 -10
  279. data/test/unit/ut_23_svc_tracker.rb +48 -0
  280. data/test/unit/ut_24_radial_reader.rb +458 -0
  281. data/test/unit/ut_25_process_status.rb +143 -0
  282. data/test/unit/ut_26_deep.rb +131 -0
  283. data/test/unit/ut_2_dashboard.rb +114 -0
  284. data/test/unit/ut_3_worker.rb +54 -0
  285. data/test/unit/ut_4_expmap.rb +1 -1
  286. data/test/unit/ut_5_tree.rb +23 -23
  287. data/test/unit/ut_6_condition.rb +71 -29
  288. data/test/unit/ut_7_workitem.rb +18 -4
  289. data/test/unit/ut_8_tree_to_dot.rb +1 -1
  290. data/test/unit/ut_9_xml_reader.rb +1 -1
  291. metadata +142 -63
  292. data/jruby_issue.txt +0 -32
  293. data/lib/ruote/engine/process_status.rb +0 -403
  294. data/lib/ruote/log/pretty.rb +0 -165
  295. data/lib/ruote/log/test_logger.rb +0 -204
  296. data/lib/ruote/util/serializer.rb +0 -103
  297. data/phil.txt +0 -14
  298. data/test/functional/eft_33_let.rb +0 -31
  299. data/test/functional/ft_19_alias.rb +0 -33
  300. data/test/functional/ft_47_wfid_generator.rb +0 -54
  301. data/test/unit/storage.rb +0 -403
  302. data/test/unit/storages.rb +0 -37
  303. data/test/unit/ut_13_serializer.rb +0 -65
  304. data/test/unit/ut_18_engine.rb +0 -47
  305. data/test/unit/ut_3_wait_logger.rb +0 -39
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
2
+ # Copyright (c) 2005-2012, John Mettraux, jmettraux@gmail.com
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  # of this software and associated documentation files (the "Software"), to deal
@@ -22,6 +22,7 @@
22
22
  # Made in Japan.
23
23
  #++
24
24
 
25
+ require 'sourcify'
25
26
  require 'ruote/util/ometa'
26
27
 
27
28
 
@@ -92,6 +93,10 @@ module Ruote
92
93
  RubyDsl.create_branch('x', {}, &block).last.first
93
94
  end
94
95
 
96
+ class << self
97
+ alias tree to_tree
98
+ end
99
+
95
100
  # :nodoc:
96
101
  #
97
102
  module RubyDsl
@@ -120,18 +125,18 @@ module Ruote
120
125
 
121
126
  name = name[1..-1] while name[0, 1] == '_'
122
127
 
123
- h = attributes.inject({}) { |h1, a|
128
+ h = attributes.each_with_object({}) { |a, h1|
124
129
 
125
- a.is_a?(Hash) ? h1.merge!(a) : h1[a] = nil
130
+ if a.is_a?(Hash)
131
+ h1.merge!(a)
132
+ else
133
+ h1[a] = nil
134
+ end
126
135
 
127
- h1
128
-
129
- }.inject({}) { |h1, (k, v)|
136
+ }.remap { |(k, v), h1|
130
137
 
131
138
  k = k.is_a?(Regexp) ? k.inspect : k.to_s
132
139
  h1[k] = to_json(v)
133
-
134
- h1
135
140
  }
136
141
 
137
142
  c = BranchContext.new(name, h)
@@ -146,10 +151,40 @@ module Ruote
146
151
  when Symbol; v.to_s
147
152
  when Regexp; v.inspect
148
153
  when Array; v.collect { |e| to_json(e) }
149
- when Hash; v.inject({}) { |h, (k, v)| h[k.to_s] = to_json(v); h }
154
+ when Hash; v.remap { |(k, v), h| h[to_json(k)] = to_json(v) }
155
+ when Proc; v.to_raw_source + "\n"
150
156
  else v
151
157
  end
152
158
  end
153
159
  end
160
+
161
+ #
162
+ # The same .read and .understands? method as the other readers are found here.
163
+ #
164
+ module RubyReader
165
+
166
+ # Returns true if s seems to contain a Ruby process definition
167
+ #
168
+ def self.understands?(s)
169
+
170
+ s.match(
171
+ /\bRuote\.(process_definition|workflow_definition|define)\b/
172
+ ) != nil
173
+ end
174
+
175
+ # Evaluates the ruby string in the code, but at fist, thanks to the
176
+ # treechecker, makes sure it doesn't code malicious ruby code (at least
177
+ # tries very hard).
178
+ #
179
+ def self.read(s, treechecker)
180
+
181
+ treechecker.definition_check(s)
182
+ eval(s)
183
+
184
+ rescue SyntaxError => se
185
+ #p se
186
+ raise ArgumentError.new("Ruby syntax error : #{se.message}")
187
+ end
188
+ end
154
189
  end
155
190
 
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
2
+ # Copyright (c) 2005-2012, John Mettraux, jmettraux@gmail.com
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  # of this software and associated documentation files (the "Software"), to deal
@@ -33,6 +33,13 @@ module Ruote
33
33
  #
34
34
  module XmlReader
35
35
 
36
+ # Returns true if the string seems to be an XML string.
37
+ #
38
+ def self.understands?(s)
39
+
40
+ !! s.strip.match(/<.+>/)
41
+ end
42
+
36
43
  #
37
44
  # A helper class to store the temporary tree while it gets read.
38
45
  #
@@ -40,14 +47,11 @@ module Ruote
40
47
 
41
48
  attr_reader :parent, :attributes, :children
42
49
 
43
- def initialize(parent, name, attributes)
50
+ def initialize(parent, name, atts)
44
51
 
45
52
  @parent = parent
46
53
  @name = name
47
- @attributes = attributes.inject({}) { |h, (k, v)|
48
- h[k.gsub(/-/, '_')] = v
49
- h
50
- }
54
+ @attributes = atts.remap { |(k, v), h| h[k.gsub(/-/, '_')] = v }
51
55
  @children = []
52
56
 
53
57
  parent.children << self if parent
@@ -59,10 +63,9 @@ module Ruote
59
63
  end
60
64
  end
61
65
 
62
- #
63
66
  # Parses the XML string into a process definition tree (array of arrays).
64
67
  #
65
- def self.read(s)
68
+ def self.read(s, opt=nil)
66
69
 
67
70
  parser = REXML::Parsers::SAX2Parser.new(s)
68
71
 
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
2
+ # Copyright (c) 2005-2012, John Mettraux, jmettraux@gmail.com
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  # of this software and associated documentation files (the "Software"), to deal
@@ -29,7 +29,7 @@ module Ruote
29
29
  # The core methods for the Receiver class (sometimes a Mixin is easier
30
30
  # to integrate).
31
31
  #
32
- # (The engine itself includes this mixin, the LocalParticipant module
32
+ # (The dashboard itself includes this mixin, the LocalParticipant module
33
33
  # includes it as well).
34
34
  #
35
35
  module ReceiverMixin
@@ -49,11 +49,68 @@ module Ruote
49
49
  'receiver' => sign)
50
50
  end
51
51
 
52
+ # Wraps a call to receive(workitem)
53
+ #
54
+ # Not aliasing so that if someone changes the receive implementation,
55
+ # reply is affected as well.
56
+ #
57
+ def reply(workitem)
58
+
59
+ receive(workitem)
60
+ end
61
+
62
+ # Can be used to raise an error in the workflow instance.
63
+ #
64
+ # Can be called either with an error class and arguments, either with
65
+ # an error instance (and no arguments).
66
+ #
67
+ # The workitem can be either an instance of Ruote::Workitem or a workitem
68
+ # in its Hash representation.
69
+ #
70
+ # receiver.flunk(workitem, ArgumentError, "not enough info")
71
+ #
72
+ # rescue => e
73
+ # receiver.flunk(workitem, e)
74
+ # end
75
+ #
76
+ def flunk(workitem, error_class_or_instance_or_message, *err_arguments)
77
+
78
+ err = error_class_or_instance_or_message
79
+
80
+ if err.is_a?(String)
81
+ err = RuntimeError.new(err)
82
+ err.set_backtrace(caller)
83
+
84
+ elsif err.is_a?(Class)
85
+ err = err.new(*err_arguments)
86
+ err.set_backtrace(caller)
87
+ end
88
+
89
+ workitem = workitem.h if workitem.respond_to?(:h)
90
+
91
+ @context.storage.put_msg(
92
+ 'raise',
93
+ 'fei' => workitem['fei'],
94
+ 'wfid' => workitem['wfid'],
95
+ 'msg' => {
96
+ 'action' => 'dispatch',
97
+ 'fei' => workitem['fei'],
98
+ 'participant_name' => workitem['participant_name'],
99
+ 'participant' => nil,
100
+ 'workitem' => workitem
101
+ },
102
+ 'error' => {
103
+ 'class' => err.class.name,
104
+ 'message' => err.message,
105
+ 'trace' => err.backtrace
106
+ })
107
+ end
108
+
52
109
  # Given a process definitions and optional initial fields and variables,
53
110
  # launches a new process instance.
54
111
  #
55
- # This method is mostly used from the Ruote::Engine class (which includes
56
- # this mixin).
112
+ # This method is mostly used from the Ruote::Dashboard class (which
113
+ # includes this mixin).
57
114
  #
58
115
  # process_definition must be a result of Ruote.process_definition call
59
116
  # or XML or JSON serialized process definition, as accepted by
@@ -61,42 +118,30 @@ module Ruote
61
118
  #
62
119
  # fields are workflow parameters that will be placed in workitem.fields.
63
120
  #
64
- # variables contain engine variables.
121
+ # Calls to this method returns the newly launched "workflow instance id"
122
+ # ("wfid" for short), the [hopefully] unique identifier for the
123
+ # process instance.
65
124
  #
66
- def launch(process_definition, fields={}, variables={})
125
+ # == custom :wfid
126
+ #
127
+ # When calling this method, it's OK to pass a field named :wfid (Symbol,
128
+ # not String) that will be used as the identifier for the process instance.
129
+ #
130
+ def launch(process_definition, fields={}, variables={}, root_stash=nil)
67
131
 
68
- wfid = @context.wfidgen.generate
132
+ wfid = fields[:wfid] || @context.wfidgen.generate
69
133
 
70
134
  @context.storage.put_msg(
71
135
  'launch',
72
136
  'wfid' => wfid,
73
137
  'tree' => @context.reader.read(process_definition),
74
138
  'workitem' => { 'fields' => fields },
75
- 'variables' => variables)
139
+ 'variables' => variables,
140
+ 'stash' => root_stash)
76
141
 
77
142
  wfid
78
143
  end
79
144
 
80
- # Wraps a call to receive(workitem)
81
- #
82
- # Not aliasing so that if someone changes the receive implementation,
83
- # reply is affected as well.
84
- #
85
- def reply(workitem)
86
-
87
- receive(workitem)
88
- end
89
-
90
- # Wraps a call to receive(workitem)
91
- #
92
- # Not aliasing so that if someone changes the receive implementation,
93
- # reply_to_engine is affected as well.
94
- #
95
- def reply_to_engine(workitem)
96
-
97
- receive(workitem)
98
- end
99
-
100
145
  # A receiver signs a workitem when it comes back.
101
146
  #
102
147
  # Not used much as of now.
@@ -116,13 +161,8 @@ module Ruote
116
161
  Ruote::FlowExpressionId.extract_h(workitem_or_fei))
117
162
  end
118
163
 
119
- # For example :
120
- #
121
- # fexp = engine.fexp(fei)
122
- # # or
123
- # fexp = engine.fexp(workitem)
124
- #
125
164
  alias fexp fetch_flow_expression
165
+ alias flow_expression fetch_flow_expression
126
166
 
127
167
  # A convenience methods for advanced users (like Oleg).
128
168
  #
@@ -140,11 +180,13 @@ module Ruote
140
180
  # on_terminate processes are not triggered for on_error processes.
141
181
  # on_error processes are triggered for on_terminate processes as well.
142
182
  #
143
- def applied_workitem(fei)
183
+ def fetch_workitem(fexp_or_fei)
144
184
 
145
- Ruote::Workitem.new(fexp(fei).h.applied_workitem)
185
+ Ruote::Workitem.new(flow_expression(fexp_or_fei).h.applied_workitem)
146
186
  end
147
- alias workitem applied_workitem
187
+
188
+ alias workitem fetch_workitem
189
+ alias applied_workitem fetch_workitem
148
190
 
149
191
  protected
150
192
 
@@ -161,15 +203,25 @@ module Ruote
161
203
  # http://groups.google.com/group/openwferu-users/t/2e6a95708c10847b for the
162
204
  # justification.
163
205
  #
164
- def put(fei, hash)
206
+ def stash_put(workitem_or_fei, key, value=nil)
165
207
 
166
- fexp = Ruote::Exp::FlowExpression.fetch(@context, fei.to_h)
208
+ hash = key.is_a?(Hash) ? key : { key => value }
167
209
 
168
- (fexp.h['stash'] ||= {}).merge!(hash)
210
+ exp = fetch_flow_expression(workitem_or_fei)
169
211
 
170
- fexp.persist_or_raise
212
+ (exp.h['stash'] ||= {}).merge!(hash)
213
+
214
+ r = exp.try_persist
215
+
216
+ return hash if r == nil
217
+ return stash_put(workitem_or_fei, key, value) if r != true
218
+
219
+ fei = Ruote::FlowExpressionId.extract(workitem_or_fei).sid rescue 'xxx'
220
+ raise ArgumentError.new("failed to put, expression #{fei} is gone")
171
221
  end
172
222
 
223
+ alias put stash_put
224
+
173
225
  # Fetches back a stashed value.
174
226
  #
175
227
  # get(fei, 'colour')
@@ -183,14 +235,15 @@ module Ruote
183
235
  # put & get are useful for a participant that needs to communicate
184
236
  # between its consume and its cancel.
185
237
  #
186
- def get(fei, key=nil)
187
-
188
- fexp = Ruote::Exp::FlowExpression.fetch(@context, fei.to_h)
238
+ def stash_get(workitem_or_fei, key=nil)
189
239
 
190
- stash = fexp.h['stash'] rescue {}
240
+ stash = fetch_flow_expression(workitem_or_fei).h['stash'] rescue nil
241
+ stash ||= {}
191
242
 
192
243
  key ? stash[key] : stash
193
244
  end
245
+
246
+ alias get stash_get
194
247
  end
195
248
 
196
249
  #
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
2
+ # Copyright (c) 2005-2012, John Mettraux, jmettraux@gmail.com
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  # of this software and associated documentation files (the "Software"), to deal
@@ -22,6 +22,7 @@
22
22
  # Made in Japan.
23
23
  #++
24
24
 
25
+ require 'ostruct'
25
26
  require 'ruote/util/time'
26
27
 
27
28
 
@@ -32,6 +33,10 @@ module Ruote
32
33
  #
33
34
  module StorageBase
34
35
 
36
+ #--
37
+ # misc
38
+ #++
39
+
35
40
  def context
36
41
 
37
42
  @context ||= Ruote::Context.new(self)
@@ -50,6 +55,21 @@ module Ruote
50
55
  delete(doc).nil?
51
56
  end
52
57
 
58
+ # A helper for the #worker method, it returns that dummy worker
59
+ # when there is no reference to the calling worker in the current
60
+ # thread's local variables.
61
+ #
62
+ DUMMY_WORKER = OpenStruct.new(
63
+ :name => 'worker', :identity => 'unknown', :state => 'running')
64
+
65
+ # Warning, this is not equivalent to doing @context.worker, this method
66
+ # fetches the worker from the local thread variables.
67
+ #
68
+ def worker
69
+
70
+ Thread.current['ruote_worker'] || DUMMY_WORKER
71
+ end
72
+
53
73
  #--
54
74
  # configurations
55
75
  #++
@@ -59,6 +79,18 @@ module Ruote
59
79
  get('configurations', key)
60
80
  end
61
81
 
82
+ def replace_engine_configuration(options)
83
+
84
+ return if options.delete('preserve_configuration')
85
+
86
+ conf = get('configurations', 'engine')
87
+
88
+ doc = options.merge('type' => 'configurations', '_id' => 'engine')
89
+ doc['_rev'] = conf['_rev'] if conf
90
+
91
+ put(doc)
92
+ end
93
+
62
94
  #--
63
95
  # messages
64
96
  #++
@@ -68,29 +100,11 @@ module Ruote
68
100
  msg = prepare_msg_doc(action, options)
69
101
 
70
102
  put(msg)
71
-
72
- #put(msg, :update_rev => true)
73
- #(@local_msgs ||= []) << Ruote.fulldup(msg)
74
103
  end
75
104
 
76
- #def get_local_msgs
77
- # p @local_msgs
78
- # if @local_msgs
79
- # r = @local_msgs
80
- # @local_msgs = nil
81
- # r
82
- # else
83
- # []
84
- # end
85
- #end
86
-
87
105
  def get_msgs
88
106
 
89
- get_many(
90
- 'msgs', nil, :limit => 300
91
- ).sort { |a, b|
92
- a['put_at'] <=> b['put_at']
93
- }
107
+ get_many('msgs', nil, :limit => 300).sort_by { |d| d['put_at'] }
94
108
  end
95
109
 
96
110
  def empty?(type)
@@ -102,13 +116,27 @@ module Ruote
102
116
  # expressions
103
117
  #++
104
118
 
119
+ # Given a wfid, returns all the expressions of that process instance.
120
+ #
121
+ def find_expressions(wfid)
122
+
123
+ get_many('expressions', wfid)
124
+ end
125
+
126
+ # For a given wfid, returns all the expressions (array of Hash instances)
127
+ # that have a nil 'parent_id'.
128
+ #
129
+ def find_root_expressions(wfid)
130
+
131
+ find_expressions(wfid).select { |hexp| hexp['parent_id'].nil? }
132
+ end
133
+
134
+ # For a given wfid, fetches all the root expressions, sort by expid and
135
+ # return the first. Hopefully it's the right root_expression.
136
+ #
105
137
  def find_root_expression(wfid)
106
138
 
107
- get_many('expressions', wfid).sort_by { |fexp|
108
- fexp['fei']['expid']
109
- }.select { |e|
110
- e['parent_id'].nil?
111
- }.first
139
+ find_root_expressions(wfid).sort_by { |hexp| hexp['fei']['expid'] }.first
112
140
  end
113
141
 
114
142
  # Given all the expressions stored here, returns a sorted list of unique
@@ -250,6 +278,38 @@ module Ruote
250
278
  end
251
279
  end
252
280
 
281
+ # Removes a process by removing all its schedules, expressions, errors,
282
+ # workitems and trackers.
283
+ #
284
+ # Warning: will not trigger any cancel behaviours at all, just removes
285
+ # the process.
286
+ #
287
+ def remove_process(wfid)
288
+
289
+ 2.times do
290
+ # two passes
291
+
292
+ Thread.pass
293
+
294
+ %w[ schedules expressions errors workitems ].each do |type|
295
+ get_many(type, wfid).each { |d| delete(d) }
296
+ end
297
+
298
+ doc = get_trackers
299
+
300
+ doc['trackers'].delete_if { |k, v| k.end_with?("!#{wfid}") }
301
+
302
+ @context.storage.put(doc)
303
+ end
304
+ end
305
+
306
+ def dump(type)
307
+
308
+ require 'yaml'
309
+
310
+ YAML.dump({ type => get_many(type) })
311
+ end
312
+
253
313
  protected
254
314
 
255
315
  # Used by put_msg
@@ -303,6 +363,7 @@ module Ruote
303
363
  'original' => s,
304
364
  'at' => Ruote.time_to_utc_s(at),
305
365
  'owner' => owner_fei,
366
+ 'wfid' => owner_fei['wfid'],
306
367
  'msg' => msg
307
368
  }
308
369
  end
@@ -322,27 +383,35 @@ module Ruote
322
383
  schedules.select { |sch| sch['at'] <= now }
323
384
  end
324
385
 
325
- ## Returns true if the doc wfid is included in the wfids passed.
326
- ##
327
- #def wfid_match? (doc, wfids)
328
- # wfids.find { |wfid| doc['_id'].index(wfid) } != nil
329
- #end
330
-
331
386
  # Used by #get_many. Returns true whenever one of the keys matches the
332
387
  # doc['_id']. Works with strings (_id ends with key) or regexes (_id matches
333
388
  # key).
334
389
  #
335
- # It's a class method meant to be used by the various storage
336
- # implementations.
390
+ def key_match?(type, keys, doc)
391
+
392
+ _id = doc.is_a?(Hash) ? doc['_id'] : doc
393
+
394
+ if keys.first.is_a?(String) && type == 'schedules'
395
+ keys.find { |key| _id.match(/#{key}-\d+$/) }
396
+ elsif keys.first.is_a?(String)
397
+ keys.find { |key| _id.end_with?(key) }
398
+ else # Regexp
399
+ keys.find { |key| _id.match(key) }
400
+ end
401
+ end
402
+
403
+ # (Only used by ruote-couch 2.2.x)
404
+ #
405
+ # TODO: remove me at some point
337
406
  #
338
407
  def self.key_match?(keys, doc)
339
408
 
340
409
  _id = doc.is_a?(Hash) ? doc['_id'] : doc
341
410
 
342
411
  if keys.first.is_a?(String)
343
- keys.find { |key| _id[-key.length..-1] == key }
412
+ keys.find { |key| _id.end_with?(key) }
344
413
  else # Regexp
345
- keys.find { |key| key.match(_id) }
414
+ keys.find { |key| _id.match(key) }
346
415
  end
347
416
  end
348
417
  end