ruote 2.2.0 → 2.3.0

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 (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