ruote-maestrodev 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.txt +290 -0
- data/CREDITS.txt +99 -0
- data/LICENSE.txt +21 -0
- data/README.rdoc +88 -0
- data/Rakefile +108 -0
- data/TODO.txt +488 -0
- data/lib/ruote.rb +7 -0
- data/lib/ruote/context.rb +194 -0
- data/lib/ruote/engine.rb +1062 -0
- data/lib/ruote/engine/process_error.rb +122 -0
- data/lib/ruote/engine/process_status.rb +448 -0
- data/lib/ruote/exp/command.rb +87 -0
- data/lib/ruote/exp/commanded.rb +69 -0
- data/lib/ruote/exp/condition.rb +227 -0
- data/lib/ruote/exp/fe_add_branches.rb +138 -0
- data/lib/ruote/exp/fe_apply.rb +154 -0
- data/lib/ruote/exp/fe_cancel_process.rb +78 -0
- data/lib/ruote/exp/fe_command.rb +156 -0
- data/lib/ruote/exp/fe_concurrence.rb +321 -0
- data/lib/ruote/exp/fe_concurrent_iterator.rb +219 -0
- data/lib/ruote/exp/fe_cron.rb +141 -0
- data/lib/ruote/exp/fe_cursor.rb +324 -0
- data/lib/ruote/exp/fe_define.rb +112 -0
- data/lib/ruote/exp/fe_echo.rb +60 -0
- data/lib/ruote/exp/fe_equals.rb +115 -0
- data/lib/ruote/exp/fe_error.rb +82 -0
- data/lib/ruote/exp/fe_filter.rb +648 -0
- data/lib/ruote/exp/fe_forget.rb +88 -0
- data/lib/ruote/exp/fe_given.rb +154 -0
- data/lib/ruote/exp/fe_if.rb +127 -0
- data/lib/ruote/exp/fe_inc.rb +205 -0
- data/lib/ruote/exp/fe_iterator.rb +234 -0
- data/lib/ruote/exp/fe_let.rb +75 -0
- data/lib/ruote/exp/fe_listen.rb +304 -0
- data/lib/ruote/exp/fe_lose.rb +110 -0
- data/lib/ruote/exp/fe_noop.rb +45 -0
- data/lib/ruote/exp/fe_once.rb +215 -0
- data/lib/ruote/exp/fe_participant.rb +287 -0
- data/lib/ruote/exp/fe_read.rb +69 -0
- data/lib/ruote/exp/fe_redo.rb +82 -0
- data/lib/ruote/exp/fe_ref.rb +152 -0
- data/lib/ruote/exp/fe_registerp.rb +110 -0
- data/lib/ruote/exp/fe_reserve.rb +126 -0
- data/lib/ruote/exp/fe_restore.rb +102 -0
- data/lib/ruote/exp/fe_save.rb +72 -0
- data/lib/ruote/exp/fe_sequence.rb +59 -0
- data/lib/ruote/exp/fe_set.rb +154 -0
- data/lib/ruote/exp/fe_subprocess.rb +211 -0
- data/lib/ruote/exp/fe_that.rb +92 -0
- data/lib/ruote/exp/fe_undo.rb +67 -0
- data/lib/ruote/exp/fe_unregisterp.rb +69 -0
- data/lib/ruote/exp/fe_wait.rb +95 -0
- data/lib/ruote/exp/flowexpression.rb +886 -0
- data/lib/ruote/exp/iterator.rb +81 -0
- data/lib/ruote/exp/merge.rb +118 -0
- data/lib/ruote/exp/ro_attributes.rb +212 -0
- data/lib/ruote/exp/ro_filters.rb +136 -0
- data/lib/ruote/exp/ro_persist.rb +154 -0
- data/lib/ruote/exp/ro_variables.rb +189 -0
- data/lib/ruote/exp/ro_vf.rb +68 -0
- data/lib/ruote/fei.rb +260 -0
- data/lib/ruote/id/mnemo_wfid_generator.rb +43 -0
- data/lib/ruote/id/wfid_generator.rb +81 -0
- data/lib/ruote/log/default_history.rb +122 -0
- data/lib/ruote/log/pretty.rb +176 -0
- data/lib/ruote/log/storage_history.rb +159 -0
- data/lib/ruote/log/test_logger.rb +208 -0
- data/lib/ruote/log/wait_logger.rb +64 -0
- data/lib/ruote/part/block_participant.rb +137 -0
- data/lib/ruote/part/code_participant.rb +81 -0
- data/lib/ruote/part/engine_participant.rb +189 -0
- data/lib/ruote/part/local_participant.rb +138 -0
- data/lib/ruote/part/no_op_participant.rb +60 -0
- data/lib/ruote/part/null_participant.rb +54 -0
- data/lib/ruote/part/rev_participant.rb +169 -0
- data/lib/ruote/part/smtp_participant.rb +116 -0
- data/lib/ruote/part/storage_participant.rb +392 -0
- data/lib/ruote/part/template.rb +84 -0
- data/lib/ruote/participant.rb +7 -0
- data/lib/ruote/reader.rb +278 -0
- data/lib/ruote/reader/json.rb +49 -0
- data/lib/ruote/reader/radial.rb +290 -0
- data/lib/ruote/reader/ruby_dsl.rb +186 -0
- data/lib/ruote/reader/xml.rb +99 -0
- data/lib/ruote/receiver/base.rb +212 -0
- data/lib/ruote/storage/base.rb +364 -0
- data/lib/ruote/storage/composite_storage.rb +121 -0
- data/lib/ruote/storage/fs_storage.rb +139 -0
- data/lib/ruote/storage/hash_storage.rb +211 -0
- data/lib/ruote/svc/dispatch_pool.rb +158 -0
- data/lib/ruote/svc/dollar_sub.rb +298 -0
- data/lib/ruote/svc/error_handler.rb +138 -0
- data/lib/ruote/svc/expression_map.rb +97 -0
- data/lib/ruote/svc/participant_list.rb +397 -0
- data/lib/ruote/svc/tracker.rb +172 -0
- data/lib/ruote/svc/treechecker.rb +141 -0
- data/lib/ruote/tree_dot.rb +85 -0
- data/lib/ruote/util/filter.rb +525 -0
- data/lib/ruote/util/hashdot.rb +79 -0
- data/lib/ruote/util/look.rb +128 -0
- data/lib/ruote/util/lookup.rb +127 -0
- data/lib/ruote/util/misc.rb +167 -0
- data/lib/ruote/util/ometa.rb +71 -0
- data/lib/ruote/util/serializer.rb +103 -0
- data/lib/ruote/util/subprocess.rb +88 -0
- data/lib/ruote/util/time.rb +100 -0
- data/lib/ruote/util/tree.rb +58 -0
- data/lib/ruote/version.rb +29 -0
- data/lib/ruote/worker.rb +386 -0
- data/lib/ruote/workitem.rb +394 -0
- data/phil.txt +14 -0
- data/ruote.gemspec +44 -0
- data/test/bm/ci.rb +55 -0
- data/test/bm/ici.rb +71 -0
- data/test/bm/juuman.rb +54 -0
- data/test/bm/launch_bench.rb +37 -0
- data/test/bm/load_26c.rb +97 -0
- data/test/bm/mega.rb +64 -0
- data/test/bm/seq_thousand.rb +31 -0
- data/test/bm/t.rb +35 -0
- data/test/functional/base.rb +247 -0
- data/test/functional/concurrent_base.rb +98 -0
- data/test/functional/crunner.rb +31 -0
- data/test/functional/ct_0_concurrence.rb +65 -0
- data/test/functional/ct_1_iterator.rb +67 -0
- data/test/functional/ct_2_cancel.rb +81 -0
- data/test/functional/eft_0_process_definition.rb +65 -0
- data/test/functional/eft_10_cancel_process.rb +46 -0
- data/test/functional/eft_11_wait.rb +109 -0
- data/test/functional/eft_12_listen.rb +500 -0
- data/test/functional/eft_13_iterator.rb +342 -0
- data/test/functional/eft_14_cursor.rb +456 -0
- data/test/functional/eft_15_loop.rb +69 -0
- data/test/functional/eft_16_if.rb +183 -0
- data/test/functional/eft_17_equals.rb +55 -0
- data/test/functional/eft_18_concurrent_iterator.rb +410 -0
- data/test/functional/eft_19_reserve.rb +136 -0
- data/test/functional/eft_1_echo.rb +68 -0
- data/test/functional/eft_20_save.rb +116 -0
- data/test/functional/eft_21_restore.rb +61 -0
- data/test/functional/eft_22_noop.rb +28 -0
- data/test/functional/eft_23_apply.rb +168 -0
- data/test/functional/eft_24_add_branches.rb +98 -0
- data/test/functional/eft_25_command.rb +28 -0
- data/test/functional/eft_26_error.rb +77 -0
- data/test/functional/eft_27_inc.rb +280 -0
- data/test/functional/eft_28_once.rb +135 -0
- data/test/functional/eft_29_cron.rb +64 -0
- data/test/functional/eft_2_sequence.rb +58 -0
- data/test/functional/eft_30_ref.rb +155 -0
- data/test/functional/eft_31_registerp.rb +130 -0
- data/test/functional/eft_32_lose.rb +93 -0
- data/test/functional/eft_33_let.rb +31 -0
- data/test/functional/eft_34_given.rb +123 -0
- data/test/functional/eft_35_filter.rb +375 -0
- data/test/functional/eft_36_read.rb +95 -0
- data/test/functional/eft_3_participant.rb +149 -0
- data/test/functional/eft_4_set.rb +296 -0
- data/test/functional/eft_5_subprocess.rb +163 -0
- data/test/functional/eft_6_concurrence.rb +304 -0
- data/test/functional/eft_7_forget.rb +61 -0
- data/test/functional/eft_8_undo.rb +114 -0
- data/test/functional/eft_9_redo.rb +138 -0
- data/test/functional/ft_0_worker.rb +65 -0
- data/test/functional/ft_10_dollar.rb +304 -0
- data/test/functional/ft_11_recursion.rb +109 -0
- data/test/functional/ft_12_launchitem.rb +43 -0
- data/test/functional/ft_13_variables.rb +151 -0
- data/test/functional/ft_14_re_apply.rb +324 -0
- data/test/functional/ft_15_timeout.rb +226 -0
- data/test/functional/ft_16_participant_params.rb +98 -0
- data/test/functional/ft_17_conditional.rb +102 -0
- data/test/functional/ft_18_kill.rb +138 -0
- data/test/functional/ft_19_participant_code.rb +67 -0
- data/test/functional/ft_1_process_status.rb +796 -0
- data/test/functional/ft_20_storage_participant.rb +543 -0
- data/test/functional/ft_21_forget.rb +153 -0
- data/test/functional/ft_22_process_definitions.rb +90 -0
- data/test/functional/ft_23_load_defs.rb +79 -0
- data/test/functional/ft_24_block_participant.rb +235 -0
- data/test/functional/ft_25_receiver.rb +207 -0
- data/test/functional/ft_26_participant_rtimeout.rb +179 -0
- data/test/functional/ft_27_var_indirection.rb +128 -0
- data/test/functional/ft_28_null_noop_participants.rb +51 -0
- data/test/functional/ft_29_part_template.rb +60 -0
- data/test/functional/ft_2_errors.rb +380 -0
- data/test/functional/ft_30_smtp_participant.rb +122 -0
- data/test/functional/ft_31_part_blocking.rb +72 -0
- data/test/functional/ft_33_participant_subprocess_priority.rb +32 -0
- data/test/functional/ft_34_cursor_rewind.rb +101 -0
- data/test/functional/ft_35_add_service.rb +56 -0
- data/test/functional/ft_36_storage_history.rb +150 -0
- data/test/functional/ft_37_default_history.rb +109 -0
- data/test/functional/ft_38_participant_more.rb +193 -0
- data/test/functional/ft_39_wait_for.rb +136 -0
- data/test/functional/ft_3_participant_registration.rb +574 -0
- data/test/functional/ft_40_wait_logger.rb +62 -0
- data/test/functional/ft_41_participants.rb +91 -0
- data/test/functional/ft_42_storage_copy.rb +71 -0
- data/test/functional/ft_43_participant_on_reply.rb +87 -0
- data/test/functional/ft_44_var_participant.rb +35 -0
- data/test/functional/ft_45_participant_accept.rb +64 -0
- data/test/functional/ft_46_launch_single.rb +83 -0
- data/test/functional/ft_47_wfid_generator.rb +54 -0
- data/test/functional/ft_48_lose.rb +112 -0
- data/test/functional/ft_49_engine_on_error.rb +201 -0
- data/test/functional/ft_4_cancel.rb +132 -0
- data/test/functional/ft_50_engine_config.rb +22 -0
- data/test/functional/ft_51_misc.rb +67 -0
- data/test/functional/ft_52_case.rb +134 -0
- data/test/functional/ft_53_engine_on_terminate.rb +95 -0
- data/test/functional/ft_54_patterns.rb +104 -0
- data/test/functional/ft_55_engine_participant.rb +303 -0
- data/test/functional/ft_56_filter_attribute.rb +259 -0
- data/test/functional/ft_57_rev_participant.rb +252 -0
- data/test/functional/ft_58_workitem.rb +69 -0
- data/test/functional/ft_59_pause.rb +343 -0
- data/test/functional/ft_5_on_error.rb +384 -0
- data/test/functional/ft_60_code_participant.rb +45 -0
- data/test/functional/ft_61_trailing_fields.rb +34 -0
- data/test/functional/ft_62_exp_name_and_dollar_substitution.rb +35 -0
- data/test/functional/ft_6_on_cancel.rb +221 -0
- data/test/functional/ft_7_tags.rb +177 -0
- data/test/functional/ft_8_participant_consumption.rb +124 -0
- data/test/functional/ft_9_subprocesses.rb +146 -0
- data/test/functional/restart_base.rb +34 -0
- data/test/functional/rt_0_wait.rb +55 -0
- data/test/functional/rt_1_listen.rb +56 -0
- data/test/functional/rt_2_errors.rb +56 -0
- data/test/functional/rt_3_once.rb +70 -0
- data/test/functional/rt_4_cron.rb +64 -0
- data/test/functional/rt_5_timeout.rb +60 -0
- data/test/functional/rtest.rb +8 -0
- data/test/functional/storage_helper.rb +93 -0
- data/test/functional/test.rb +44 -0
- data/test/functional/vertical.rb +46 -0
- data/test/path_helper.rb +15 -0
- data/test/test.rb +13 -0
- data/test/test_helper.rb +28 -0
- data/test/unit/storage.rb +428 -0
- data/test/unit/storages.rb +37 -0
- data/test/unit/test.rb +28 -0
- data/test/unit/ut_0_ruby_reader.rb +223 -0
- data/test/unit/ut_11_lookup.rb +122 -0
- data/test/unit/ut_13_serializer.rb +65 -0
- data/test/unit/ut_14_is_uri.rb +28 -0
- data/test/unit/ut_15_util.rb +57 -0
- data/test/unit/ut_16_reader.rb +225 -0
- data/test/unit/ut_18_engine.rb +47 -0
- data/test/unit/ut_19_part_template.rb +86 -0
- data/test/unit/ut_1_fei.rb +165 -0
- data/test/unit/ut_20_composite_storage.rb +74 -0
- data/test/unit/ut_21_svc_participant_list.rb +46 -0
- data/test/unit/ut_22_filter.rb +1094 -0
- data/test/unit/ut_23_svc_tracker.rb +48 -0
- data/test/unit/ut_24_radial_reader.rb +332 -0
- data/test/unit/ut_25_merge.rb +113 -0
- data/test/unit/ut_3_wait_logger.rb +39 -0
- data/test/unit/ut_4_expmap.rb +20 -0
- data/test/unit/ut_5_tree.rb +54 -0
- data/test/unit/ut_6_condition.rb +303 -0
- data/test/unit/ut_7_workitem.rb +99 -0
- data/test/unit/ut_8_tree_to_dot.rb +72 -0
- data/test/unit/ut_9_xml_reader.rb +61 -0
- metadata +504 -0
@@ -0,0 +1,92 @@
|
|
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
|
+
require 'ruote/exp/fe_sequence'
|
26
|
+
|
27
|
+
|
28
|
+
module Ruote::Exp
|
29
|
+
|
30
|
+
#
|
31
|
+
# The 'that' and the 'of' expressions are used in conjuction with
|
32
|
+
# the 'given' expression (GivenExpression).
|
33
|
+
#
|
34
|
+
# In can be used 'standalone', it thus becomes then merely an 'if'.
|
35
|
+
#
|
36
|
+
# The children of the that/of are executed if the condition evaluates to
|
37
|
+
# true. The children are executed one by one, as if the that/of were
|
38
|
+
# a sequence.
|
39
|
+
#
|
40
|
+
# given '${status}' do
|
41
|
+
# that '${location} == CH' do
|
42
|
+
# set 'f:bank' => 'UBS'
|
43
|
+
# subprocess 'buy_chocolate'
|
44
|
+
# end
|
45
|
+
# of 'ready' do
|
46
|
+
# participant 'saleshead', :msg => 'customer ready'
|
47
|
+
# participant 'salesman', :task => 'visiter customer'
|
48
|
+
# end
|
49
|
+
# of 'over' do
|
50
|
+
# participant 'manager', :msg => 'process over'
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# (Yes, I know, it's a poor example).
|
55
|
+
#
|
56
|
+
class ThatExpression < SequenceExpression
|
57
|
+
|
58
|
+
names :that, :of
|
59
|
+
|
60
|
+
def reply(workitem)
|
61
|
+
|
62
|
+
if workitem['fei'] == h.fei # apply --> reply
|
63
|
+
|
64
|
+
cond = attribute(:t) || attribute(:test) || attribute_text
|
65
|
+
|
66
|
+
if name == 'of'
|
67
|
+
comparator = cond.match(/^\s*\/.*\/\s*$/) ? '=~' : '=='
|
68
|
+
cond = "#{workitem['fields']['__given__']} #{comparator} #{cond}"
|
69
|
+
end
|
70
|
+
|
71
|
+
h.result = Condition.true?(cond)
|
72
|
+
|
73
|
+
if h.result
|
74
|
+
apply_child(0, workitem)
|
75
|
+
else
|
76
|
+
reply_to_parent(workitem)
|
77
|
+
end
|
78
|
+
|
79
|
+
else # reply from child
|
80
|
+
|
81
|
+
super
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def reply_to_parent(workitem)
|
86
|
+
|
87
|
+
workitem['fields']['__result__'] = h.result
|
88
|
+
super
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
# Made in Japan.
|
23
|
+
#++
|
24
|
+
|
25
|
+
|
26
|
+
module Ruote::Exp
|
27
|
+
|
28
|
+
#
|
29
|
+
# Undoes (cancels) another expression referred by its tag.
|
30
|
+
#
|
31
|
+
# pdef = Ruote.process_definition do
|
32
|
+
# concurrence do
|
33
|
+
# alpha :tag => 'kilroy'
|
34
|
+
# undo :ref => 'kilroy'
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# This example is rather tiny, but it shows a process branch (undo) cancelling
|
39
|
+
# another (alpha).
|
40
|
+
#
|
41
|
+
# == cancel
|
42
|
+
#
|
43
|
+
# This expression is aliased to 'cancel'
|
44
|
+
#
|
45
|
+
# cancel :ref => 'invoicing_stage'
|
46
|
+
#
|
47
|
+
class UndoExpression < FlowExpression
|
48
|
+
|
49
|
+
names :undo, :cancel
|
50
|
+
|
51
|
+
def apply
|
52
|
+
|
53
|
+
ref = attribute(:ref) || attribute_text
|
54
|
+
tag = ref ? lookup_variable(ref) : nil
|
55
|
+
|
56
|
+
@context.storage.put_msg('cancel', 'fei' => tag) if Ruote.is_a_fei?(tag)
|
57
|
+
|
58
|
+
reply_to_parent(h.applied_workitem)
|
59
|
+
end
|
60
|
+
|
61
|
+
def reply(workitem)
|
62
|
+
|
63
|
+
# never called
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
@@ -0,0 +1,69 @@
|
|
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
|
+
require 'ruote/exp/fe_registerp'
|
26
|
+
|
27
|
+
|
28
|
+
module Ruote::Exp
|
29
|
+
|
30
|
+
#
|
31
|
+
# Unregisters a participant.
|
32
|
+
#
|
33
|
+
# Ruote.process_definition do
|
34
|
+
# unregisterp 'alfred'
|
35
|
+
# unregisterp :name => 'bob'
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# Shows the same behaviour as
|
39
|
+
#
|
40
|
+
# engine.unregister_participant 'alfred'
|
41
|
+
# engine.unregister_participant 'bob'
|
42
|
+
#
|
43
|
+
# The expression 'registerp' can be used to register participants from
|
44
|
+
# a process definition.
|
45
|
+
#
|
46
|
+
class UnregisterpExpression < RegisterpExpression
|
47
|
+
|
48
|
+
names :unregisterp
|
49
|
+
|
50
|
+
def apply
|
51
|
+
|
52
|
+
registerp_allowed?
|
53
|
+
|
54
|
+
name = attribute(:name) || attribute_text
|
55
|
+
|
56
|
+
result = begin
|
57
|
+
context.engine.unregister_participant(name)
|
58
|
+
true
|
59
|
+
rescue
|
60
|
+
false
|
61
|
+
end
|
62
|
+
|
63
|
+
h.applied_workitem['fields']['__result__'] = result
|
64
|
+
|
65
|
+
reply_to_parent(h.applied_workitem)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
@@ -0,0 +1,95 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
# Made in Japan.
|
23
|
+
#++
|
24
|
+
|
25
|
+
|
26
|
+
module Ruote::Exp
|
27
|
+
|
28
|
+
#
|
29
|
+
# Waits (sleeps) for a given period of time before resuming the flow.
|
30
|
+
#
|
31
|
+
# sequence do
|
32
|
+
# accounting :task => 'invoice'
|
33
|
+
# wait '30d' # 30 days
|
34
|
+
# accounting :task => 'check if customer paid'
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# '_sleep' is also OK
|
38
|
+
#
|
39
|
+
# _sleep '7d10h' # 7 days and 10 hours
|
40
|
+
#
|
41
|
+
# (the underscore prevents collision with Ruby's sleep method)
|
42
|
+
#
|
43
|
+
# == :for and :until
|
44
|
+
#
|
45
|
+
# 'wait' accepts as well :
|
46
|
+
#
|
47
|
+
# wait :for => '30d' # wait for 30 days
|
48
|
+
# wait :until => '2011/12/10 12:00:00' # any parseable date/time format
|
49
|
+
#
|
50
|
+
class WaitExpression < FlowExpression
|
51
|
+
|
52
|
+
names :wait, :sleep
|
53
|
+
|
54
|
+
def apply
|
55
|
+
|
56
|
+
h.for = attribute(:for) || attribute_text
|
57
|
+
h.until = attribute(:until)
|
58
|
+
|
59
|
+
s = h.for
|
60
|
+
s = h.until if s == ''
|
61
|
+
|
62
|
+
h.at = s
|
63
|
+
|
64
|
+
if h.at
|
65
|
+
|
66
|
+
h.schedule_id = @context.storage.put_schedule(
|
67
|
+
'at',
|
68
|
+
h.fei,
|
69
|
+
h.at,
|
70
|
+
'action' => 'reply',
|
71
|
+
'fei' => h.fei,
|
72
|
+
'workitem' => h.applied_workitem)
|
73
|
+
|
74
|
+
persist_or_raise
|
75
|
+
|
76
|
+
else
|
77
|
+
|
78
|
+
reply_to_parent(h.applied_workitem)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
#--
|
83
|
+
# no need to override, simply reply to parent expression.
|
84
|
+
#def reply (workitem)
|
85
|
+
#end
|
86
|
+
#++
|
87
|
+
|
88
|
+
def cancel(flavour)
|
89
|
+
|
90
|
+
@context.storage.delete_schedule(h.schedule_id)
|
91
|
+
reply_to_parent(h.applied_workitem)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
@@ -0,0 +1,886 @@
|
|
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
|
+
require 'ruote/util/time'
|
26
|
+
require 'ruote/util/ometa'
|
27
|
+
require 'ruote/util/hashdot'
|
28
|
+
|
29
|
+
|
30
|
+
module Ruote::Exp
|
31
|
+
|
32
|
+
#
|
33
|
+
# Ruote is a process definition interpreter. It doesn't directly "read"
|
34
|
+
# process definitions, it relies on a parser/generator to produce "abstract
|
35
|
+
# syntax trees" that look like
|
36
|
+
#
|
37
|
+
# [ expression_name, { ... attributes ... }, [ children_expressions ] ]
|
38
|
+
#
|
39
|
+
# The nodes (and leaves) in the trees are expressions. This is the base
|
40
|
+
# class for all expressions.
|
41
|
+
#
|
42
|
+
# The most visible expressions are "define", "sequence" and "participant".
|
43
|
+
# Think :
|
44
|
+
#
|
45
|
+
# pdef = Ruote.process_definition do
|
46
|
+
# sequence do
|
47
|
+
# participant :ref => 'customer'
|
48
|
+
# participant :ref => 'accounting'
|
49
|
+
# participant :ref => 'logistics'
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# Each node is an expression...
|
54
|
+
#
|
55
|
+
class FlowExpression
|
56
|
+
|
57
|
+
include Ruote::WithH
|
58
|
+
include Ruote::WithMeta
|
59
|
+
|
60
|
+
require 'ruote/exp/ro_persist'
|
61
|
+
require 'ruote/exp/ro_attributes'
|
62
|
+
require 'ruote/exp/ro_variables'
|
63
|
+
require 'ruote/exp/ro_filters'
|
64
|
+
require 'ruote/exp/ro_vf'
|
65
|
+
|
66
|
+
COMMON_ATT_KEYS = %w[
|
67
|
+
if unless forget timeout on_error on_cancel on_timeout ]
|
68
|
+
|
69
|
+
attr_reader :h
|
70
|
+
|
71
|
+
h_reader :variables
|
72
|
+
h_reader :created_time
|
73
|
+
h_reader :original_tree
|
74
|
+
h_reader :updated_tree
|
75
|
+
|
76
|
+
h_reader :children
|
77
|
+
h_reader :state
|
78
|
+
|
79
|
+
h_reader :on_error
|
80
|
+
h_reader :on_cancel
|
81
|
+
h_reader :on_timeout
|
82
|
+
|
83
|
+
attr_reader :context
|
84
|
+
|
85
|
+
# Mostly used when the expression is returned via Ruote::Engine#ps(wfid) or
|
86
|
+
# Ruote::Engine#processes(). If an error occurred for this flow expression,
|
87
|
+
# #ps will set this error field so that it yields the ProcessError.
|
88
|
+
#
|
89
|
+
# So, for short, usually, this attribute yields nil.
|
90
|
+
#
|
91
|
+
attr_accessor :error
|
92
|
+
|
93
|
+
def initialize(context, h)
|
94
|
+
|
95
|
+
@context = context
|
96
|
+
|
97
|
+
@msg = nil
|
98
|
+
# contains generally the msg the expression got instantiated for
|
99
|
+
|
100
|
+
self.h = h
|
101
|
+
|
102
|
+
h._id ||= Ruote.to_storage_id(h.fei)
|
103
|
+
h['type'] ||= 'expressions'
|
104
|
+
h.name ||= self.class.expression_names.first
|
105
|
+
h.children ||= []
|
106
|
+
h.applied_workitem['fei'] = h.fei
|
107
|
+
h.created_time ||= Ruote.now_to_utc_s
|
108
|
+
|
109
|
+
h.on_cancel ||= attribute(:on_cancel)
|
110
|
+
h.on_error ||= attribute(:on_error)
|
111
|
+
h.on_timeout ||= attribute(:on_timeout)
|
112
|
+
end
|
113
|
+
|
114
|
+
def h=(hash)
|
115
|
+
@h = hash
|
116
|
+
class << h
|
117
|
+
include Ruote::HashDot
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Returns the Ruote::FlowExpressionId for this expression.
|
122
|
+
#
|
123
|
+
def fei
|
124
|
+
|
125
|
+
Ruote::FlowExpressionId.new(h.fei)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Returns the Ruote::FlowExpressionIf of the parent expression, or nil
|
129
|
+
# if there is no parent expression.
|
130
|
+
#
|
131
|
+
def parent_id
|
132
|
+
|
133
|
+
h.parent_id ? Ruote::FlowExpressionId.new(h.parent_id) : nil
|
134
|
+
end
|
135
|
+
|
136
|
+
# Fetches the parent expression, or returns nil if there is no parent
|
137
|
+
# expression.
|
138
|
+
#
|
139
|
+
def parent
|
140
|
+
|
141
|
+
Ruote::Exp::FlowExpression.fetch(@context, h.parent_id)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Turns this FlowExpression instance into a Hash (well, just hands back
|
145
|
+
# the base hash behind it).
|
146
|
+
#
|
147
|
+
def to_h
|
148
|
+
|
149
|
+
@h
|
150
|
+
end
|
151
|
+
|
152
|
+
# Instantiates expression back from hash.
|
153
|
+
#
|
154
|
+
def self.from_h(context, h)
|
155
|
+
|
156
|
+
exp_class = context.expmap.expression_class(h['name'])
|
157
|
+
|
158
|
+
exp_class.new(context, h)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Fetches an expression from the storage and readies it for service.
|
162
|
+
#
|
163
|
+
def self.fetch(context, fei)
|
164
|
+
|
165
|
+
return nil if fei.nil?
|
166
|
+
|
167
|
+
fexp = context.storage.get('expressions', Ruote.to_storage_id(fei))
|
168
|
+
|
169
|
+
fexp ? from_h(context, fexp) : nil
|
170
|
+
end
|
171
|
+
|
172
|
+
#--
|
173
|
+
# META
|
174
|
+
#++
|
175
|
+
|
176
|
+
# Keeping track of names and aliases for the expression
|
177
|
+
#
|
178
|
+
def self.names(*exp_names)
|
179
|
+
|
180
|
+
exp_names = exp_names.collect { |n| n.to_s }
|
181
|
+
meta_def(:expression_names) { exp_names }
|
182
|
+
end
|
183
|
+
|
184
|
+
#--
|
185
|
+
# apply/reply
|
186
|
+
#++
|
187
|
+
|
188
|
+
# Called by the worker when it has something to do for a FlowExpression.
|
189
|
+
#
|
190
|
+
def self.do_action(context, msg)
|
191
|
+
|
192
|
+
fei = msg['fei']
|
193
|
+
action = msg['action']
|
194
|
+
|
195
|
+
#p msg unless fei
|
196
|
+
|
197
|
+
if action == 'reply' && fei['engine_id'] != context.engine_id
|
198
|
+
#
|
199
|
+
# the reply has to go to another engine, let's locate the
|
200
|
+
# 'engine participant' and give it the workitem/reply
|
201
|
+
#
|
202
|
+
# see ft_37 for a test/example
|
203
|
+
|
204
|
+
engine_participant =
|
205
|
+
context.plist.lookup(fei['engine_id'], msg['workitem'])
|
206
|
+
|
207
|
+
raise(
|
208
|
+
"no EngineParticipant found under name '#{fei['engine_id']}'"
|
209
|
+
) unless engine_participant
|
210
|
+
|
211
|
+
engine_participant.reply(fei, msg['workitem'])
|
212
|
+
return
|
213
|
+
end
|
214
|
+
|
215
|
+
# normal case
|
216
|
+
|
217
|
+
fexp = nil
|
218
|
+
|
219
|
+
3.times do
|
220
|
+
fexp = fetch(context, msg['fei'])
|
221
|
+
break if fexp
|
222
|
+
sleep 0.028
|
223
|
+
end
|
224
|
+
# this retry system is only useful with ruote-couch
|
225
|
+
|
226
|
+
fexp.send("do_#{action}", msg) if fexp
|
227
|
+
end
|
228
|
+
|
229
|
+
# Called by the worker when it has just created this FlowExpression and
|
230
|
+
# wants to apply it.
|
231
|
+
#
|
232
|
+
def do_apply(msg)
|
233
|
+
|
234
|
+
@msg = Ruote.fulldup(msg)
|
235
|
+
|
236
|
+
if not Condition.apply?(attribute(:if), attribute(:unless))
|
237
|
+
|
238
|
+
return reply_to_parent(h.applied_workitem)
|
239
|
+
end
|
240
|
+
|
241
|
+
if attribute(:forget).to_s == 'true'
|
242
|
+
|
243
|
+
pi = h.parent_id
|
244
|
+
wi = Ruote.fulldup(h.applied_workitem)
|
245
|
+
|
246
|
+
h.variables = compile_variables
|
247
|
+
h.parent_id = nil
|
248
|
+
h.forgotten = true
|
249
|
+
|
250
|
+
@context.storage.put_msg('reply', 'fei' => pi, 'workitem' => wi) if pi
|
251
|
+
# reply to parent immediately (if there is a parent)
|
252
|
+
|
253
|
+
elsif attribute(:lose).to_s == 'true'
|
254
|
+
|
255
|
+
h.lost = true
|
256
|
+
end
|
257
|
+
|
258
|
+
filter
|
259
|
+
|
260
|
+
consider_tag
|
261
|
+
consider_timeout
|
262
|
+
|
263
|
+
apply
|
264
|
+
end
|
265
|
+
|
266
|
+
# FlowExpression call this method when they're done and they want their
|
267
|
+
# parent expression to take over (it will end up calling the #reply of
|
268
|
+
# the parent expression).
|
269
|
+
#
|
270
|
+
def reply_to_parent(workitem, delete=true)
|
271
|
+
|
272
|
+
filter(workitem)
|
273
|
+
|
274
|
+
if h.tagname
|
275
|
+
|
276
|
+
unset_variable(h.tagname)
|
277
|
+
|
278
|
+
Ruote::Workitem.remove_tag(workitem, h.tagname)
|
279
|
+
|
280
|
+
@context.storage.put_msg(
|
281
|
+
'left_tag',
|
282
|
+
'tag' => h.tagname,
|
283
|
+
'fei' => h.fei,
|
284
|
+
'workitem' => workitem)
|
285
|
+
end
|
286
|
+
|
287
|
+
if h.timeout_schedule_id && h.state != 'timing_out'
|
288
|
+
|
289
|
+
@context.storage.delete_schedule(h.timeout_schedule_id)
|
290
|
+
end
|
291
|
+
|
292
|
+
if h.state == 'failing' # on_error is implicit (#fail got called)
|
293
|
+
|
294
|
+
trigger('on_error', workitem)
|
295
|
+
|
296
|
+
elsif h.state == 'cancelling' and h.on_cancel
|
297
|
+
|
298
|
+
trigger('on_cancel', workitem)
|
299
|
+
|
300
|
+
elsif h.state == 'cancelling' and h.on_re_apply
|
301
|
+
|
302
|
+
trigger('on_re_apply', workitem)
|
303
|
+
|
304
|
+
elsif h.state == 'timing_out' and h.on_timeout
|
305
|
+
|
306
|
+
trigger('on_timeout', workitem)
|
307
|
+
|
308
|
+
elsif h.lost and h.state == nil
|
309
|
+
|
310
|
+
# do not reply, sit here (and wait for cancellation probably)
|
311
|
+
|
312
|
+
else # vanilla reply
|
313
|
+
|
314
|
+
(do_unpersist || return) if delete
|
315
|
+
# remove expression from storage
|
316
|
+
|
317
|
+
if h.parent_id
|
318
|
+
|
319
|
+
@context.storage.put_msg(
|
320
|
+
'reply',
|
321
|
+
'fei' => h.parent_id,
|
322
|
+
'workitem' => workitem.merge!('fei' => h.fei),
|
323
|
+
'updated_tree' => h.updated_tree) # nil most of the time
|
324
|
+
else
|
325
|
+
|
326
|
+
@context.storage.put_msg(
|
327
|
+
h.forgotten ? 'ceased' : 'terminated',
|
328
|
+
'wfid' => h.fei['wfid'],
|
329
|
+
'fei' => h.fei,
|
330
|
+
'workitem' => workitem)
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
# Wraps #reply (does the administrative part of the reply work).
|
336
|
+
#
|
337
|
+
def do_reply(msg)
|
338
|
+
|
339
|
+
@msg = Ruote.fulldup(msg)
|
340
|
+
# keeping the message, for 'retry' in collision cases
|
341
|
+
|
342
|
+
workitem = msg['workitem']
|
343
|
+
fei = workitem['fei']
|
344
|
+
|
345
|
+
if ut = msg['updated_tree']
|
346
|
+
ct = tree.dup
|
347
|
+
ct.last[Ruote::FlowExpressionId.child_id(fei)] = ut
|
348
|
+
update_tree(ct)
|
349
|
+
end
|
350
|
+
|
351
|
+
h.children.delete(fei)
|
352
|
+
# accept without any check ?
|
353
|
+
|
354
|
+
if h.state == 'paused'
|
355
|
+
|
356
|
+
(h['paused_replies'] ||= []) << msg
|
357
|
+
|
358
|
+
do_persist
|
359
|
+
|
360
|
+
elsif h.state != nil # failing or timing out ...
|
361
|
+
|
362
|
+
if h.children.size < 1
|
363
|
+
reply_to_parent(workitem)
|
364
|
+
else
|
365
|
+
persist_or_raise # for the updated h.children
|
366
|
+
end
|
367
|
+
|
368
|
+
else # vanilla reply
|
369
|
+
|
370
|
+
reply(workitem)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
# (only makes sense for the participant expression though)
|
375
|
+
#
|
376
|
+
alias :do_receive :do_reply
|
377
|
+
|
378
|
+
# A default implementation for all the expressions.
|
379
|
+
#
|
380
|
+
def reply(workitem)
|
381
|
+
|
382
|
+
reply_to_parent(workitem)
|
383
|
+
end
|
384
|
+
|
385
|
+
# The raw handling of messages passed to expressions (the fine handling
|
386
|
+
# is done in the #cancel method).
|
387
|
+
#
|
388
|
+
def do_cancel(msg)
|
389
|
+
|
390
|
+
@msg = Ruote.fulldup(msg)
|
391
|
+
|
392
|
+
flavour = msg['flavour']
|
393
|
+
|
394
|
+
return if h.state == 'cancelling' && flavour != 'kill'
|
395
|
+
# cancel on cancel gets discarded
|
396
|
+
|
397
|
+
return if h.state == 'failed' && flavour == 'timeout'
|
398
|
+
# do not timeout expressions that are "in error" (failed)
|
399
|
+
|
400
|
+
@msg = Ruote.fulldup(msg)
|
401
|
+
|
402
|
+
h.state = case flavour
|
403
|
+
when 'kill' then 'dying'
|
404
|
+
when 'timeout' then 'timing_out'
|
405
|
+
else 'cancelling'
|
406
|
+
end
|
407
|
+
|
408
|
+
h.applied_workitem['fields']['__timed_out__'] = [
|
409
|
+
h.fei, Ruote.now_to_utc_s, tree.first, compile_atts
|
410
|
+
] if h.state == 'timing_out'
|
411
|
+
|
412
|
+
if h.state == 'cancelling'
|
413
|
+
|
414
|
+
if t = msg['on_cancel']
|
415
|
+
|
416
|
+
h.on_cancel = t
|
417
|
+
|
418
|
+
elsif hra = msg['re_apply']
|
419
|
+
|
420
|
+
hra = {} if hra == true
|
421
|
+
|
422
|
+
h.on_re_apply = hra['tree'] || tree
|
423
|
+
|
424
|
+
if fs = hra['fields']
|
425
|
+
h.applied_workitem['fields'] = fs
|
426
|
+
end
|
427
|
+
if mfs = hra['merge_in_fields']
|
428
|
+
h.applied_workitem['fields'].merge!(mfs)
|
429
|
+
end
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
cancel(flavour)
|
434
|
+
end
|
435
|
+
|
436
|
+
# This default implementation cancels all the [registered] children
|
437
|
+
# of this expression.
|
438
|
+
#
|
439
|
+
def cancel(flavour)
|
440
|
+
|
441
|
+
return reply_to_parent(h.applied_workitem) if h.children.empty?
|
442
|
+
#
|
443
|
+
# there are no children, nothing to cancel, let's just reply to
|
444
|
+
# the parent expression
|
445
|
+
|
446
|
+
do_persist || return
|
447
|
+
#
|
448
|
+
# before firing the cancel message to the children
|
449
|
+
#
|
450
|
+
# if the do_persist returns false, it means it failed, implying this
|
451
|
+
# expression is stale, let's return, thus discarding this cancel message
|
452
|
+
|
453
|
+
children.each do |cfei|
|
454
|
+
#
|
455
|
+
# let's send a cancel message to each of the children
|
456
|
+
#
|
457
|
+
# maybe some of them are gone or have not yet been applied, anyway,
|
458
|
+
# the message are sent
|
459
|
+
|
460
|
+
@context.storage.put_msg(
|
461
|
+
'cancel',
|
462
|
+
'fei' => cfei,
|
463
|
+
'parent_id' => h.fei, # indicating that this is a "cancel child"
|
464
|
+
'flavour' => flavour)
|
465
|
+
end
|
466
|
+
|
467
|
+
#if ! children.find { |i| Ruote::Exp::FlowExpression.fetch(@context, i) }
|
468
|
+
# #
|
469
|
+
# # since none of the children could be found in the storage right now,
|
470
|
+
# # it could mean that all children are already done or it could mean
|
471
|
+
# # that they are not yet applied...
|
472
|
+
# #
|
473
|
+
# # just to be sure let's send a new cancel message to this expression
|
474
|
+
# #
|
475
|
+
# # it's very important, since if there is no child to cancel the parent
|
476
|
+
# # the flow might get stuck here
|
477
|
+
# @context.storage.put_msg(
|
478
|
+
# 'cancel',
|
479
|
+
# 'fei' => h.fei,
|
480
|
+
# 'flavour' => flavour)
|
481
|
+
#end
|
482
|
+
end
|
483
|
+
|
484
|
+
# Called when handling an on_error, will place itself in a 'failing' state
|
485
|
+
# and cancel the children (when the reply from the children comes back,
|
486
|
+
# the on_reply will get triggered).
|
487
|
+
#
|
488
|
+
def do_fail(msg)
|
489
|
+
|
490
|
+
@msg = Ruote.fulldup(msg)
|
491
|
+
|
492
|
+
@h['state'] = 'failing'
|
493
|
+
@h['applied_workitem'] = msg['workitem']
|
494
|
+
|
495
|
+
if h.children.size < 1
|
496
|
+
reply_to_parent(@h['applied_workitem'])
|
497
|
+
else
|
498
|
+
persist_or_raise
|
499
|
+
h.children.each { |i| @context.storage.put_msg('cancel', 'fei' => i) }
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
# Expression received a "pause" message. Will put the expression in the
|
504
|
+
# "paused" state and then pass the message to the children.
|
505
|
+
#
|
506
|
+
# If the expression is in a non-nil state (failed, timed_out, ...), the
|
507
|
+
# message will be ignored.
|
508
|
+
#
|
509
|
+
def do_pause(msg)
|
510
|
+
|
511
|
+
return if h.state != nil
|
512
|
+
|
513
|
+
h['state'] = 'paused'
|
514
|
+
|
515
|
+
do_persist || return
|
516
|
+
|
517
|
+
h.children.each { |i|
|
518
|
+
@context.storage.put_msg('pause', 'fei' => i)
|
519
|
+
} unless msg['breakpoint']
|
520
|
+
end
|
521
|
+
|
522
|
+
# Will "unpause" the expression (if it was paused), and trigger any
|
523
|
+
# 'paused_replies' (replies that came while the expression was paused).
|
524
|
+
#
|
525
|
+
def do_resume(msg)
|
526
|
+
|
527
|
+
return if h.state != 'paused'
|
528
|
+
|
529
|
+
h['state'] = nil
|
530
|
+
replies = h.delete('paused_replies') || []
|
531
|
+
|
532
|
+
do_persist || return
|
533
|
+
|
534
|
+
h.children.each { |i| @context.storage.put_msg('resume', 'fei' => i) }
|
535
|
+
# resume children
|
536
|
+
|
537
|
+
replies.each { |m| @context.storage.put_msg(m.delete('action'), m) }
|
538
|
+
# trigger replies
|
539
|
+
end
|
540
|
+
|
541
|
+
#--
|
542
|
+
# misc
|
543
|
+
#++
|
544
|
+
|
545
|
+
# Launches a subprocesses (usually called from the #apply of certain
|
546
|
+
# expression implementations.
|
547
|
+
#
|
548
|
+
def launch_sub(pos, subtree, opts={})
|
549
|
+
|
550
|
+
i = h.fei.merge(
|
551
|
+
'subid' => Ruote.generate_subid(h.fei.inspect),
|
552
|
+
'expid' => pos)
|
553
|
+
|
554
|
+
#p '=== launch_sub ==='
|
555
|
+
#p [ :launcher, h.fei['expid'], h.fei['subid'], h.fei['wfid'] ]
|
556
|
+
#p [ :launched, i['expid'], i['subid'], i['wfid'] ]
|
557
|
+
|
558
|
+
forget = opts[:forget]
|
559
|
+
|
560
|
+
register_child(i) unless forget
|
561
|
+
|
562
|
+
variables = (
|
563
|
+
forget ? compile_variables : {}
|
564
|
+
).merge!(opts[:variables] || {})
|
565
|
+
|
566
|
+
@context.storage.put_msg(
|
567
|
+
'launch',
|
568
|
+
'fei' => i,
|
569
|
+
'parent_id' => forget ? nil : h.fei,
|
570
|
+
'tree' => subtree,
|
571
|
+
'workitem' => opts[:workitem] || h.applied_workitem,
|
572
|
+
'variables' => variables,
|
573
|
+
'forgotten' => forget)
|
574
|
+
end
|
575
|
+
|
576
|
+
# Returns true if the given fei points to an expression in the parent
|
577
|
+
# chain of this expression.
|
578
|
+
#
|
579
|
+
def ancestor?(fei)
|
580
|
+
|
581
|
+
fei = fei.to_h if fei.respond_to?(:to_h)
|
582
|
+
|
583
|
+
return false unless h.parent_id
|
584
|
+
return true if h.parent_id == fei
|
585
|
+
|
586
|
+
parent.ancestor?(fei)
|
587
|
+
end
|
588
|
+
|
589
|
+
# Looks up "on_error" attribute
|
590
|
+
#
|
591
|
+
def lookup_on_error
|
592
|
+
|
593
|
+
if h.on_error
|
594
|
+
|
595
|
+
self
|
596
|
+
|
597
|
+
elsif h.parent_id
|
598
|
+
|
599
|
+
par = parent
|
600
|
+
# :( get_parent would probably be a better name for #parent
|
601
|
+
|
602
|
+
#if par.nil? && ($DEBUG || ARGV.include?('-d'))
|
603
|
+
# puts "~~"
|
604
|
+
# puts "parent gone for"
|
605
|
+
# puts "fei #{Ruote.sid(h.fei)}"
|
606
|
+
# puts "tree #{tree.inspect}"
|
607
|
+
# puts "replying to #{Ruote.sid(h.parent_id)}"
|
608
|
+
# puts "~~"
|
609
|
+
#end
|
610
|
+
# is sometimes helpful during debug sessions
|
611
|
+
|
612
|
+
par ? par.lookup_on_error : nil
|
613
|
+
|
614
|
+
else
|
615
|
+
|
616
|
+
nil
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
# Looks up parent with on_error attribute and triggers it
|
621
|
+
#
|
622
|
+
def handle_on_error(msg, error)
|
623
|
+
|
624
|
+
return false if h.state == 'failing'
|
625
|
+
|
626
|
+
oe_parent = lookup_on_error
|
627
|
+
|
628
|
+
return false unless oe_parent
|
629
|
+
# no parent with on_error attribute found
|
630
|
+
|
631
|
+
handler = oe_parent.on_error.to_s
|
632
|
+
|
633
|
+
return false if handler == ''
|
634
|
+
# empty on_error handler nullifies ancestor's on_error
|
635
|
+
|
636
|
+
workitem = msg['workitem']
|
637
|
+
|
638
|
+
workitem['fields']['__error__'] = {
|
639
|
+
'fei' => fei,
|
640
|
+
'at' => Ruote.now_to_utc_s,
|
641
|
+
'class' => error.class.to_s,
|
642
|
+
'message' => error.message,
|
643
|
+
'trace' => error.backtrace
|
644
|
+
}
|
645
|
+
|
646
|
+
@context.storage.put_msg(
|
647
|
+
'fail',
|
648
|
+
'fei' => oe_parent.h.fei,
|
649
|
+
'workitem' => workitem)
|
650
|
+
|
651
|
+
true # yes, error is being handled.
|
652
|
+
end
|
653
|
+
|
654
|
+
#--
|
655
|
+
# TREE
|
656
|
+
#++
|
657
|
+
|
658
|
+
# Returns the current version of the tree (returns the updated version
|
659
|
+
# if it got updated.
|
660
|
+
#
|
661
|
+
def tree
|
662
|
+
h.updated_tree || h.original_tree
|
663
|
+
end
|
664
|
+
|
665
|
+
# Updates the tree of this expression
|
666
|
+
#
|
667
|
+
# update_tree(t)
|
668
|
+
#
|
669
|
+
# will set the updated tree to t
|
670
|
+
#
|
671
|
+
# update_tree
|
672
|
+
#
|
673
|
+
# will copy (deep copy) the original tree as the updated_tree.
|
674
|
+
#
|
675
|
+
# Adding a child to a sequence expression :
|
676
|
+
#
|
677
|
+
# seq.update_tree
|
678
|
+
# seq.updated_tree[2] << [ 'participant', { 'ref' => 'bob' }, [] ]
|
679
|
+
# seq.do_persist
|
680
|
+
#
|
681
|
+
def update_tree(t=nil)
|
682
|
+
h.updated_tree = t || Ruote.fulldup(h.original_tree)
|
683
|
+
end
|
684
|
+
|
685
|
+
# Returns the name of this expression, like 'sequence', 'participant',
|
686
|
+
# 'cursor', etc...
|
687
|
+
#
|
688
|
+
def name
|
689
|
+
|
690
|
+
tree[0]
|
691
|
+
end
|
692
|
+
|
693
|
+
# Returns the attributes of this expression (like { 'ref' => 'toto' } or
|
694
|
+
# { 'timeout' => '2d' }.
|
695
|
+
#
|
696
|
+
def attributes
|
697
|
+
|
698
|
+
tree[1]
|
699
|
+
end
|
700
|
+
|
701
|
+
# Returns the "AST" view on the children of this expression...
|
702
|
+
#
|
703
|
+
def tree_children
|
704
|
+
|
705
|
+
tree[2]
|
706
|
+
end
|
707
|
+
|
708
|
+
protected
|
709
|
+
|
710
|
+
# Returns a Graphviz dot string representing this expression (and its
|
711
|
+
# children).
|
712
|
+
#
|
713
|
+
def to_dot(opts)
|
714
|
+
|
715
|
+
i = fei()
|
716
|
+
|
717
|
+
label = "#{[ i.wfid, i.subid, i.expid].join(' ')} #{tree.first}"
|
718
|
+
label += " (#{h.state})" if h.state
|
719
|
+
|
720
|
+
a = []
|
721
|
+
a << "\"#{i.to_storage_id}\" [ label=\"#{label}\" ];"
|
722
|
+
|
723
|
+
# parent
|
724
|
+
|
725
|
+
if h.parent_id
|
726
|
+
a << "\"#{i.to_storage_id}\" -> \"#{parent_id.to_storage_id}\";"
|
727
|
+
end
|
728
|
+
|
729
|
+
# children
|
730
|
+
|
731
|
+
h.children.each do |cfei|
|
732
|
+
a << "\"#{i.to_storage_id}\" -> \"#{Ruote.to_storage_id(cfei)}\";"
|
733
|
+
end
|
734
|
+
|
735
|
+
a
|
736
|
+
end
|
737
|
+
|
738
|
+
# Used locally but also by ConcurrenceExpression, when preparing children
|
739
|
+
# before they get applied.
|
740
|
+
#
|
741
|
+
def pre_apply_child(child_index, workitem, forget)
|
742
|
+
|
743
|
+
child_fei = h.fei.merge(
|
744
|
+
'expid' => "#{h.fei['expid']}_#{child_index}",
|
745
|
+
'subid' => Ruote.generate_subid(h.fei.inspect))
|
746
|
+
|
747
|
+
h.children << child_fei unless forget
|
748
|
+
|
749
|
+
msg = {
|
750
|
+
'fei' => child_fei,
|
751
|
+
'tree' => tree.last[child_index],
|
752
|
+
'parent_id' => forget ? nil : h.fei,
|
753
|
+
'variables' => forget ? compile_variables : nil,
|
754
|
+
'workitem' => workitem
|
755
|
+
}
|
756
|
+
msg['forgotten'] = true if forget
|
757
|
+
|
758
|
+
msg
|
759
|
+
end
|
760
|
+
|
761
|
+
# Used by expressions when, well, applying a child expression of theirs.
|
762
|
+
#
|
763
|
+
def apply_child(child_index, workitem, forget=false)
|
764
|
+
|
765
|
+
msg = pre_apply_child(child_index, workitem, forget)
|
766
|
+
|
767
|
+
persist_or_raise unless forget
|
768
|
+
# no need to persist the parent (this) if the child is to be forgotten
|
769
|
+
|
770
|
+
@context.storage.put_msg('apply', msg)
|
771
|
+
end
|
772
|
+
|
773
|
+
# Some expressions have to keep track of their (instantiated) children,
|
774
|
+
# this method does the registration (of the child's fei).
|
775
|
+
#
|
776
|
+
def register_child(fei)
|
777
|
+
|
778
|
+
h.children << fei
|
779
|
+
persist_or_raise
|
780
|
+
end
|
781
|
+
|
782
|
+
# Called to check if the expression has a :tag attribute. If yes,
|
783
|
+
# will register the tag in a variable (and in the workitem).
|
784
|
+
#
|
785
|
+
def consider_tag
|
786
|
+
|
787
|
+
if h.tagname = attribute(:tag)
|
788
|
+
|
789
|
+
set_variable(h.tagname, h.fei)
|
790
|
+
|
791
|
+
Ruote::Workitem.add_tag(h.applied_workitem, h.tagname)
|
792
|
+
|
793
|
+
@context.storage.put_msg(
|
794
|
+
'entered_tag',
|
795
|
+
'tag' => h.tagname,
|
796
|
+
'fei' => h.fei,
|
797
|
+
'workitem' => h.applied_workitem)
|
798
|
+
end
|
799
|
+
end
|
800
|
+
|
801
|
+
# Called by do_apply. Overriden in ParticipantExpression and RefExpression.
|
802
|
+
#
|
803
|
+
def consider_timeout
|
804
|
+
|
805
|
+
do_schedule_timeout(attribute(:timeout))
|
806
|
+
end
|
807
|
+
|
808
|
+
# Called by consider_timeout (FlowExpression) and schedule_timeout
|
809
|
+
# (ParticipantExpression).
|
810
|
+
#
|
811
|
+
def do_schedule_timeout(timeout)
|
812
|
+
|
813
|
+
timeout = timeout.to_s
|
814
|
+
|
815
|
+
return if timeout.strip == ''
|
816
|
+
|
817
|
+
h.timeout_schedule_id = @context.storage.put_schedule(
|
818
|
+
'at',
|
819
|
+
h.fei,
|
820
|
+
timeout,
|
821
|
+
'action' => 'cancel',
|
822
|
+
'fei' => h.fei,
|
823
|
+
'flavour' => 'timeout')
|
824
|
+
end
|
825
|
+
|
826
|
+
# (Called by trigger_on_cancel & co)
|
827
|
+
#
|
828
|
+
def supplant_with(tree, opts)
|
829
|
+
|
830
|
+
# at first, nuke self
|
831
|
+
|
832
|
+
r = try_unpersist
|
833
|
+
|
834
|
+
raise(
|
835
|
+
"failed to remove exp to supplant "+
|
836
|
+
"#{Ruote.to_storage_id(h.fei)} #{tree.first}"
|
837
|
+
) if r.respond_to?(:keys)
|
838
|
+
|
839
|
+
# then re-apply
|
840
|
+
|
841
|
+
if t = opts['trigger']
|
842
|
+
tree[1]['_triggered'] = t.to_s
|
843
|
+
end
|
844
|
+
|
845
|
+
@context.storage.put_msg(
|
846
|
+
'apply',
|
847
|
+
{ 'fei' => h.fei,
|
848
|
+
'parent_id' => h.parent_id,
|
849
|
+
'tree' => tree,
|
850
|
+
'workitem' => h.applied_workitem,
|
851
|
+
'variables' => h.variables
|
852
|
+
}.merge!(opts))
|
853
|
+
end
|
854
|
+
|
855
|
+
# 'on_{error|timeout|cancel|re_apply}' triggering
|
856
|
+
#
|
857
|
+
def trigger(on, workitem)
|
858
|
+
|
859
|
+
hon = h[on]
|
860
|
+
|
861
|
+
t = hon.is_a?(String) ? [ hon, {}, [] ] : hon
|
862
|
+
|
863
|
+
if on == 'on_error'
|
864
|
+
|
865
|
+
if hon == 'redo' or hon == 'retry'
|
866
|
+
|
867
|
+
t = tree
|
868
|
+
|
869
|
+
elsif hon == 'undo' or hon == 'pass'
|
870
|
+
|
871
|
+
h.state = 'failed'
|
872
|
+
reply_to_parent(workitem)
|
873
|
+
|
874
|
+
return
|
875
|
+
end
|
876
|
+
|
877
|
+
elsif on == 'on_timeout'
|
878
|
+
|
879
|
+
t = tree if hon == 'redo' or hon == 'retry'
|
880
|
+
end
|
881
|
+
|
882
|
+
supplant_with(t, 'trigger' => on)
|
883
|
+
end
|
884
|
+
end
|
885
|
+
end
|
886
|
+
|