ruote 2.1.11 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.txt +60 -0
- data/CREDITS.txt +22 -4
- data/LICENSE.txt +1 -1
- data/README.rdoc +6 -7
- data/Rakefile +58 -59
- data/TODO.txt +137 -65
- data/couch_url.txt +1 -0
- data/jruby_issue.txt +32 -0
- data/lib/ruote.rb +1 -1
- data/lib/ruote/context.rb +12 -10
- data/lib/ruote/engine.rb +280 -145
- data/lib/ruote/engine/process_error.rb +5 -5
- data/lib/ruote/engine/process_status.rb +47 -28
- data/lib/ruote/exp/command.rb +7 -10
- data/lib/ruote/exp/commanded.rb +2 -2
- data/lib/ruote/exp/condition.rb +130 -43
- data/lib/ruote/exp/fe_add_branches.rb +2 -2
- data/lib/ruote/exp/fe_apply.rb +1 -1
- data/lib/ruote/exp/fe_cancel_process.rb +3 -3
- data/lib/ruote/exp/fe_command.rb +3 -3
- data/lib/ruote/exp/fe_concurrence.rb +4 -4
- data/lib/ruote/exp/fe_concurrent_iterator.rb +17 -5
- data/lib/ruote/exp/fe_cron.rb +3 -3
- data/lib/ruote/exp/fe_cursor.rb +5 -5
- data/lib/ruote/exp/fe_define.rb +3 -3
- data/lib/ruote/exp/fe_echo.rb +3 -3
- data/lib/ruote/exp/fe_equals.rb +2 -2
- data/lib/ruote/exp/fe_error.rb +2 -2
- data/lib/ruote/exp/fe_filter.rb +519 -0
- data/lib/ruote/exp/fe_forget.rb +9 -2
- data/lib/ruote/exp/fe_given.rb +154 -0
- data/lib/ruote/exp/fe_if.rb +16 -13
- data/lib/ruote/exp/fe_inc.rb +3 -3
- data/lib/ruote/exp/fe_iterator.rb +4 -4
- data/lib/ruote/exp/fe_let.rb +75 -0
- data/lib/ruote/exp/fe_listen.rb +68 -12
- data/lib/ruote/exp/fe_lose.rb +110 -0
- data/lib/ruote/exp/fe_noop.rb +1 -1
- data/lib/ruote/exp/{fe_when.rb → fe_once.rb} +25 -21
- data/lib/ruote/exp/fe_participant.rb +14 -17
- data/lib/ruote/exp/fe_redo.rb +10 -6
- data/lib/ruote/exp/fe_ref.rb +1 -1
- data/lib/ruote/exp/fe_registerp.rb +112 -0
- data/lib/ruote/exp/fe_reserve.rb +3 -3
- data/lib/ruote/exp/fe_restore.rb +2 -2
- data/lib/ruote/exp/fe_save.rb +2 -2
- data/lib/ruote/exp/fe_sequence.rb +3 -4
- data/lib/ruote/exp/fe_set.rb +16 -7
- data/lib/ruote/exp/fe_subprocess.rb +23 -1
- data/lib/ruote/exp/fe_that.rb +92 -0
- data/lib/ruote/exp/fe_undo.rb +3 -3
- data/lib/ruote/exp/fe_unregisterp.rb +71 -0
- data/lib/ruote/exp/fe_wait.rb +2 -2
- data/lib/ruote/exp/flowexpression.rb +153 -78
- data/lib/ruote/exp/iterator.rb +2 -2
- data/lib/ruote/exp/merge.rb +2 -2
- data/lib/ruote/exp/ro_attributes.rb +14 -12
- data/lib/ruote/exp/ro_filters.rb +136 -0
- data/lib/ruote/exp/ro_persist.rb +51 -35
- data/lib/ruote/exp/ro_variables.rb +18 -27
- data/lib/ruote/fei.rb +73 -33
- data/lib/ruote/id/mnemo_wfid_generator.rb +1 -1
- data/lib/ruote/id/wfid_generator.rb +11 -4
- data/lib/ruote/log/default_history.rb +122 -0
- data/lib/ruote/log/pretty.rb +36 -8
- data/lib/ruote/log/storage_history.rb +37 -5
- data/lib/ruote/log/test_logger.rb +26 -24
- data/lib/ruote/log/wait_logger.rb +5 -3
- data/lib/ruote/part/block_participant.rb +22 -11
- data/lib/ruote/part/engine_participant.rb +6 -7
- data/lib/ruote/part/local_participant.rb +6 -12
- data/lib/ruote/part/no_op_participant.rb +4 -4
- data/lib/ruote/part/null_participant.rb +4 -4
- data/lib/ruote/part/smtp_participant.rb +4 -4
- data/lib/ruote/part/storage_participant.rb +40 -20
- data/lib/ruote/part/template.rb +4 -4
- data/lib/ruote/participant.rb +0 -1
- data/lib/ruote/{parser.rb → reader.rb} +30 -25
- data/lib/ruote/{parser → reader}/ruby_dsl.rb +28 -11
- data/lib/ruote/{parser → reader}/xml.rb +6 -5
- data/lib/ruote/receiver/base.rb +35 -13
- data/lib/ruote/storage/base.rb +20 -18
- data/lib/ruote/storage/composite_storage.rb +10 -10
- data/lib/ruote/storage/fs_storage.rb +17 -10
- data/lib/ruote/storage/hash_storage.rb +29 -18
- data/lib/ruote/svc/dispatch_pool.rb +41 -14
- data/lib/ruote/svc/dollar_sub.rb +50 -17
- data/lib/ruote/svc/error_handler.rb +19 -11
- data/lib/ruote/svc/expression_map.rb +4 -4
- data/lib/ruote/svc/participant_list.rb +105 -100
- data/lib/ruote/svc/tracker.rb +58 -18
- data/lib/ruote/svc/treechecker.rb +51 -24
- data/lib/ruote/tree_dot.rb +4 -4
- data/lib/ruote/util/filter.rb +440 -0
- data/lib/ruote/util/hashdot.rb +4 -4
- data/lib/ruote/util/look.rb +2 -6
- data/lib/ruote/util/lookup.rb +9 -7
- data/lib/ruote/util/misc.rb +40 -8
- data/lib/ruote/util/ometa.rb +1 -1
- data/lib/ruote/util/serializer.rb +4 -4
- data/lib/ruote/util/subprocess.rb +29 -9
- data/lib/ruote/util/time.rb +4 -4
- data/lib/ruote/util/tree.rb +3 -3
- data/lib/ruote/version.rb +2 -2
- data/lib/ruote/worker.rb +55 -32
- data/lib/ruote/workitem.rb +64 -11
- data/ruote.gemspec +31 -302
- data/test/bm/launch_bench.rb +37 -0
- data/test/functional/base.rb +60 -18
- data/test/functional/concurrent_base.rb +2 -2
- data/test/functional/ct_0_concurrence.rb +1 -1
- data/test/functional/ct_1_iterator.rb +1 -1
- data/test/functional/ct_2_cancel.rb +1 -1
- data/test/functional/eft_0_process_definition.rb +2 -2
- data/test/functional/eft_10_cancel_process.rb +1 -1
- data/test/functional/eft_11_wait.rb +19 -11
- data/test/functional/eft_12_listen.rb +79 -13
- data/test/functional/eft_13_iterator.rb +13 -10
- data/test/functional/eft_14_cursor.rb +98 -9
- data/test/functional/eft_15_loop.rb +6 -4
- data/test/functional/eft_16_if.rb +12 -0
- data/test/functional/eft_18_concurrent_iterator.rb +31 -32
- data/test/functional/eft_19_reserve.rb +4 -4
- data/test/functional/eft_1_echo.rb +9 -0
- data/test/functional/eft_20_save.rb +4 -4
- data/test/functional/{eft_28_when.rb → eft_28_once.rb} +33 -7
- data/test/functional/eft_30_ref.rb +17 -2
- 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 +269 -0
- data/test/functional/eft_3_participant.rb +4 -6
- data/test/functional/eft_4_set.rb +16 -2
- data/test/functional/eft_5_subprocess.rb +2 -4
- data/test/functional/eft_6_concurrence.rb +29 -29
- data/test/functional/eft_8_undo.rb +39 -3
- data/test/functional/eft_9_redo.rb +94 -2
- data/test/functional/ft_10_dollar.rb +81 -2
- data/test/functional/ft_11_recursion.rb +13 -17
- data/test/functional/ft_12_launchitem.rb +9 -5
- data/test/functional/ft_13_variables.rb +7 -9
- data/test/functional/ft_14_re_apply.rb +6 -9
- data/test/functional/ft_15_timeout.rb +18 -18
- data/test/functional/ft_16_participant_params.rb +1 -3
- data/test/functional/ft_17_conditional.rb +25 -2
- data/test/functional/ft_18_kill.rb +65 -12
- data/test/functional/ft_1_process_status.rb +147 -71
- data/test/functional/ft_20_storage_participant.rb +0 -1
- data/test/functional/ft_21_forget.rb +82 -1
- data/test/functional/{ft_24_block_participants.rb → ft_24_block_participant.rb} +42 -11
- data/test/functional/ft_25_receiver.rb +47 -17
- data/test/functional/{ft_26_participant_timeout.rb → ft_26_participant_rtimeout.rb} +56 -19
- data/test/functional/ft_29_part_template.rb +6 -5
- data/test/functional/ft_2_errors.rb +21 -37
- data/test/functional/ft_30_smtp_participant.rb +1 -1
- data/test/functional/ft_31_part_blocking.rb +8 -6
- data/test/functional/ft_34_cursor_rewind.rb +13 -10
- data/test/functional/ft_35_add_service.rb +1 -1
- data/test/functional/ft_36_storage_history.rb +24 -1
- data/test/functional/ft_37_default_history.rb +109 -0
- data/test/functional/ft_38_participant_more.rb +10 -10
- data/test/functional/ft_39_wait_for.rb +12 -9
- data/test/functional/ft_3_participant_registration.rb +111 -32
- data/test/functional/ft_40_wait_logger.rb +2 -1
- data/test/functional/ft_41_participants.rb +30 -4
- data/test/functional/ft_43_participant_on_reply.rb +6 -23
- data/test/functional/ft_45_participant_accept.rb +4 -4
- data/test/functional/ft_46_launch_single.rb +36 -2
- 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 +66 -6
- 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_37_engine_participant.rb → ft_55_engine_participant.rb} +4 -5
- data/test/functional/ft_56_filter_attribute.rb +259 -0
- data/test/functional/ft_5_on_error.rb +77 -30
- data/test/functional/ft_6_on_cancel.rb +66 -11
- data/test/functional/ft_7_tags.rb +94 -5
- data/test/functional/ft_8_participant_consumption.rb +36 -5
- data/test/functional/ft_9_subprocesses.rb +10 -10
- data/test/functional/rt_1_listen.rb +3 -3
- data/test/functional/{rt_3_when.rb → rt_3_once.rb} +4 -4
- data/test/functional/storage_helper.rb +15 -13
- data/test/functional/test.rb +1 -3
- data/test/test_helper.rb +0 -8
- data/test/unit/storage.rb +154 -10
- data/test/unit/{ut_0_ruby_parser.rb → ut_0_ruby_reader.rb} +61 -11
- data/test/unit/ut_11_lookup.rb +7 -0
- data/test/unit/ut_13_serializer.rb +1 -1
- data/test/unit/ut_15_util.rb +23 -0
- data/test/unit/{ut_16_parser.rb → ut_16_reader.rb} +11 -13
- data/test/unit/ut_1_fei.rb +57 -10
- data/test/unit/ut_20_composite_storage.rb +25 -11
- data/test/unit/ut_21_participant_list.rb +47 -0
- data/test/unit/ut_22_filter.rb +903 -0
- data/test/unit/ut_3_wait_logger.rb +2 -6
- data/test/unit/ut_6_condition.rb +164 -17
- data/test/unit/ut_7_workitem.rb +28 -0
- data/test/unit/ut_8_tree_to_dot.rb +1 -1
- data/test/unit/{ut_9_xml_parser.rb → ut_9_xml_reader.rb} +5 -5
- metadata +108 -84
- data/.gitignore +0 -4
- data/examples/barley.rb +0 -391
- data/examples/flickr_report.rb +0 -107
- data/examples/pong.rb +0 -37
- data/examples/ruote_quickstart.rb +0 -43
- data/examples/web_first_page.rb +0 -68
- data/lib/ruote/part/hash_participant.rb +0 -91
- data/test/README.rdoc +0 -15
- data/test/functional/crunner.sh +0 -19
- data/test/pdef.xml +0 -7
- data/test/unit/ut_2_wfidgen.rb +0 -21
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#--
|
|
2
|
-
# Copyright (c) 2005-
|
|
2
|
+
# Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
|
|
3
3
|
#
|
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
# of this software and associated documentation files (the "Software"), to deal
|
|
@@ -101,7 +101,7 @@ module Ruote::Exp
|
|
|
101
101
|
reply_to_parent(h.applied_workitem)
|
|
102
102
|
end
|
|
103
103
|
|
|
104
|
-
def reply
|
|
104
|
+
def reply(workitem)
|
|
105
105
|
|
|
106
106
|
# never called
|
|
107
107
|
end
|
data/lib/ruote/exp/fe_apply.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#--
|
|
2
|
-
# Copyright (c) 2005-
|
|
2
|
+
# Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
|
|
3
3
|
#
|
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
# of this software and associated documentation files (the "Software"), to deal
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#--
|
|
2
|
-
# Copyright (c) 2005-
|
|
2
|
+
# Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
|
|
3
3
|
#
|
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
# of this software and associated documentation files (the "Software"), to deal
|
|
@@ -51,14 +51,14 @@ module Ruote::Exp
|
|
|
51
51
|
#
|
|
52
52
|
class CancelProcessExpression < FlowExpression
|
|
53
53
|
|
|
54
|
-
names :cancel_process
|
|
54
|
+
names :cancel_process, :terminate
|
|
55
55
|
|
|
56
56
|
def apply
|
|
57
57
|
|
|
58
58
|
@context.storage.put_msg('cancel_process', 'wfid' => h.fei['wfid'])
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
-
def reply
|
|
61
|
+
def reply(workitem)
|
|
62
62
|
|
|
63
63
|
# never called
|
|
64
64
|
end
|
data/lib/ruote/exp/fe_command.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#--
|
|
2
|
-
# Copyright (c) 2005-
|
|
2
|
+
# Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
|
|
3
3
|
#
|
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
# of this software and associated documentation files (the "Software"), to deal
|
|
@@ -87,7 +87,7 @@ module Ruote::Exp
|
|
|
87
87
|
|
|
88
88
|
include CommandMixin
|
|
89
89
|
|
|
90
|
-
names :skip, :back, :jump, :rewind, :continue, :break, :stop
|
|
90
|
+
names :skip, :back, :jump, :rewind, :continue, :break, :stop, :over
|
|
91
91
|
|
|
92
92
|
def apply
|
|
93
93
|
|
|
@@ -143,7 +143,7 @@ module Ruote::Exp
|
|
|
143
143
|
# (CommandExpression includes CommandMixin, but since it doesn't have
|
|
144
144
|
# children, no need to 'evince' it)
|
|
145
145
|
#
|
|
146
|
-
def fetch_command_target
|
|
146
|
+
def fetch_command_target(exp=parent)
|
|
147
147
|
|
|
148
148
|
case exp
|
|
149
149
|
when nil then nil
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#--
|
|
2
|
-
# Copyright (c) 2005-
|
|
2
|
+
# Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
|
|
3
3
|
#
|
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
# of this software and associated documentation files (the "Software"), to deal
|
|
@@ -182,7 +182,7 @@ module Ruote::Exp
|
|
|
182
182
|
apply_children
|
|
183
183
|
end
|
|
184
184
|
|
|
185
|
-
def reply
|
|
185
|
+
def reply(workitem)
|
|
186
186
|
|
|
187
187
|
if h.cmerge == 'first' || h.cmerge == 'last'
|
|
188
188
|
h.workitems << workitem
|
|
@@ -230,7 +230,7 @@ module Ruote::Exp
|
|
|
230
230
|
msgs.each { |m| @context.storage.put_msg('apply', m) }
|
|
231
231
|
end
|
|
232
232
|
|
|
233
|
-
def over?
|
|
233
|
+
def over?(workitem)
|
|
234
234
|
|
|
235
235
|
over_if = attribute(:over_if, workitem)
|
|
236
236
|
over_unless = attribute(:over_unless, workitem)
|
|
@@ -253,7 +253,7 @@ module Ruote::Exp
|
|
|
253
253
|
h.ccount ? [ h.ccount, tree_children.size ].min : tree_children.size
|
|
254
254
|
end
|
|
255
255
|
|
|
256
|
-
def reply_to_parent
|
|
256
|
+
def reply_to_parent(_workitem)
|
|
257
257
|
|
|
258
258
|
workitem = merge_all_workitems
|
|
259
259
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#--
|
|
2
|
-
# Copyright (c) 2005-
|
|
2
|
+
# Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
|
|
3
3
|
#
|
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
# of this software and associated documentation files (the "Software"), to deal
|
|
@@ -112,23 +112,34 @@ module Ruote::Exp
|
|
|
112
112
|
#
|
|
113
113
|
# Read more at the 'add_branches' expression description.
|
|
114
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
|
+
#
|
|
115
126
|
class ConcurrentIteratorExpression < ConcurrenceExpression
|
|
116
127
|
|
|
117
128
|
include IteratorMixin
|
|
118
129
|
|
|
119
|
-
names :concurrent_iterator
|
|
130
|
+
names :concurrent_iterator, :citerator
|
|
120
131
|
|
|
121
132
|
ADD_BRANCHES_FIELD = '__add_branches__'
|
|
122
133
|
|
|
123
134
|
# Overrides FlowExpression#register_child to make sure that persist is
|
|
124
135
|
# not called.
|
|
125
136
|
#
|
|
126
|
-
def register_child
|
|
137
|
+
def register_child(fei)
|
|
127
138
|
|
|
128
139
|
h.children << fei
|
|
129
140
|
end
|
|
130
141
|
|
|
131
|
-
def add_branches
|
|
142
|
+
def add_branches(list)
|
|
132
143
|
|
|
133
144
|
if h.times_iterator && list.size == 1
|
|
134
145
|
|
|
@@ -142,6 +153,7 @@ module Ruote::Exp
|
|
|
142
153
|
h.list_size += 1
|
|
143
154
|
|
|
144
155
|
workitem = Ruote.fulldup(h.applied_workitem)
|
|
156
|
+
#workitem = Rufus::Json.dup(h.applied_workitem)
|
|
145
157
|
|
|
146
158
|
variables = { 'ii' => h.list_size - 1 }
|
|
147
159
|
|
|
@@ -159,7 +171,7 @@ module Ruote::Exp
|
|
|
159
171
|
end
|
|
160
172
|
end
|
|
161
173
|
|
|
162
|
-
def reply
|
|
174
|
+
def reply(workitem)
|
|
163
175
|
|
|
164
176
|
if ab = workitem['fields'].delete(ADD_BRANCHES_FIELD)
|
|
165
177
|
|
data/lib/ruote/exp/fe_cron.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#--
|
|
2
|
-
# Copyright (c) 2005-
|
|
2
|
+
# Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
|
|
3
3
|
#
|
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
# of this software and associated documentation files (the "Software"), to deal
|
|
@@ -99,7 +99,7 @@ module Ruote::Exp
|
|
|
99
99
|
reschedule
|
|
100
100
|
end
|
|
101
101
|
|
|
102
|
-
def reply
|
|
102
|
+
def reply(workitem)
|
|
103
103
|
|
|
104
104
|
launch_sub(
|
|
105
105
|
"#{h.fei['expid']}_0",
|
|
@@ -110,7 +110,7 @@ module Ruote::Exp
|
|
|
110
110
|
reschedule
|
|
111
111
|
end
|
|
112
112
|
|
|
113
|
-
def cancel
|
|
113
|
+
def cancel(flavour)
|
|
114
114
|
|
|
115
115
|
@context.storage.delete_schedule(h.job_id)
|
|
116
116
|
reply_to_parent(h.applied_workitem)
|
data/lib/ruote/exp/fe_cursor.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#--
|
|
2
|
-
# Copyright (c) 2005-
|
|
2
|
+
# Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
|
|
3
3
|
#
|
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
# of this software and associated documentation files (the "Software"), to deal
|
|
@@ -178,7 +178,7 @@ module Ruote::Exp
|
|
|
178
178
|
# class Reviewer
|
|
179
179
|
# include Ruote::LocalParticipant
|
|
180
180
|
#
|
|
181
|
-
# def consume
|
|
181
|
+
# def consume(workitem)
|
|
182
182
|
# # somehow review the book
|
|
183
183
|
# if review == 'bad'
|
|
184
184
|
# #workitem.fields['__command__'] = [ 'rewind' ] # old style
|
|
@@ -189,7 +189,7 @@ module Ruote::Exp
|
|
|
189
189
|
# reply_to_engine(workitem)
|
|
190
190
|
# end
|
|
191
191
|
#
|
|
192
|
-
# def cancel
|
|
192
|
+
# def cancel(fei, flavour)
|
|
193
193
|
# # cancel if review is still going on...
|
|
194
194
|
# end
|
|
195
195
|
# end
|
|
@@ -250,7 +250,7 @@ module Ruote::Exp
|
|
|
250
250
|
|
|
251
251
|
# Determines which child expression of the cursor is to be applied next.
|
|
252
252
|
#
|
|
253
|
-
def move_on
|
|
253
|
+
def move_on(workitem=h.applied_workitem)
|
|
254
254
|
|
|
255
255
|
position = workitem['fei'] == h.fei ?
|
|
256
256
|
-1 : Ruote::FlowExpressionId.child_id(workitem['fei'])
|
|
@@ -286,7 +286,7 @@ module Ruote::Exp
|
|
|
286
286
|
# Jumps to an integer position, or the name of an expression
|
|
287
287
|
# or a tag name of a ref name.
|
|
288
288
|
#
|
|
289
|
-
def jump_to
|
|
289
|
+
def jump_to(workitem, position, arg)
|
|
290
290
|
|
|
291
291
|
pos = Integer(arg) rescue nil
|
|
292
292
|
|
data/lib/ruote/exp/fe_define.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#--
|
|
2
|
-
# Copyright (c) 2005-
|
|
2
|
+
# Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
|
|
3
3
|
#
|
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
# of this software and associated documentation files (the "Software"), to deal
|
|
@@ -92,7 +92,7 @@ module Ruote::Exp
|
|
|
92
92
|
# Returns true if the tree's root expression is a definition
|
|
93
93
|
# (define, process_definition, ...)
|
|
94
94
|
#
|
|
95
|
-
def self.is_definition?
|
|
95
|
+
def self.is_definition?(tree)
|
|
96
96
|
|
|
97
97
|
self.expression_names.include?(tree.first)
|
|
98
98
|
end
|
|
@@ -100,7 +100,7 @@ module Ruote::Exp
|
|
|
100
100
|
# Used by instances of this class and also the expression pool,
|
|
101
101
|
# when launching a new process instance.
|
|
102
102
|
#
|
|
103
|
-
def self.reorganize
|
|
103
|
+
def self.reorganize(tree)
|
|
104
104
|
|
|
105
105
|
definitions, bodies = tree[2].partition { |b| is_definition?(b) }
|
|
106
106
|
name = tree[1]['name'] || tree[1].keys.find { |k| tree[1][k] == nil }
|
data/lib/ruote/exp/fe_echo.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#--
|
|
2
|
-
# Copyright (c) 2005-
|
|
2
|
+
# Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
|
|
3
3
|
#
|
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
# of this software and associated documentation files (the "Software"), to deal
|
|
@@ -40,7 +40,7 @@ module Ruote::Exp
|
|
|
40
40
|
|
|
41
41
|
def apply
|
|
42
42
|
|
|
43
|
-
text = "#{attribute_text}\n"
|
|
43
|
+
text = "#{attribute(:text) || attribute_text}\n"
|
|
44
44
|
|
|
45
45
|
if t = @context['s_tracer']
|
|
46
46
|
t << text
|
|
@@ -51,7 +51,7 @@ module Ruote::Exp
|
|
|
51
51
|
reply_to_parent(h.applied_workitem)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
def reply
|
|
54
|
+
def reply(workitem)
|
|
55
55
|
|
|
56
56
|
# never called
|
|
57
57
|
end
|
data/lib/ruote/exp/fe_equals.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#--
|
|
2
|
-
# Copyright (c) 2005-
|
|
2
|
+
# Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
|
|
3
3
|
#
|
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
# of this software and associated documentation files (the "Software"), to deal
|
|
@@ -96,7 +96,7 @@ module Ruote::Exp
|
|
|
96
96
|
keys.collect { |k| grab_value(k) }
|
|
97
97
|
end
|
|
98
98
|
|
|
99
|
-
def grab_value
|
|
99
|
+
def grab_value(k)
|
|
100
100
|
|
|
101
101
|
attval = attribute(k)
|
|
102
102
|
|
data/lib/ruote/exp/fe_error.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#--
|
|
2
|
-
# Copyright (c) 2005-
|
|
2
|
+
# Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
|
|
3
3
|
#
|
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
# of this software and associated documentation files (the "Software"), to deal
|
|
@@ -64,7 +64,7 @@ module Ruote::Exp
|
|
|
64
64
|
'workitem' => h.applied_workitem)
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
-
def reply
|
|
67
|
+
def reply(workitem)
|
|
68
68
|
|
|
69
69
|
return reply_to_parent(workitem) if h.triggered
|
|
70
70
|
|
|
@@ -0,0 +1,519 @@
|
|
|
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/filter'
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
module Ruote::Exp
|
|
29
|
+
|
|
30
|
+
#
|
|
31
|
+
# Filter is a one-way filter expression. It filters workitem fields.
|
|
32
|
+
# Validations and Transformations are possible.
|
|
33
|
+
#
|
|
34
|
+
# Validations will raise errors (that'll block the process segment
|
|
35
|
+
# unless an :on_error attribute somehow deals with the problem).
|
|
36
|
+
#
|
|
37
|
+
# Transformations will copy values around fields.
|
|
38
|
+
#
|
|
39
|
+
# There are two ways to use it. With a single rule or with an array of
|
|
40
|
+
# rules.
|
|
41
|
+
#
|
|
42
|
+
# filter 'x', :type => 'string'
|
|
43
|
+
# # will raise an error if the field 'x' doesn't contain a String
|
|
44
|
+
#
|
|
45
|
+
# or
|
|
46
|
+
#
|
|
47
|
+
# filter :in => [
|
|
48
|
+
# { :field => 'x', :type => 'string' },
|
|
49
|
+
# { :field => 'y', :type => 'number' }
|
|
50
|
+
# ]
|
|
51
|
+
#
|
|
52
|
+
# For the remainder of this piece of documentation, the one rule filter
|
|
53
|
+
# will be used.
|
|
54
|
+
#
|
|
55
|
+
# == filtering targets (field names)
|
|
56
|
+
#
|
|
57
|
+
# Top level field names are OK :
|
|
58
|
+
#
|
|
59
|
+
# filter 'customer_id', :type => 'string'
|
|
60
|
+
# filter 'invoice_id', :type => 'number'
|
|
61
|
+
#
|
|
62
|
+
# Pointing to fields lying deeper is OK :
|
|
63
|
+
#
|
|
64
|
+
# filter 'customer.id', :type => 'number'
|
|
65
|
+
# filter 'customer.name', :type => 'string'
|
|
66
|
+
# filter 'invoice', :type => 'array'
|
|
67
|
+
# filter 'invoice.0.id', :type => 'number'
|
|
68
|
+
#
|
|
69
|
+
# (Note the dollar notation is also OK with such dotted identifiers)
|
|
70
|
+
#
|
|
71
|
+
# It's possible to target multiple fields by passing a list of field names
|
|
72
|
+
# or a regular expression.
|
|
73
|
+
#
|
|
74
|
+
# filter 'city, region, country', :type => 'string'
|
|
75
|
+
# # will make sure that those 3 fields hold a string value
|
|
76
|
+
#
|
|
77
|
+
# filter '/^address\.x_/', :type => number
|
|
78
|
+
# filter '/^address!x_/', :type => number
|
|
79
|
+
# # fields whosename start with x_ in the address hash should be numbers
|
|
80
|
+
#
|
|
81
|
+
# Note the "!" used as a shortcut for "\." in the second line.
|
|
82
|
+
#
|
|
83
|
+
#
|
|
84
|
+
# == validations
|
|
85
|
+
#
|
|
86
|
+
# === 'type'
|
|
87
|
+
#
|
|
88
|
+
# Ruote is a Ruby library, it adopts Ruby "laissez-faire" for workitem
|
|
89
|
+
# fields, but sometimes, some type oriented validation is necessary.
|
|
90
|
+
# Ruote limits itself to the types found in the JSON specification with
|
|
91
|
+
# one or two additions.
|
|
92
|
+
#
|
|
93
|
+
# filter 'x', :type => 'string'
|
|
94
|
+
# filter 'x', :type => 'number'
|
|
95
|
+
# filter 'x', :type => 'bool'
|
|
96
|
+
# filter 'x', :type => 'boolean'
|
|
97
|
+
# filter 'x', :type => 'null'
|
|
98
|
+
#
|
|
99
|
+
# filter 'x', :type => 'array'
|
|
100
|
+
#
|
|
101
|
+
# filter 'x', :type => 'object'
|
|
102
|
+
# filter 'x', :type => 'hash'
|
|
103
|
+
# # 'object' and 'hash' are equivalent
|
|
104
|
+
#
|
|
105
|
+
# It's OK to pass multiple types for a field
|
|
106
|
+
#
|
|
107
|
+
# filter 'x', :type => 'bool,number'
|
|
108
|
+
# filter 'x', :type => [ 'string', 'array' ]
|
|
109
|
+
#
|
|
110
|
+
# filter 'x', :type => 'string,null'
|
|
111
|
+
# # a string or null or not set
|
|
112
|
+
#
|
|
113
|
+
# The array and the object/hash types accept a subtype for their values
|
|
114
|
+
# (a hash/object must have string keys anyway).
|
|
115
|
+
#
|
|
116
|
+
# filter 'x', :type => 'array<number>'
|
|
117
|
+
# filter 'x', :type => 'array<string>'
|
|
118
|
+
# filter 'x', :type => 'array<array<string>>'
|
|
119
|
+
#
|
|
120
|
+
# filter 'x', :type => 'array<string,number>'
|
|
121
|
+
# # an array of strings or numbers (both)
|
|
122
|
+
# filter 'x', :type => 'array<string>,array<number>'
|
|
123
|
+
# # an array of strings or an array of numbers
|
|
124
|
+
#
|
|
125
|
+
# === 'match' and 'smatch'
|
|
126
|
+
#
|
|
127
|
+
# 'match' will check if a field, when turned into a string, matches
|
|
128
|
+
# a given regular expression.
|
|
129
|
+
#
|
|
130
|
+
# filter 'x', :match => '1'
|
|
131
|
+
# # will match "11", 1, 1.0, "212"
|
|
132
|
+
#
|
|
133
|
+
# 'smatch' works the same but only accepts fields that are strings.
|
|
134
|
+
#
|
|
135
|
+
# filter 'x', :smatch => '^user_'
|
|
136
|
+
# # valid only if x's value is a string that starts with "user_"
|
|
137
|
+
#
|
|
138
|
+
# === 'size' and 'empty'
|
|
139
|
+
#
|
|
140
|
+
# 'size' is valid for values that respond to the #size method (strings
|
|
141
|
+
# hashes and arrays).
|
|
142
|
+
#
|
|
143
|
+
# filter 'x', :size => 4
|
|
144
|
+
# # will be valid of values like [ 1, 2, 3, 4 ], "toto" or
|
|
145
|
+
# # { 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4 }
|
|
146
|
+
#
|
|
147
|
+
# filter 'x', :size => [ 4, 5 ]
|
|
148
|
+
# filter 'x', :size => '4,5'
|
|
149
|
+
# # four to five elements
|
|
150
|
+
#
|
|
151
|
+
# filter 'x', :size => [ 4 ]
|
|
152
|
+
# filter 'x', :size => [ 4, nil ]
|
|
153
|
+
# filter 'x', :size => '4,'
|
|
154
|
+
# # four or more elements
|
|
155
|
+
#
|
|
156
|
+
# filter 'x', :size => [ nil, 4 ]
|
|
157
|
+
# filter 'x', :size => ',4'
|
|
158
|
+
# # four elements or less
|
|
159
|
+
#
|
|
160
|
+
# Similarly, the 'empty' check will evaluate to true (ie not raise an
|
|
161
|
+
# exception) if the value responds to #empty? and is, well, not empty.
|
|
162
|
+
#
|
|
163
|
+
# filter 'x', :empty => true
|
|
164
|
+
#
|
|
165
|
+
# === 'in'
|
|
166
|
+
#
|
|
167
|
+
# Checks if a value is in a given set of values.
|
|
168
|
+
#
|
|
169
|
+
# filter 'x', :in => [ 1, 2, 3 ]
|
|
170
|
+
# filter 'x', :in => "john, jeff, jim"
|
|
171
|
+
#
|
|
172
|
+
# === 'has'
|
|
173
|
+
#
|
|
174
|
+
# Checks if an array contains certain values
|
|
175
|
+
#
|
|
176
|
+
# filter 'x', :has => 1
|
|
177
|
+
# filter 'x', :has => "x"
|
|
178
|
+
# filter 'x', :has => [ 1, 7, 12 ]
|
|
179
|
+
# filter 'x', :has => "abraham, bob, charly"
|
|
180
|
+
#
|
|
181
|
+
# Also checks if a hash has certain keys (strings only of course)
|
|
182
|
+
#
|
|
183
|
+
# filter 'x', :has => "x"
|
|
184
|
+
# filter 'x', :has => "abraham, bob, charly"
|
|
185
|
+
#
|
|
186
|
+
# === 'valid'
|
|
187
|
+
#
|
|
188
|
+
# Sometimes, it's better to immediately say 'true' or 'false'.
|
|
189
|
+
#
|
|
190
|
+
# filter 'x', :valid => 'true'
|
|
191
|
+
# filter 'x', :valid => 'false'
|
|
192
|
+
#
|
|
193
|
+
# Not very useful...
|
|
194
|
+
#
|
|
195
|
+
# In fact, it's meant to be used with the dollar notation
|
|
196
|
+
#
|
|
197
|
+
# filter 'x', :valid => '${other.field}'
|
|
198
|
+
# # will be valid if ${other.field} evaluates to 'true'...
|
|
199
|
+
#
|
|
200
|
+
# === cumulating validations
|
|
201
|
+
#
|
|
202
|
+
# As seen before, type validations can be cumulated.
|
|
203
|
+
#
|
|
204
|
+
# filter 'x', :type => 'bool,number'
|
|
205
|
+
#
|
|
206
|
+
# Validations can be cumulated as well
|
|
207
|
+
#
|
|
208
|
+
# filter 'x', :type => 'array<number>', :has => [ 1, 2 ]
|
|
209
|
+
# # will be valid if the field 'x' holds an array of numbers
|
|
210
|
+
# # and that array has 1 and 2 among its elements
|
|
211
|
+
#
|
|
212
|
+
# === validation errors
|
|
213
|
+
#
|
|
214
|
+
# By defaults a validation error will result in a process error (ie the
|
|
215
|
+
# process instance will have to be manually fixed and resumed, or there
|
|
216
|
+
# is a :on_error somewhere dealing automatically with errors).
|
|
217
|
+
#
|
|
218
|
+
# It's possible to prevent raising an error and simply record the validation
|
|
219
|
+
# errors.
|
|
220
|
+
#
|
|
221
|
+
# filter 'x', :type => 'bool,number', :record => true
|
|
222
|
+
#
|
|
223
|
+
# will enumerate validation errors in the '__validation_errors__' workitem
|
|
224
|
+
# field.
|
|
225
|
+
#
|
|
226
|
+
# filter 'y', :type => 'bool,number', :record => 'verrors'
|
|
227
|
+
#
|
|
228
|
+
# will enumerate validation errors in teh 'verrors' workitem field.
|
|
229
|
+
#
|
|
230
|
+
# To flush the recording field, use :flush => true
|
|
231
|
+
#
|
|
232
|
+
# sequence do
|
|
233
|
+
# filter 'x', :type => 'string', :record => true
|
|
234
|
+
# filter 'y', :type => 'number', :record => true, :flush => true
|
|
235
|
+
# participant 'after'
|
|
236
|
+
# end
|
|
237
|
+
#
|
|
238
|
+
# the participant 'after' will only see the result of the second filter.
|
|
239
|
+
#
|
|
240
|
+
# For complex filters, if the first rule has :record => true, the
|
|
241
|
+
# 'recording' will happen for the whole filter.
|
|
242
|
+
#
|
|
243
|
+
# sequence do
|
|
244
|
+
# filter :in => [
|
|
245
|
+
# { :field => 'x', :type => 'string', :record => true },
|
|
246
|
+
# { :field => 'y', :type => 'number' } ]
|
|
247
|
+
# participant 'after'
|
|
248
|
+
# end
|
|
249
|
+
#
|
|
250
|
+
#
|
|
251
|
+
# == transformations
|
|
252
|
+
#
|
|
253
|
+
# So far, only the validation aspect of filter was shown. They can also be
|
|
254
|
+
# used to transform the workitem.
|
|
255
|
+
#
|
|
256
|
+
# filter 'x', :type => 'string', :or => 'missing'
|
|
257
|
+
# # will replace the value of x by 'missing' if it's not a string
|
|
258
|
+
#
|
|
259
|
+
# filter 'z', :remove => true
|
|
260
|
+
# # will remove the workitem field z
|
|
261
|
+
#
|
|
262
|
+
# filter 'a,b,c', 'set' => '---'
|
|
263
|
+
# # sets the field a, b and c to '---'
|
|
264
|
+
#
|
|
265
|
+
# === 'remove'
|
|
266
|
+
#
|
|
267
|
+
# Removes a field (or a subfield).
|
|
268
|
+
#
|
|
269
|
+
# filter 'z', :remove => true
|
|
270
|
+
#
|
|
271
|
+
# === 'default'
|
|
272
|
+
#
|
|
273
|
+
# If there is no value for a field, sets it
|
|
274
|
+
#
|
|
275
|
+
# filter 'x', 'default' => 0
|
|
276
|
+
# # will set x to 0, if it's not set or its value is nil
|
|
277
|
+
#
|
|
278
|
+
# filter '/^user-.+/', 'default' => 'nemo'
|
|
279
|
+
# # will set any 'user-...' field to 'nemo' if its value is nil
|
|
280
|
+
#
|
|
281
|
+
# === 'or'
|
|
282
|
+
#
|
|
283
|
+
# 'or' combines with a condition. The 'or' value is set if the condition
|
|
284
|
+
# evaluates to false.
|
|
285
|
+
#
|
|
286
|
+
# Using 'or' without a condition makes it equivalent to a 'default'.
|
|
287
|
+
#
|
|
288
|
+
# filter 'x', 'or' => 0
|
|
289
|
+
# # will set x to 0, if it's not set or its value is nil
|
|
290
|
+
#
|
|
291
|
+
# filter 'x', 'type' => 'number', 'or' => 0
|
|
292
|
+
# # if x is not set or is not a number, will set it to 0
|
|
293
|
+
#
|
|
294
|
+
# Multiple conditions are OK
|
|
295
|
+
#
|
|
296
|
+
# filter 'x', 't' => 'array', 'has' => 'cat', 'or' => []
|
|
297
|
+
# # if x is an array and has the 'cat' element, nothing will happen.
|
|
298
|
+
# # Else x will be set to [].
|
|
299
|
+
#
|
|
300
|
+
# === 'and'
|
|
301
|
+
#
|
|
302
|
+
# 'and' is much like 'or', but it triggers if the condition evaluates to true.
|
|
303
|
+
#
|
|
304
|
+
# filter 'x', 'type' => number, 'and' => '*removed*'
|
|
305
|
+
# # if x is a number, it will replace it with '*removed*'
|
|
306
|
+
#
|
|
307
|
+
# === 'set'
|
|
308
|
+
#
|
|
309
|
+
# Like 'remove' removes unconditionally, 'set' sets a field unconditionally.
|
|
310
|
+
#
|
|
311
|
+
# filter 'x', 'set' => 'blue'
|
|
312
|
+
# # sets the field x to 'blue'
|
|
313
|
+
#
|
|
314
|
+
# === copy, merge, migrate / to, from
|
|
315
|
+
#
|
|
316
|
+
# # in : { 'x' => 'y' }
|
|
317
|
+
# filter 'x', 'copy_to' => 'z'
|
|
318
|
+
# # out : { 'x' => 'y', 'z' => 'y' }
|
|
319
|
+
#
|
|
320
|
+
# # in : { 'x' => 'y' }
|
|
321
|
+
# filter 'z', 'copy_from' => 'x'
|
|
322
|
+
# # out : { 'x' => 'y', 'z' => 'y' }
|
|
323
|
+
#
|
|
324
|
+
# # in : { 'x' => 'y' }
|
|
325
|
+
# filter 'z', 'copy_from' => 'x'
|
|
326
|
+
# # out : { 'x' => 'y', 'z' => 'y' }
|
|
327
|
+
#
|
|
328
|
+
# # in : { 'a' => %w[ x y ]})
|
|
329
|
+
# filter '/a\.(.+)/', 'copy_to' => 'b\1'
|
|
330
|
+
# # out : { 'a' => %w[ x y ], 'b0' => 'x', 'b1' => 'y' },
|
|
331
|
+
#
|
|
332
|
+
# # in : { 'a' => %w[ x y ]})
|
|
333
|
+
# filter '/a!(.+)/', 'copy_to' => 'b\1'
|
|
334
|
+
# # out : { 'a' => %w[ x y ], 'b0' => 'x', 'b1' => 'y' },
|
|
335
|
+
# #
|
|
336
|
+
# # '!' is used as a replacement for '\.' in regexes
|
|
337
|
+
#
|
|
338
|
+
# # in : { 'a' => 'b', 'c' => 'd', 'source' => [ 7 ] })
|
|
339
|
+
# filter '/^.$/', 'copy_from' => 'source.0'
|
|
340
|
+
# # out : { 'a' => 7, 'c' => 7, 'source' => [ 7 ] },
|
|
341
|
+
#
|
|
342
|
+
# ...
|
|
343
|
+
#
|
|
344
|
+
# 'copy_to' and 'copy_from' copy whole fields. 'move_to' and 'move_from'
|
|
345
|
+
# move fields.
|
|
346
|
+
#
|
|
347
|
+
# 'merge_to' and 'merge_from' merge hashes (or add values to
|
|
348
|
+
# arrays), 'push_to' and 'push_from' are aliases for 'merge_to' and
|
|
349
|
+
# 'merge_from' respectively.
|
|
350
|
+
#
|
|
351
|
+
# 'migrate_to' and 'migrate_from' act like 'merge_to' and 'merge_from' but
|
|
352
|
+
# delete the merge source afterwards (like 'move').
|
|
353
|
+
#
|
|
354
|
+
# All those hash/array filter operations understand the '.' field, meaning
|
|
355
|
+
# the hash being filtered itself.
|
|
356
|
+
#
|
|
357
|
+
# # in : { 'x' => { 'a' => 1, 'b' => 2 } })
|
|
358
|
+
# filter 'x', 'merge_to' => '.'
|
|
359
|
+
# # out : { 'x' => { 'a' => 1, 'b' => 2 }, 'a' => 1, 'b' => 2 },
|
|
360
|
+
#
|
|
361
|
+
# === access to 'previous versions' with ~ and ~~
|
|
362
|
+
#
|
|
363
|
+
# Before a filter is applied, a copy of the hash to filter is placed under
|
|
364
|
+
# the '~' key in the hash itself.
|
|
365
|
+
#
|
|
366
|
+
# this filter will at first set the field x to 0, and then reset it to its
|
|
367
|
+
# original value :
|
|
368
|
+
#
|
|
369
|
+
# filter :in => [
|
|
370
|
+
# { :field => 'x', :set => 0 },
|
|
371
|
+
# { :field => 'x', :copy_from => '~.x' }
|
|
372
|
+
# ]
|
|
373
|
+
#
|
|
374
|
+
# For the 'filter' expression, '~~' contains the same thing as '~', but
|
|
375
|
+
# for the :filter attribute, it contains the hash (workitem fields) as
|
|
376
|
+
# it was when the expression with the :filter attribute got reached (applied).
|
|
377
|
+
#
|
|
378
|
+
# === 'restore' and 'restore_from'
|
|
379
|
+
#
|
|
380
|
+
# Since these two filter operations leverage '~~', they're not very useful
|
|
381
|
+
# for the 'filter' expression. But they make lots of sense for the :filter
|
|
382
|
+
# attribute.
|
|
383
|
+
#
|
|
384
|
+
# # in : { 'x' => 'a', 'y' => 'a' },
|
|
385
|
+
# filter :in => [
|
|
386
|
+
# { 'field' => 'x', 'set' => 'X' },
|
|
387
|
+
# { 'field' => 'y', 'set' => 'Y' },
|
|
388
|
+
# { 'field' => '/^.$/', 'restore' => true } ]
|
|
389
|
+
# # out : { 'x' => 'a', 'y' => 'a' },
|
|
390
|
+
#
|
|
391
|
+
# # in : { 'x' => 'a', 'y' => 'a' },
|
|
392
|
+
# filter :in => [
|
|
393
|
+
# { 'field' => 'A', 'set' => {} },
|
|
394
|
+
# { 'field' => '.', 'merge_to' => 'A' },
|
|
395
|
+
# { 'field' => 'x', 'set' => 'X' },
|
|
396
|
+
# { 'field' => 'y', 'set' => 'Y' },
|
|
397
|
+
# { 'field' => '/^[a-z]$/', 'restore_from' => 'A' },
|
|
398
|
+
# { 'field' => 'A', 'delete' => true } ]
|
|
399
|
+
# # out : { 'x' => 'a', 'y' => 'a' })
|
|
400
|
+
#
|
|
401
|
+
#
|
|
402
|
+
# == short forms
|
|
403
|
+
#
|
|
404
|
+
# Could help make filters a bit more compact.
|
|
405
|
+
#
|
|
406
|
+
# * 'size', 'sz'
|
|
407
|
+
# * 'empty', 'e'
|
|
408
|
+
# * 'in', 'i'
|
|
409
|
+
# * 'has', 'h'
|
|
410
|
+
# * 'type', 't'
|
|
411
|
+
# * 'match', 'm'
|
|
412
|
+
# * 'smatch', 'sm'
|
|
413
|
+
# * 'valid', 'v'
|
|
414
|
+
#
|
|
415
|
+
# * 'remove', 'rm', 'delete', 'del'
|
|
416
|
+
# * 'set', 's'
|
|
417
|
+
# * 'copy_to', 'cp_to'
|
|
418
|
+
# * 'move_to', 'mv_to'
|
|
419
|
+
# * 'merge_to', 'mg_to'
|
|
420
|
+
# * 'migrate_to', 'mi_to'
|
|
421
|
+
# * 'restore', 'restore_from', 'rs'
|
|
422
|
+
#
|
|
423
|
+
#
|
|
424
|
+
# == compared to the :filter attribute
|
|
425
|
+
#
|
|
426
|
+
# The :filter attribute accepts participant names, but for this filter
|
|
427
|
+
# expression, it makes no sense accepting partipants... Simply invoke
|
|
428
|
+
# the participant as usual.
|
|
429
|
+
#
|
|
430
|
+
# The 'restore' operation makes lots of sense for the :filter attribute
|
|
431
|
+
# though.
|
|
432
|
+
#
|
|
433
|
+
class FilterExpression < FlowExpression
|
|
434
|
+
|
|
435
|
+
names :filter
|
|
436
|
+
|
|
437
|
+
def apply
|
|
438
|
+
|
|
439
|
+
filter = referenced_filter || complete_filter || one_line_filter
|
|
440
|
+
|
|
441
|
+
record = filter.first.delete('record') rescue nil
|
|
442
|
+
flush = filter.first.delete('flush') rescue nil
|
|
443
|
+
|
|
444
|
+
record = '__validation_errors__' if record == true
|
|
445
|
+
|
|
446
|
+
opts = {
|
|
447
|
+
:double_tilde => parent_id ?
|
|
448
|
+
(parent.h.applied_workitem['fields'] rescue nil) : nil,
|
|
449
|
+
:no_raise => record
|
|
450
|
+
}
|
|
451
|
+
#
|
|
452
|
+
# parent_fields are placed in the ^^ available to the filter
|
|
453
|
+
|
|
454
|
+
fields = Ruote.filter(filter, h.applied_workitem['fields'], opts)
|
|
455
|
+
|
|
456
|
+
if record and fields.is_a?(Array)
|
|
457
|
+
#
|
|
458
|
+
# validation failed, :record requested, list deviations in
|
|
459
|
+
# the given field name
|
|
460
|
+
|
|
461
|
+
(flush ?
|
|
462
|
+
h.applied_workitem['fields'][record] = [] :
|
|
463
|
+
h.applied_workitem['fields'][record] ||= []
|
|
464
|
+
).concat(fields)
|
|
465
|
+
|
|
466
|
+
reply_to_parent(h.applied_workitem)
|
|
467
|
+
|
|
468
|
+
else
|
|
469
|
+
#
|
|
470
|
+
# filtering successful
|
|
471
|
+
|
|
472
|
+
reply_to_parent(h.applied_workitem.merge('fields' => fields))
|
|
473
|
+
end
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
def reply(workitem)
|
|
477
|
+
|
|
478
|
+
# never called
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
protected
|
|
482
|
+
|
|
483
|
+
def referenced_filter
|
|
484
|
+
|
|
485
|
+
prefix, key = attribute_text.split(':')
|
|
486
|
+
|
|
487
|
+
return nil unless %w[ v var variable f field ].include?(prefix)
|
|
488
|
+
|
|
489
|
+
filter = prefix.match(/^v/) ?
|
|
490
|
+
lookup_variable(key) : Ruote.lookup(h.applied_workitem['fields'], key)
|
|
491
|
+
|
|
492
|
+
if filter.is_a?(Hash) and i = filter['in']
|
|
493
|
+
return i
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
filter
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
def complete_filter
|
|
500
|
+
|
|
501
|
+
return nil if attribute_text != ''
|
|
502
|
+
|
|
503
|
+
attribute(:in)
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
def one_line_filter
|
|
507
|
+
|
|
508
|
+
[ attributes.inject({}) { |h, (k, v)|
|
|
509
|
+
if v.nil?
|
|
510
|
+
h['field'] = k
|
|
511
|
+
else
|
|
512
|
+
h[k] = v
|
|
513
|
+
end
|
|
514
|
+
h
|
|
515
|
+
} ]
|
|
516
|
+
end
|
|
517
|
+
end
|
|
518
|
+
end
|
|
519
|
+
|