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,154 @@
|
|
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
|
+
# The 'apply' expression is an advanced expression.
|
30
|
+
#
|
31
|
+
# It takes as input an AST and applies it. The AST may be placed in a field
|
32
|
+
# or a variable or passed directly to the apply.
|
33
|
+
#
|
34
|
+
# These apply examples :
|
35
|
+
#
|
36
|
+
# apply :tree => [ 'echo', { 'nada' => nil }, [] ]
|
37
|
+
#
|
38
|
+
# sequence do
|
39
|
+
# set :var => 'tree', :val => [ 'echo', { 'nada' => nil }, [] ]
|
40
|
+
# apply # looks by default in variable 'tree'
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# sequence do
|
44
|
+
# set :var => 't', :val => [ 'echo', { 'nada' => nil }, [] ]
|
45
|
+
# apply :tree_var => 't'
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# sequence do
|
49
|
+
# set :field => 't', :val => [ 'echo', { 'nada' => nil }, [] ]
|
50
|
+
# apply :tree_field => 't'
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# All are equivalent to
|
54
|
+
#
|
55
|
+
# echo 'nada'
|
56
|
+
#
|
57
|
+
#
|
58
|
+
# == apply and subprocesses
|
59
|
+
#
|
60
|
+
# There is an interesting way of using 'apply', it's close to the way of
|
61
|
+
# the Ruby "yield" expression.
|
62
|
+
#
|
63
|
+
# pdef = Ruote.process_definition 'test' do
|
64
|
+
# sequence do
|
65
|
+
# handle do
|
66
|
+
# participant 'alpha'
|
67
|
+
# end
|
68
|
+
# handle do
|
69
|
+
# participant 'bravo'
|
70
|
+
# end
|
71
|
+
# end
|
72
|
+
# define 'handle' do
|
73
|
+
# sequence do
|
74
|
+
# participant 'prepare_data'
|
75
|
+
# apply
|
76
|
+
# participant 'rearrange_data'
|
77
|
+
# end
|
78
|
+
# end
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# With this process definition, the particpant alpha and bravo are handed
|
82
|
+
# a workitem in sequence, but each time, the data gets prepared and
|
83
|
+
# re-arranged.
|
84
|
+
#
|
85
|
+
# 'apply' simply picks the value of the tree to apply in the local variable
|
86
|
+
# 'tree'.
|
87
|
+
#
|
88
|
+
# Passing variables to applied trees is possible :
|
89
|
+
#
|
90
|
+
# pdef = Ruote.process_definition do
|
91
|
+
# handle do
|
92
|
+
# participant '${v:target}', :message => 'x'
|
93
|
+
# end
|
94
|
+
# define 'handle' do
|
95
|
+
# sequence do
|
96
|
+
# participant 'prepare_data'
|
97
|
+
# apply :v => 'alpha'
|
98
|
+
# apply :v => 'bravo'
|
99
|
+
# participant 'rearrange_data'
|
100
|
+
# end
|
101
|
+
# end
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
#
|
105
|
+
# == on_error
|
106
|
+
#
|
107
|
+
# It's OK, to place an on_error on the apply
|
108
|
+
#
|
109
|
+
# pdef = Ruote.process_definition do
|
110
|
+
# handle do
|
111
|
+
# sequence do
|
112
|
+
# echo 'in'
|
113
|
+
# nemo
|
114
|
+
# end
|
115
|
+
# end
|
116
|
+
# define 'handle' do
|
117
|
+
# apply :on_error => 'notify' # <==
|
118
|
+
# echo 'over.'
|
119
|
+
# end
|
120
|
+
# define 'notify' do
|
121
|
+
# echo 'error'
|
122
|
+
# end
|
123
|
+
# end
|
124
|
+
#
|
125
|
+
class ApplyExpression < FlowExpression
|
126
|
+
|
127
|
+
names :apply
|
128
|
+
|
129
|
+
# TODO : maybe accept directly ruby and xml (and json)
|
130
|
+
# TODO : _yield ?
|
131
|
+
|
132
|
+
# TODO : apply [ 'echo', { 'nada' => nil }, [] ]
|
133
|
+
|
134
|
+
def apply
|
135
|
+
|
136
|
+
#
|
137
|
+
# find 'tree'
|
138
|
+
|
139
|
+
tree =
|
140
|
+
lookup_val_prefix('tree', :escape => true) ||
|
141
|
+
lookup_variable('tree')
|
142
|
+
|
143
|
+
return reply_to_parent(h.applied_workitem) unless tree
|
144
|
+
|
145
|
+
#
|
146
|
+
# apply 'tree'
|
147
|
+
|
148
|
+
launch_sub(
|
149
|
+
"#{h.fei['expid']}_0", tree,
|
150
|
+
:variables => compile_atts(:escape => true))
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
@@ -0,0 +1,78 @@
|
|
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
|
+
# Cancels a whole process instance.
|
30
|
+
#
|
31
|
+
# pdef = Ruote.process_definition :name => 'test' do
|
32
|
+
# sequence do
|
33
|
+
# participant :ref => 'editor'
|
34
|
+
# concurrence do
|
35
|
+
# participant :ref => 'reviewer1'
|
36
|
+
# participant :ref => 'reviewer2'
|
37
|
+
# sequence do
|
38
|
+
# participant :ref => 'main_reviewer'
|
39
|
+
# cancel_process :if => '${f:over} == true'
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
# participant :ref => 'editor'
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# This example has a 'main_reviewer' with the ability to cancel the whole
|
47
|
+
# process, if he sets the workitem field 'over' to 'true'.
|
48
|
+
#
|
49
|
+
# If the goal is to cancel only a segment of a process instance, the
|
50
|
+
# expression 'undo' (Ruote::Exp::UndoExpression) is better suited.
|
51
|
+
#
|
52
|
+
# == 'terminate'
|
53
|
+
#
|
54
|
+
# Sometimes 'terminate' reads better than 'cancel_process'
|
55
|
+
#
|
56
|
+
# Ruote.process_definition do
|
57
|
+
# alice :task => 'do this'
|
58
|
+
# terminate :if => '${no_need_for_bob}'
|
59
|
+
# bob :task => 'do that'
|
60
|
+
# charly :task => 'just do it'
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
class CancelProcessExpression < FlowExpression
|
64
|
+
|
65
|
+
names :cancel_process, :terminate
|
66
|
+
|
67
|
+
def apply
|
68
|
+
|
69
|
+
@context.storage.put_msg('cancel_process', 'wfid' => h.fei['wfid'])
|
70
|
+
end
|
71
|
+
|
72
|
+
def reply(workitem)
|
73
|
+
|
74
|
+
# never called
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
@@ -0,0 +1,156 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
# Made in Japan.
|
23
|
+
#++
|
24
|
+
|
25
|
+
|
26
|
+
require 'ruote/exp/command'
|
27
|
+
|
28
|
+
|
29
|
+
module Ruote::Exp
|
30
|
+
|
31
|
+
#
|
32
|
+
# This class gathers the 'skip', 'back', 'jump', 'rewind', 'continue', 'reset'
|
33
|
+
# and 'break' expressions which are used inside of the 'cursor' and 'repeat'
|
34
|
+
# (loop) expressions.
|
35
|
+
#
|
36
|
+
# Look at the 'cursor' expression Ruote::Exp::Cursor for a discussion of
|
37
|
+
# each of those [sub]expressions.
|
38
|
+
#
|
39
|
+
# The expression that understand commands are 'cursor', 'repeat' ('loop') and
|
40
|
+
# 'iterator'.
|
41
|
+
# 'concurrent_iterator' does not understand commands since it fires all its
|
42
|
+
# branches when applied.
|
43
|
+
#
|
44
|
+
# == :ref => 'tag'
|
45
|
+
#
|
46
|
+
# It's OK to tag a cursor/loop/iterator with the :tag attribute and then
|
47
|
+
# point a command to it via :ref :
|
48
|
+
#
|
49
|
+
# concurrence do
|
50
|
+
#
|
51
|
+
# cursor :tag => 'main' do
|
52
|
+
# author
|
53
|
+
# editor
|
54
|
+
# publisher
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# # meanwhile ...
|
58
|
+
#
|
59
|
+
# sequence do
|
60
|
+
# sponsor
|
61
|
+
# rewind :ref => 'main', :if => '${f:stop}'
|
62
|
+
# end
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# This :ref technique may also be used with nested cursor/loop/iterator
|
66
|
+
# constructs :
|
67
|
+
#
|
68
|
+
# cursor :tag => 'main' do
|
69
|
+
# cursor do
|
70
|
+
# author
|
71
|
+
# editor
|
72
|
+
# rewind :if => '${f:not_ok}'
|
73
|
+
# _break :ref => 'main', :if => '${f:abort_everything}'
|
74
|
+
# end
|
75
|
+
# head_of_edition
|
76
|
+
# rewind :if => '${f:not_ok}'
|
77
|
+
# publisher
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# this example features two nested cursors. There is a "_break" in the inner
|
81
|
+
# cursor, but it will break the main 'cursor' (and thus break the whole
|
82
|
+
# review process).
|
83
|
+
#
|
84
|
+
# :ref works with the 'iterator' expression as well.
|
85
|
+
#
|
86
|
+
class CommandExpression < FlowExpression
|
87
|
+
|
88
|
+
include CommandMixin
|
89
|
+
|
90
|
+
names :skip, :back, :jump, :rewind, :continue, :break, :stop, :over, :reset
|
91
|
+
|
92
|
+
def apply
|
93
|
+
|
94
|
+
param = case name
|
95
|
+
when 'skip', 'back' then attribute(:step) || attribute_text
|
96
|
+
when 'jump' then attribute(:to) || attribute_text
|
97
|
+
else nil
|
98
|
+
end
|
99
|
+
|
100
|
+
param = Integer(param) rescue param
|
101
|
+
|
102
|
+
command_workitem = Ruote.fulldup(h.applied_workitem)
|
103
|
+
|
104
|
+
set_command(command_workitem, name, param)
|
105
|
+
|
106
|
+
target = parent
|
107
|
+
ancestor = true
|
108
|
+
|
109
|
+
if ref = attribute(:ref)
|
110
|
+
|
111
|
+
fei = lookup_variable(ref)
|
112
|
+
|
113
|
+
target = Ruote::FlowExpressionId.is_a_fei?(fei) ?
|
114
|
+
Ruote::Exp::FlowExpression.fetch(@context, fei) : nil
|
115
|
+
|
116
|
+
target = target.is_a?(Ruote::Exp::CommandedExpression) ?
|
117
|
+
target : nil
|
118
|
+
|
119
|
+
ancestor = target ? ancestor?(target.h.fei) : false
|
120
|
+
|
121
|
+
else
|
122
|
+
|
123
|
+
target = fetch_command_target
|
124
|
+
end
|
125
|
+
|
126
|
+
return reply_to_parent(h.applied_workitem) if target.nil?
|
127
|
+
return reply_to_parent(command_workitem) if target.h.fei == h.parent_id
|
128
|
+
|
129
|
+
@context.storage.put_msg(
|
130
|
+
'reply',
|
131
|
+
'fei' => target.h.fei,
|
132
|
+
'workitem' => command_workitem,
|
133
|
+
'command' => [ name, param ]) # purely indicative for now
|
134
|
+
|
135
|
+
reply_to_parent(h.applied_workitem) unless ancestor
|
136
|
+
end
|
137
|
+
|
138
|
+
protected
|
139
|
+
|
140
|
+
# Walks up the expression tree (process instance and returns the first
|
141
|
+
# expression that includes the CommandMixin
|
142
|
+
#
|
143
|
+
# (CommandExpression includes CommandMixin, but since it doesn't have
|
144
|
+
# children, no need to 'evince' it)
|
145
|
+
#
|
146
|
+
def fetch_command_target(exp=parent)
|
147
|
+
|
148
|
+
case exp
|
149
|
+
when nil then nil
|
150
|
+
when Ruote::Exp::CommandedExpression then exp
|
151
|
+
else fetch_command_target(exp.parent)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
@@ -0,0 +1,321 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
# Made in Japan.
|
23
|
+
#++
|
24
|
+
|
25
|
+
|
26
|
+
require 'ruote/exp/merge'
|
27
|
+
|
28
|
+
|
29
|
+
module Ruote::Exp
|
30
|
+
|
31
|
+
#
|
32
|
+
# The 'concurrence' expression applies its child branches in parallel
|
33
|
+
# (well it makes a best effort to make them run in parallel).
|
34
|
+
#
|
35
|
+
# concurrence do
|
36
|
+
# alpha
|
37
|
+
# bravo
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# == attributes
|
41
|
+
#
|
42
|
+
# The concurrence expression takes a number of attributes that allow for
|
43
|
+
# sophisticated control (especially at merge time).
|
44
|
+
#
|
45
|
+
# === :count
|
46
|
+
#
|
47
|
+
# concurrence :count => 1 do
|
48
|
+
# alpha
|
49
|
+
# bravo
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# in that example, the concurrence will terminate as soon as 1 (count) of
|
53
|
+
# the branches replies. The other branch will get cancelled.
|
54
|
+
#
|
55
|
+
# === :remaining
|
56
|
+
#
|
57
|
+
# As said for :count, the remaining branches get cancelled. By setting
|
58
|
+
# :remaining to :forget (or 'forget'), the remaining branches will continue
|
59
|
+
# their execution, forgotten.
|
60
|
+
#
|
61
|
+
# concurrence :count => 1, :remaining => :forget do
|
62
|
+
# alpha
|
63
|
+
# bravo
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# === :merge
|
67
|
+
#
|
68
|
+
# By default, the workitems override each others. By default, the first
|
69
|
+
# workitem to reply will win.
|
70
|
+
#
|
71
|
+
# sequence do
|
72
|
+
# concurrence do
|
73
|
+
# alpha
|
74
|
+
# bravo
|
75
|
+
# end
|
76
|
+
# charly
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# In that example, if 'alpha' replied first, the workitem that reaches
|
80
|
+
# 'charly' once 'bravo' replied will have the payload as seen/modified by
|
81
|
+
# 'alpha'.
|
82
|
+
#
|
83
|
+
# The :merge attribute determines which branch wins the merge.
|
84
|
+
#
|
85
|
+
# * first (default)
|
86
|
+
# * last
|
87
|
+
# * highest
|
88
|
+
# * lowest
|
89
|
+
#
|
90
|
+
# highest and lowest refer to the position in the list of branch. It's useful
|
91
|
+
# to set a fixed winner.
|
92
|
+
#
|
93
|
+
# concurrence :merge => :highest do
|
94
|
+
# alpha
|
95
|
+
# bravo
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# makes sure that alpha's version of the workitem wins.
|
99
|
+
#
|
100
|
+
# === :merge_type
|
101
|
+
#
|
102
|
+
# ==== :override
|
103
|
+
#
|
104
|
+
# By default, the merge type is set to 'override', which means that the
|
105
|
+
# 'winning' workitem's payload supplants all other workitems' payloads.
|
106
|
+
#
|
107
|
+
# ==== :mix
|
108
|
+
#
|
109
|
+
# Setting :merge_type to :mix, will actually attempt to merge field by field,
|
110
|
+
# making sure that the field value of the winner(s) are used.
|
111
|
+
#
|
112
|
+
# ==== :isolate
|
113
|
+
#
|
114
|
+
# :isolate will rearrange the resulting workitem payload so that there is
|
115
|
+
# a new field for each branch. The name of each field is the index of the
|
116
|
+
# branch from '0' to ...
|
117
|
+
#
|
118
|
+
# ==== :stack
|
119
|
+
#
|
120
|
+
# :stack will stack the workitems coming back from the concurrence branches
|
121
|
+
# in an array whose order is determined by the :merge attributes. The array
|
122
|
+
# is placed in the 'stack' field of the resulting workitem.
|
123
|
+
# Note that the :stack merge_type also creates a 'stack_attributes' field
|
124
|
+
# and populates it with the expanded attributes of the concurrence.
|
125
|
+
#
|
126
|
+
# Thus
|
127
|
+
#
|
128
|
+
# sequence do
|
129
|
+
# concurrence :merge => :highest, :merge_type => :stack do
|
130
|
+
# reviewer1
|
131
|
+
# reviewer2
|
132
|
+
# end
|
133
|
+
# editor
|
134
|
+
# end
|
135
|
+
#
|
136
|
+
# will see the 'editor' receive a workitem whose fields look like :
|
137
|
+
#
|
138
|
+
# { 'stack' => [{ ... reviewer1 fields ... }, { ... reviewer2 fields ... }],
|
139
|
+
# 'stack_attributes' => { 'merge'=> 'highest', 'merge_type' => 'stack' } }
|
140
|
+
#
|
141
|
+
# This could prove useful for participant having to deal with multiple merge
|
142
|
+
# strategy results.
|
143
|
+
#
|
144
|
+
# ==== :union
|
145
|
+
#
|
146
|
+
# Will override atomic fields, concat arrays and merge hashes...
|
147
|
+
#
|
148
|
+
# The union of those two workitems
|
149
|
+
#
|
150
|
+
# { 'a' => 0, 'b' => [ 'x' ], 'c' => { 'aa' => 'bb' }
|
151
|
+
# { 'a' => 1, 'b' => [ 'y' ], 'c' => { 'cc' => 'dd' }
|
152
|
+
#
|
153
|
+
# will be
|
154
|
+
#
|
155
|
+
# { 'a' => 1, 'b' => [ 'x', 'y' ], 'c' => { 'aa' => 'bb', 'cc' => 'dd' } }
|
156
|
+
#
|
157
|
+
#
|
158
|
+
# === :over_if (and :over_unless)
|
159
|
+
#
|
160
|
+
# Like the :count attribute controls how many branches have to reply before
|
161
|
+
# a concurrence ends, the :over attribute is used to specify a condition
|
162
|
+
# upon which the concurrence will [prematurely] end.
|
163
|
+
#
|
164
|
+
# concurrence :over_if => '${f:over}'
|
165
|
+
# alpha
|
166
|
+
# bravo
|
167
|
+
# charly
|
168
|
+
# end
|
169
|
+
#
|
170
|
+
# will end the concurrence as soon as one of the branches replies with a
|
171
|
+
# workitem whose field 'over' is set to true. (the remaining branches will
|
172
|
+
# get cancelled unless :remaining => :forget is set).
|
173
|
+
#
|
174
|
+
# :over_unless needs no explanation.
|
175
|
+
#
|
176
|
+
class ConcurrenceExpression < FlowExpression
|
177
|
+
|
178
|
+
include MergeMixin
|
179
|
+
|
180
|
+
names :concurrence
|
181
|
+
|
182
|
+
def apply
|
183
|
+
|
184
|
+
h.ccount = attribute(:count).to_i rescue 0
|
185
|
+
h.ccount = nil if h.ccount < 1
|
186
|
+
|
187
|
+
h.cmerge = att(:merge, %w[ first last highest lowest ])
|
188
|
+
h.cmerge_type = att(:merge_type, %w[ override mix isolate stack union ])
|
189
|
+
h.remaining = att(:remaining, %w[ cancel forget ])
|
190
|
+
|
191
|
+
h.workitems = (h.cmerge == 'first' || h.cmerge == 'last') ? [] : {}
|
192
|
+
|
193
|
+
h.over = false
|
194
|
+
|
195
|
+
apply_children
|
196
|
+
end
|
197
|
+
|
198
|
+
def reply(workitem)
|
199
|
+
|
200
|
+
workitem = Ruote.fulldup(workitem)
|
201
|
+
#
|
202
|
+
# since workitem field merging might happen, better to work on
|
203
|
+
# a copy of the workitem (so that history, coming afterwards,
|
204
|
+
# doesn't see a modified version of the workitem)
|
205
|
+
|
206
|
+
if h.cmerge == 'first' || h.cmerge == 'last'
|
207
|
+
h.workitems << workitem
|
208
|
+
else
|
209
|
+
h.workitems[workitem['fei']['expid']] = workitem
|
210
|
+
end
|
211
|
+
|
212
|
+
over = h.over
|
213
|
+
h.over = over || over?(workitem)
|
214
|
+
|
215
|
+
if (not over) && h.over
|
216
|
+
# just became 'over'
|
217
|
+
|
218
|
+
reply_to_parent(nil)
|
219
|
+
|
220
|
+
elsif h.children.empty?
|
221
|
+
|
222
|
+
do_unpersist || return
|
223
|
+
|
224
|
+
@context.storage.put_msg(
|
225
|
+
'ceased',
|
226
|
+
'wfid' => h.fei['wfid'], 'fei' => h.fei, 'workitem' => workitem)
|
227
|
+
else
|
228
|
+
|
229
|
+
do_persist
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
protected
|
234
|
+
|
235
|
+
def apply_children
|
236
|
+
|
237
|
+
# a) register children
|
238
|
+
# b) persist
|
239
|
+
# c) trigger children applies
|
240
|
+
|
241
|
+
msgs = []
|
242
|
+
|
243
|
+
tree_children.each_with_index do |c, i|
|
244
|
+
msgs << pre_apply_child(i, h.applied_workitem.dup, false)
|
245
|
+
end
|
246
|
+
|
247
|
+
persist_or_raise
|
248
|
+
|
249
|
+
msgs.each { |m| @context.storage.put_msg('apply', m) }
|
250
|
+
end
|
251
|
+
|
252
|
+
def over?(workitem)
|
253
|
+
|
254
|
+
over_if = attribute(:over_if, workitem)
|
255
|
+
over_unless = attribute(:over_unless, workitem)
|
256
|
+
|
257
|
+
if over_if && Condition.true?(over_if)
|
258
|
+
true
|
259
|
+
elsif over_unless && (not Condition.true?(over_unless))
|
260
|
+
true
|
261
|
+
else
|
262
|
+
(h.workitems.size >= expected_count)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
# How many branch replies are expected before the concurrence is over ?
|
267
|
+
#
|
268
|
+
# (note : concurrent_iterator overrides it)
|
269
|
+
#
|
270
|
+
def expected_count
|
271
|
+
|
272
|
+
h.ccount ? [ h.ccount, tree_children.size ].min : tree_children.size
|
273
|
+
end
|
274
|
+
|
275
|
+
def reply_to_parent(_workitem)
|
276
|
+
|
277
|
+
workitem = merge_all_workitems
|
278
|
+
|
279
|
+
if h.ccount == nil || h.children.empty?
|
280
|
+
|
281
|
+
do_unpersist && super(workitem, false)
|
282
|
+
|
283
|
+
elsif h.remaining == 'cancel'
|
284
|
+
|
285
|
+
if r = do_unpersist
|
286
|
+
|
287
|
+
super(workitem, false)
|
288
|
+
|
289
|
+
h.children.each do |i|
|
290
|
+
@context.storage.put_msg('cancel', 'fei' => i) #unless replied?(i)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
else # h.remaining == 'forget'
|
295
|
+
|
296
|
+
h.variables = compile_variables
|
297
|
+
h.forgotten = true
|
298
|
+
|
299
|
+
do_persist && super(workitem, false)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
def merge_all_workitems
|
304
|
+
|
305
|
+
return h.applied_workitem if h.workitems.size < 1
|
306
|
+
|
307
|
+
wis = case h.cmerge
|
308
|
+
when 'first'
|
309
|
+
h.workitems.reverse
|
310
|
+
when 'last'
|
311
|
+
h.workitems
|
312
|
+
when 'highest', 'lowest'
|
313
|
+
is = h.workitems.keys.sort.collect { |k| h.workitems[k] }
|
314
|
+
h.cmerge == 'highest' ? is.reverse : is
|
315
|
+
end
|
316
|
+
|
317
|
+
merge_workitems(wis, h.cmerge_type)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|