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,219 @@
|
|
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/fe_concurrence'
|
27
|
+
require 'ruote/exp/iterator'
|
28
|
+
|
29
|
+
|
30
|
+
module Ruote::Exp
|
31
|
+
|
32
|
+
#
|
33
|
+
# This expression is a cross between 'concurrence' and 'iterator'.
|
34
|
+
#
|
35
|
+
# Please look at the documentation of 'iterator' to learn more about the
|
36
|
+
# common options between 'iterator' and 'concurrent-iterator'.
|
37
|
+
#
|
38
|
+
# pdef = Ruote.process_definition :name => 'test' do
|
39
|
+
# concurrent_iterator :on_val => 'alice, bob, charly', :to_var => 'v' do
|
40
|
+
# participant '${v:v}'
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# will be equivalent to
|
45
|
+
#
|
46
|
+
# pdef = Ruote.process_definition :name => 'test' do
|
47
|
+
# concurrence do
|
48
|
+
# participant 'alice'
|
49
|
+
# participant 'bob'
|
50
|
+
# participant 'charly'
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# The 'on' and the 'to' attributes follow exactly the ones for the iterator
|
55
|
+
# expression.
|
56
|
+
#
|
57
|
+
# When there is no 'to', the current iterated value is placed in the variable
|
58
|
+
# named 'i'.
|
59
|
+
#
|
60
|
+
# The variable named 'ii' contains the current iterated index (an int bigger
|
61
|
+
# or egal to 0).
|
62
|
+
#
|
63
|
+
# 'concurrent_iterator' does not understand commands like
|
64
|
+
# rewind/break/jump/... like 'iterator' does, since it fires all its
|
65
|
+
# branches when applied.
|
66
|
+
#
|
67
|
+
# == :times and :branches
|
68
|
+
#
|
69
|
+
# Similarly to the iterator expression, the :times or the :branches attribute
|
70
|
+
# may be used in stead of the 'on' attribute.
|
71
|
+
#
|
72
|
+
# pdef = Ruote.process_definition :name => 'test' do
|
73
|
+
# concurrent_iterator :times => 3 do
|
74
|
+
# participant 'user${v:i}'
|
75
|
+
# end
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# is equivalent to
|
79
|
+
#
|
80
|
+
# pdef = Ruote.process_definition :name => 'test' do
|
81
|
+
# concurrence do
|
82
|
+
# participant 'user0'
|
83
|
+
# participant 'user1'
|
84
|
+
# participant 'user2'
|
85
|
+
# end
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
#
|
89
|
+
# == options
|
90
|
+
#
|
91
|
+
# the concurrent_iterator accepts the same options for merging as its bigger
|
92
|
+
# brother, the concurrence expression.
|
93
|
+
#
|
94
|
+
# :count, :merge (override, mix, isolate), remaining (cancel, forget) and
|
95
|
+
# :over.
|
96
|
+
#
|
97
|
+
#
|
98
|
+
# == add branches
|
99
|
+
#
|
100
|
+
# The 'add_branches'/'add_branch' expression can be used to add branches
|
101
|
+
# to a concurrent-iterator while it is running.
|
102
|
+
#
|
103
|
+
# concurrent_iterator :on => 'a, b, c' do
|
104
|
+
# sequence do
|
105
|
+
# participant :ref => 'worker_${v:i}'
|
106
|
+
# add_branches 'd, e', :if => '${v:/not_sufficient}'
|
107
|
+
# end
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
# In this example, if the process level variable 'not_sufficient' is set to
|
111
|
+
# true, workers d and e will be added to the iterated elements.
|
112
|
+
#
|
113
|
+
# Read more at the 'add_branches' expression description.
|
114
|
+
#
|
115
|
+
#
|
116
|
+
# == 'citerator'
|
117
|
+
#
|
118
|
+
# 'citerator' is an alias for 'concurrent_iterator'.
|
119
|
+
#
|
120
|
+
# pdef = Ruote.process_definition :name => 'test' do
|
121
|
+
# citerator :on_val => 'alice, bob, charly', :to_var => 'v' do
|
122
|
+
# participant '${v:v}'
|
123
|
+
# end
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
class ConcurrentIteratorExpression < ConcurrenceExpression
|
127
|
+
|
128
|
+
include IteratorMixin
|
129
|
+
|
130
|
+
names :concurrent_iterator, :citerator
|
131
|
+
|
132
|
+
ADD_BRANCHES_FIELD = '__add_branches__'
|
133
|
+
|
134
|
+
# Overrides FlowExpression#register_child to make sure that persist is
|
135
|
+
# not called.
|
136
|
+
#
|
137
|
+
def register_child(fei)
|
138
|
+
|
139
|
+
h.children << fei
|
140
|
+
end
|
141
|
+
|
142
|
+
def add_branches(list)
|
143
|
+
|
144
|
+
if h.times_iterator && list.size == 1
|
145
|
+
|
146
|
+
count = (list.first.to_i rescue nil)
|
147
|
+
|
148
|
+
list = (h.list_size + 1..h.list_size + count) if count
|
149
|
+
end
|
150
|
+
|
151
|
+
list.each do |val|
|
152
|
+
|
153
|
+
h.list_size += 1
|
154
|
+
|
155
|
+
workitem = Ruote.fulldup(h.applied_workitem)
|
156
|
+
#workitem = Rufus::Json.dup(h.applied_workitem)
|
157
|
+
|
158
|
+
variables = { 'ii' => h.list_size - 1 }
|
159
|
+
|
160
|
+
if h.to_v
|
161
|
+
variables[h.to_v] = val
|
162
|
+
else #if to_f
|
163
|
+
workitem['fields'][h.to_f] = val
|
164
|
+
end
|
165
|
+
|
166
|
+
launch_sub(
|
167
|
+
"#{h.fei['expid']}_0",
|
168
|
+
tree_children[0],
|
169
|
+
:workitem => workitem,
|
170
|
+
:variables => variables)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def reply(workitem)
|
175
|
+
|
176
|
+
if ab = workitem['fields'].delete(ADD_BRANCHES_FIELD)
|
177
|
+
|
178
|
+
add_branches(ab)
|
179
|
+
|
180
|
+
if h.fei['wfid'] != workitem['fei']['wfid'] ||
|
181
|
+
( ! workitem['fei']['expid'].match(/^#{h.fei['expid']}_\d+$/))
|
182
|
+
|
183
|
+
do_persist
|
184
|
+
return
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
super(workitem)
|
189
|
+
end
|
190
|
+
|
191
|
+
protected
|
192
|
+
|
193
|
+
def apply_children
|
194
|
+
|
195
|
+
return reply_to_parent(h.applied_workitem) unless tree_children[0]
|
196
|
+
|
197
|
+
list = determine_list
|
198
|
+
|
199
|
+
return reply_to_parent(h.applied_workitem) if list.empty?
|
200
|
+
|
201
|
+
h.to_v, h.to_f = determine_tos
|
202
|
+
h.to_v = 'i' if h.to_v.nil? && h.to_f.nil?
|
203
|
+
|
204
|
+
h.list_size = 0
|
205
|
+
|
206
|
+
add_branches(list)
|
207
|
+
|
208
|
+
persist_or_raise
|
209
|
+
end
|
210
|
+
|
211
|
+
# Overrides the implementation found in ConcurrenceExpression
|
212
|
+
#
|
213
|
+
def expected_count
|
214
|
+
|
215
|
+
h.ccount ? [ h.ccount, h.list_size ].min : h.list_size
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
@@ -0,0 +1,141 @@
|
|
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
|
+
# This expression executes its children expression according to a cron
|
30
|
+
# schedule or at a given frequency.
|
31
|
+
#
|
32
|
+
# cron '15 4 * * sun' do # every sunday at 0415
|
33
|
+
# subprocess :ref => 'refill_the_acid_baths'
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# or
|
37
|
+
#
|
38
|
+
# every '10m' do
|
39
|
+
# send_reminder # subprocess or participant
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# The 'tab' or 'interval' attributes may be used, this is a bit more verbose,
|
43
|
+
# but, for instance, in XML, it is quite necessary :
|
44
|
+
#
|
45
|
+
# <cron tab="15 4 * * sun">
|
46
|
+
# <subprocess ref="refill_the_acid_baths" />
|
47
|
+
# <cron>
|
48
|
+
#
|
49
|
+
# Triggered children subprocesses are 'forgotten'. This implies they
|
50
|
+
# will never reply to the cron/every expression and they won't get cancelled
|
51
|
+
# when the cron/every expression gets cancelled (the cron/every schedule
|
52
|
+
# gets cancelled though, no new children will get cancelled).
|
53
|
+
#
|
54
|
+
# "man 5 crontab" in the command line of your favourite unix system might
|
55
|
+
# help you with the semantics of the string expected by the cron expression.
|
56
|
+
#
|
57
|
+
#
|
58
|
+
# == an example use case
|
59
|
+
#
|
60
|
+
# The cron/every expression appears often in scenarii like :
|
61
|
+
#
|
62
|
+
# concurrence :count => 1 do
|
63
|
+
#
|
64
|
+
# participant 'operator'
|
65
|
+
#
|
66
|
+
# cron '0 9 * * 1-5' do # send a reminder every weekday at 0900
|
67
|
+
# notify 'operator'
|
68
|
+
# end
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# With a subprocess, this could become a bit more reusable :
|
72
|
+
#
|
73
|
+
# Ruote.process_defintion :name => 'sample' do
|
74
|
+
#
|
75
|
+
# sequence do
|
76
|
+
# with_reminder :participant => 'operator1'
|
77
|
+
# with_reminder :participant => 'operator2'
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# define 'with_reminder' do
|
81
|
+
# concurrence :count => 1 do
|
82
|
+
# participant '${v:participant}'
|
83
|
+
# cron '0 9 * * 1-5' do # send a reminder every weekday at 0900
|
84
|
+
# notify '${v:participant}'
|
85
|
+
# end
|
86
|
+
# end
|
87
|
+
# end
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
class CronExpression < FlowExpression
|
91
|
+
|
92
|
+
names :cron, :every
|
93
|
+
|
94
|
+
def apply
|
95
|
+
|
96
|
+
h.schedule = attribute(:tab) || attribute(:interval) || attribute_text
|
97
|
+
h.job_id = nil
|
98
|
+
|
99
|
+
reschedule
|
100
|
+
end
|
101
|
+
|
102
|
+
def reply(workitem)
|
103
|
+
|
104
|
+
launch_sub(
|
105
|
+
"#{h.fei['expid']}_0",
|
106
|
+
tree_children[0],
|
107
|
+
:workitem => Ruote.fulldup(h.applied_workitem),
|
108
|
+
:forget => true)
|
109
|
+
|
110
|
+
reschedule
|
111
|
+
end
|
112
|
+
|
113
|
+
def cancel(flavour)
|
114
|
+
|
115
|
+
@context.storage.delete_schedule(h.job_id)
|
116
|
+
reply_to_parent(h.applied_workitem)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Note : this method has to be public.
|
120
|
+
#
|
121
|
+
def reschedule
|
122
|
+
|
123
|
+
h.job_id = @context.storage.put_schedule(
|
124
|
+
'cron',
|
125
|
+
h.fei,
|
126
|
+
h.schedule,
|
127
|
+
'action' => 'reply',
|
128
|
+
'fei' => h.fei,
|
129
|
+
'workitem' => h.applied_workitem)
|
130
|
+
|
131
|
+
@context.storage.delete_schedule(h.job_id) if try_persist
|
132
|
+
#
|
133
|
+
# if the persist failed, immediately unschedule
|
134
|
+
# the just scheduled job
|
135
|
+
#
|
136
|
+
# this is meant to cope with cases where one worker reschedules
|
137
|
+
# while another just cancelled
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
@@ -0,0 +1,324 @@
|
|
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/commanded'
|
27
|
+
|
28
|
+
|
29
|
+
module Ruote::Exp
|
30
|
+
|
31
|
+
#
|
32
|
+
# This class implements the 'cursor' and the 'repeat' (loop) expressions.
|
33
|
+
#
|
34
|
+
# The cursor expression is a kind of enhanced 'sequence'. Like a sequence
|
35
|
+
# it will execute its child expression one by one, sequentially. Unlike a
|
36
|
+
# sequence though, it will obey 'commands'.
|
37
|
+
#
|
38
|
+
# cursor do
|
39
|
+
# author
|
40
|
+
# reviewer
|
41
|
+
# rewind :if => '${f:not_ok}'
|
42
|
+
# publisher
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# In this simplistic example, the process will flow from author to reviewer
|
46
|
+
# and back until the reviewer sets the workitem field 'not_ok' to something
|
47
|
+
# else than the value 'true'.
|
48
|
+
#
|
49
|
+
# There are two ways to pass commands to a cursor either directly from
|
50
|
+
# the process definition with a cursor command expression, either via
|
51
|
+
# the workitem '__command__' [special] field.
|
52
|
+
#
|
53
|
+
# == cursor commands
|
54
|
+
#
|
55
|
+
# The commands that a cursor understands are listed here. The most powerful
|
56
|
+
# ones are 'rewind' and 'jump'.
|
57
|
+
#
|
58
|
+
# === rewind
|
59
|
+
#
|
60
|
+
# Rewinds the cursor up to its first child expression.
|
61
|
+
#
|
62
|
+
# cursor do
|
63
|
+
# author
|
64
|
+
# reviewer
|
65
|
+
# rewind :if => '${f:not_ok}'
|
66
|
+
# publisher
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
# === reset
|
70
|
+
#
|
71
|
+
# Whereas 'rewind' places the cursor back to the initial step with the current
|
72
|
+
# workitem, 'reset' will rewind it and start again but with the workitem
|
73
|
+
# as it was when it reached the cursor/repeat.
|
74
|
+
#
|
75
|
+
# === stop, over & break
|
76
|
+
#
|
77
|
+
# Exits the cursor.
|
78
|
+
#
|
79
|
+
# cursor do
|
80
|
+
# author
|
81
|
+
# reviewer
|
82
|
+
# rewind :if => '${f:review} == fix'
|
83
|
+
# stop :if => '${f:review} == abort'
|
84
|
+
# publisher
|
85
|
+
# end
|
86
|
+
#
|
87
|
+
# '_break' or 'over' can be used instead of 'stop'.
|
88
|
+
#
|
89
|
+
# === skip & back
|
90
|
+
#
|
91
|
+
# Those two commands jump forth and back respectively. By default, they
|
92
|
+
# skip 1 child, but they accept a numeric parameter holding the number
|
93
|
+
# of children to skip.
|
94
|
+
#
|
95
|
+
# cursor do
|
96
|
+
# author
|
97
|
+
# reviewer
|
98
|
+
# rewind :if => '${f:review} == fix'
|
99
|
+
# skip 2 :if => '${f:review} == publish'
|
100
|
+
# reviewer2
|
101
|
+
# rewind :if => '${f:review} == fix'
|
102
|
+
# publisher
|
103
|
+
# end
|
104
|
+
#
|
105
|
+
# === jump
|
106
|
+
#
|
107
|
+
# Jump is probably the most powerful of the cursor commands. It allows to
|
108
|
+
# jump to a specified expression that is a direct child of the cursor.
|
109
|
+
#
|
110
|
+
# cursor do
|
111
|
+
# author
|
112
|
+
# reviewer
|
113
|
+
# jump :to => 'author', :if => '${f:review} == fix'
|
114
|
+
# jump :to => 'publisher', :if => '${f:review} == publish'
|
115
|
+
# reviewer2
|
116
|
+
# jump :to => 'author', :if => '${f:review} == fix'
|
117
|
+
# publisher
|
118
|
+
# end
|
119
|
+
#
|
120
|
+
# Note that the :to accepts the name of an expression or the value of
|
121
|
+
# its :ref attribute or the value of its :tag attribute.
|
122
|
+
#
|
123
|
+
# cursor do
|
124
|
+
# participant :ref => 'author'
|
125
|
+
# participant :ref => 'reviewer'
|
126
|
+
# jump :to => 'author', :if => '${f:review} == fix'
|
127
|
+
# participant :ref => 'publisher'
|
128
|
+
# end
|
129
|
+
#
|
130
|
+
# == cursor command with :ref
|
131
|
+
#
|
132
|
+
# It's OK to tag a cursor/repeat/loop with the :tag attribute and then
|
133
|
+
# point a command to it via :ref :
|
134
|
+
#
|
135
|
+
# concurrence do
|
136
|
+
#
|
137
|
+
# cursor :tag => 'main' do
|
138
|
+
# author
|
139
|
+
# editor
|
140
|
+
# publisher
|
141
|
+
# end
|
142
|
+
#
|
143
|
+
# # meanwhile ...
|
144
|
+
#
|
145
|
+
# sequence do
|
146
|
+
# sponsor
|
147
|
+
# rewind :ref => 'main', :if => '${f:stop}'
|
148
|
+
# end
|
149
|
+
# end
|
150
|
+
#
|
151
|
+
# This :ref technique may also be used with nested cursor/loop/iterator
|
152
|
+
# constructs :
|
153
|
+
#
|
154
|
+
# cursor :tag => 'main' do
|
155
|
+
# cursor do
|
156
|
+
# author
|
157
|
+
# editor
|
158
|
+
# rewind :if => '${f:not_ok}'
|
159
|
+
# _break :ref => 'main', :if => '${f:abort_everything}'
|
160
|
+
# end
|
161
|
+
# head_of_edition
|
162
|
+
# rewind :if => '${f:not_ok}'
|
163
|
+
# publisher
|
164
|
+
# end
|
165
|
+
#
|
166
|
+
# this example features two nested cursors. There is a "_break" in the inner
|
167
|
+
# cursor, but it will break the main 'cursor' (and thus break the whole
|
168
|
+
# review process).
|
169
|
+
#
|
170
|
+
# == cursor command in the workitem
|
171
|
+
#
|
172
|
+
# The command expressions are merely setting the workitem field '__command__'
|
173
|
+
# with an array value [ {command}, {arg} ].
|
174
|
+
#
|
175
|
+
# For example,
|
176
|
+
#
|
177
|
+
# jump :to => 'author'
|
178
|
+
# # is equivalent to
|
179
|
+
# set 'field:__command__' => 'author'
|
180
|
+
#
|
181
|
+
# It is entirely OK to have a participant implementation that sets __command__
|
182
|
+
# by itself.
|
183
|
+
#
|
184
|
+
# class Reviewer
|
185
|
+
# include Ruote::LocalParticipant
|
186
|
+
#
|
187
|
+
# def consume(workitem)
|
188
|
+
# # somehow review the book
|
189
|
+
# if review == 'bad'
|
190
|
+
# #workitem.fields['__command__'] = [ 'rewind' ] # old style
|
191
|
+
# workitem.command = 'rewind' # new style
|
192
|
+
# else
|
193
|
+
# # let it go
|
194
|
+
# end
|
195
|
+
# reply_to_engine(workitem)
|
196
|
+
# end
|
197
|
+
#
|
198
|
+
# def cancel(fei, flavour)
|
199
|
+
# # cancel if review is still going on...
|
200
|
+
# end
|
201
|
+
# end
|
202
|
+
#
|
203
|
+
# This example uses the Ruote::Workitem#command= method which can be fed
|
204
|
+
# strings like 'rewind', 'skip 2', 'jump to author' or the equivalent arrays
|
205
|
+
# [ 'rewind' ], [ 'skip', 2 ], [ 'jump', 'author' ].
|
206
|
+
#
|
207
|
+
#
|
208
|
+
# == :break_if / :rewind_if
|
209
|
+
#
|
210
|
+
# As an attribute of the cursor/repeat expression, you can set a :break_if.
|
211
|
+
# It tells the cursor (loop) if it has to break.
|
212
|
+
#
|
213
|
+
# cursor :break_if => '${f:completed}' do
|
214
|
+
# participant 'alpha'
|
215
|
+
# participant 'bravo'
|
216
|
+
# participant 'charly'
|
217
|
+
# end
|
218
|
+
#
|
219
|
+
# If alpha or bravo replies and the field 'completed' is set to true, this
|
220
|
+
# cursor will break.
|
221
|
+
#
|
222
|
+
# :break_unless is accepted. :over_if and :over_unless are synonyms for
|
223
|
+
# :break_if and :break_unless respectively.
|
224
|
+
#
|
225
|
+
# :rewind_if / :rewind_unless behave the same, but the cursor/loop, instead
|
226
|
+
# of breaking, is put back in its first step.
|
227
|
+
#
|
228
|
+
#
|
229
|
+
# = repeat (loop)
|
230
|
+
#
|
231
|
+
# A 'cursor' expression exits implicitely as soon as its last child replies
|
232
|
+
# to it.
|
233
|
+
# a 'repeat' expression will apply (again) the first child after the last
|
234
|
+
# child replied. A 'break' cursor command might be necessary to exit the loop
|
235
|
+
# (or a cancel_process, but that exits the whole process instance).
|
236
|
+
#
|
237
|
+
# sequence do
|
238
|
+
# repeat do
|
239
|
+
# author
|
240
|
+
# reviewer
|
241
|
+
# _break :if => '${f:review} == ok'
|
242
|
+
# end
|
243
|
+
# publisher
|
244
|
+
# end
|
245
|
+
#
|
246
|
+
class CursorExpression < CommandedExpression
|
247
|
+
|
248
|
+
names :cursor, :loop, :repeat
|
249
|
+
|
250
|
+
def apply
|
251
|
+
|
252
|
+
move_on
|
253
|
+
end
|
254
|
+
|
255
|
+
protected
|
256
|
+
|
257
|
+
# Determines which child expression of the cursor is to be applied next.
|
258
|
+
#
|
259
|
+
def move_on(workitem=h.applied_workitem)
|
260
|
+
|
261
|
+
position = workitem['fei'] == h.fei ?
|
262
|
+
-1 : Ruote::FlowExpressionId.child_id(workitem['fei'])
|
263
|
+
|
264
|
+
position += 1
|
265
|
+
|
266
|
+
com, arg = get_command(workitem)
|
267
|
+
|
268
|
+
return reply_to_parent(workitem) if com == 'break'
|
269
|
+
|
270
|
+
case com
|
271
|
+
when 'rewind', 'continue', 'reset' then position = 0
|
272
|
+
when 'skip' then position += arg
|
273
|
+
when 'jump' then position = jump_to(workitem, position, arg)
|
274
|
+
end
|
275
|
+
|
276
|
+
position = 0 if position >= tree_children.size && is_loop?
|
277
|
+
|
278
|
+
if position < tree_children.size
|
279
|
+
|
280
|
+
workitem = h.applied_workitem if com == 'reset'
|
281
|
+
apply_child(position, workitem)
|
282
|
+
|
283
|
+
else
|
284
|
+
|
285
|
+
reply_to_parent(workitem)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
# Will return true if this instance is about a 'loop' or a 'repeat'.
|
290
|
+
#
|
291
|
+
def is_loop?
|
292
|
+
|
293
|
+
name == 'loop' || name == 'repeat'
|
294
|
+
end
|
295
|
+
|
296
|
+
# Jumps to an integer position, or the name of an expression
|
297
|
+
# or a tag name of a ref name.
|
298
|
+
#
|
299
|
+
def jump_to(workitem, position, arg)
|
300
|
+
|
301
|
+
pos = Integer(arg) rescue nil
|
302
|
+
|
303
|
+
return pos if pos != nil
|
304
|
+
|
305
|
+
tree_children.each_with_index do |c, i|
|
306
|
+
|
307
|
+
exp_name = c[0]
|
308
|
+
ref = c[1]['ref']
|
309
|
+
tag = c[1]['tag']
|
310
|
+
|
311
|
+
ref = @context.dollar_sub.s(ref, self, workitem) if ref
|
312
|
+
tag = @context.dollar_sub.s(tag, self, workitem) if tag
|
313
|
+
|
314
|
+
next if exp_name != arg && ref != arg && tag != arg
|
315
|
+
|
316
|
+
pos = i
|
317
|
+
break
|
318
|
+
end
|
319
|
+
|
320
|
+
pos ? pos : position
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|