ruote 2.3.0.1 → 2.3.0.2
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 +23 -0
- data/CREDITS.txt +4 -0
- data/LICENSE.txt +1 -1
- data/lib/ruote.rb +2 -0
- data/lib/ruote/context.rb +2 -1
- data/lib/ruote/dashboard.rb +169 -13
- data/lib/ruote/dboard/mutation.rb +282 -0
- data/lib/ruote/dboard/process_error.rb +1 -1
- data/lib/ruote/dboard/process_status.rb +61 -48
- data/lib/ruote/engine.rb +1 -1
- data/lib/ruote/exp/command.rb +1 -1
- data/lib/ruote/exp/commanded.rb +1 -1
- data/lib/ruote/exp/condition.rb +2 -1
- data/lib/ruote/exp/fe_add_branches.rb +1 -1
- data/lib/ruote/exp/fe_apply.rb +1 -1
- data/lib/ruote/exp/fe_await.rb +97 -48
- data/lib/ruote/exp/fe_cancel_process.rb +1 -1
- data/lib/ruote/exp/fe_command.rb +2 -3
- data/lib/ruote/exp/fe_concurrence.rb +162 -66
- data/lib/ruote/exp/fe_concurrent_iterator.rb +25 -7
- data/lib/ruote/exp/fe_cron.rb +1 -1
- data/lib/ruote/exp/fe_cursor.rb +10 -11
- data/lib/ruote/exp/fe_define.rb +1 -1
- data/lib/ruote/exp/fe_echo.rb +1 -1
- data/lib/ruote/exp/fe_equals.rb +1 -1
- data/lib/ruote/exp/fe_error.rb +1 -1
- data/lib/ruote/exp/fe_filter.rb +1 -1
- data/lib/ruote/exp/fe_forget.rb +1 -1
- data/lib/ruote/exp/fe_given.rb +1 -1
- data/lib/ruote/exp/fe_if.rb +87 -7
- data/lib/ruote/exp/fe_inc.rb +1 -1
- data/lib/ruote/exp/fe_iterator.rb +1 -1
- data/lib/ruote/exp/fe_listen.rb +1 -1
- data/lib/ruote/exp/fe_lose.rb +1 -1
- data/lib/ruote/exp/fe_noop.rb +1 -1
- data/lib/ruote/exp/fe_on_error.rb +1 -1
- data/lib/ruote/exp/fe_once.rb +1 -1
- data/lib/ruote/exp/fe_participant.rb +49 -16
- data/lib/ruote/exp/fe_read.rb +1 -1
- data/lib/ruote/exp/fe_redo.rb +1 -1
- data/lib/ruote/exp/fe_ref.rb +1 -1
- data/lib/ruote/exp/fe_registerp.rb +1 -1
- data/lib/ruote/exp/fe_reserve.rb +1 -1
- data/lib/ruote/exp/fe_restore.rb +1 -7
- data/lib/ruote/exp/fe_save.rb +1 -1
- data/lib/ruote/exp/fe_sequence.rb +1 -1
- data/lib/ruote/exp/fe_set.rb +1 -1
- data/lib/ruote/exp/fe_stall.rb +1 -1
- data/lib/ruote/exp/fe_subprocess.rb +1 -1
- data/lib/ruote/exp/fe_that.rb +1 -1
- data/lib/ruote/exp/fe_undo.rb +1 -1
- data/lib/ruote/exp/fe_unregisterp.rb +1 -1
- data/lib/ruote/exp/fe_wait.rb +1 -1
- data/lib/ruote/exp/flow_expression.rb +117 -8
- data/lib/ruote/exp/iterator.rb +1 -1
- data/lib/ruote/exp/ro_attributes.rb +1 -1
- data/lib/ruote/exp/ro_filters.rb +1 -1
- data/lib/ruote/exp/ro_on_x.rb +4 -2
- data/lib/ruote/exp/ro_persist.rb +1 -1
- data/lib/ruote/exp/ro_timers.rb +1 -1
- data/lib/ruote/exp/ro_variables.rb +1 -1
- data/lib/ruote/extract.rb +125 -0
- data/lib/ruote/fei.rb +10 -73
- data/lib/ruote/id/mnemo_wfid_generator.rb +1 -1
- data/lib/ruote/id/wfid_generator.rb +1 -1
- data/lib/ruote/log/default_history.rb +17 -3
- data/lib/ruote/log/fancy_printing.rb +12 -32
- data/lib/ruote/log/storage_history.rb +1 -1
- data/lib/ruote/log/wait_logger.rb +15 -7
- data/lib/ruote/merge.rb +123 -0
- data/lib/ruote/observer.rb +1 -1
- data/lib/ruote/part/block_participant.rb +1 -1
- data/lib/ruote/part/code_participant.rb +1 -1
- data/lib/ruote/part/engine_participant.rb +1 -1
- data/lib/ruote/part/local_participant.rb +9 -1
- data/lib/ruote/part/no_op_participant.rb +1 -1
- data/lib/ruote/part/null_participant.rb +1 -1
- data/lib/ruote/part/participant.rb +1 -1
- data/lib/ruote/part/rev_participant.rb +1 -1
- data/lib/ruote/part/smtp_participant.rb +1 -1
- data/lib/ruote/part/storage_participant.rb +18 -1
- data/lib/ruote/part/template.rb +1 -1
- data/lib/ruote/reader.rb +1 -1
- data/lib/ruote/reader/json.rb +1 -1
- data/lib/ruote/reader/radial.rb +4 -4
- data/lib/ruote/reader/ruby_dsl.rb +1 -1
- data/lib/ruote/reader/xml.rb +1 -1
- data/lib/ruote/receiver/base.rb +13 -1
- data/lib/ruote/storage/base.rb +8 -14
- data/lib/ruote/storage/composite_storage.rb +1 -1
- data/lib/ruote/storage/fs_storage.rb +1 -1
- data/lib/ruote/storage/hash_storage.rb +2 -1
- data/lib/ruote/svc/dispatch_pool.rb +29 -18
- data/lib/ruote/svc/dollar_sub.rb +5 -8
- data/lib/ruote/svc/error_handler.rb +1 -1
- data/lib/ruote/svc/expression_map.rb +1 -1
- data/lib/ruote/svc/participant_list.rb +8 -5
- data/lib/ruote/svc/tracker.rb +154 -56
- data/lib/ruote/svc/treechecker.rb +1 -1
- data/lib/ruote/tree_dot.rb +1 -1
- data/lib/ruote/util/deep.rb +4 -2
- data/lib/ruote/util/filter.rb +1 -1
- data/lib/ruote/util/hashdot.rb +1 -1
- data/lib/ruote/util/look.rb +1 -1
- data/lib/ruote/util/lookup.rb +1 -1
- data/lib/ruote/util/misc.rb +51 -1
- data/lib/ruote/util/mpatch.rb +1 -1
- data/lib/ruote/util/ometa.rb +1 -1
- data/lib/ruote/util/subprocess.rb +1 -1
- data/lib/ruote/util/time.rb +3 -3
- data/lib/ruote/util/tree.rb +43 -4
- data/lib/ruote/version.rb +2 -2
- data/lib/ruote/worker.rb +30 -18
- data/lib/ruote/workitem.rb +1 -1
- data/ruote.gemspec +6 -2
- data/test/functional/base.rb +0 -1
- data/test/functional/concurrent_base.rb +1 -1
- data/test/functional/eft_14_cursor.rb +42 -52
- data/test/functional/eft_16_if.rb +24 -16
- data/test/functional/eft_18_concurrent_iterator.rb +31 -1
- data/test/functional/eft_6_concurrence.rb +149 -34
- data/test/functional/ft_10_dollar.rb +14 -30
- data/test/functional/ft_12_launchitem.rb +15 -0
- data/test/functional/ft_1_process_status.rb +62 -13
- data/test/functional/ft_20_storage_participant.rb +25 -0
- data/test/functional/ft_38_participant_more.rb +1 -1
- data/test/functional/ft_42_storage_copy.rb +1 -3
- data/test/functional/ft_43_participant_on_reply.rb +63 -5
- data/test/functional/ft_66_flank.rb +41 -0
- data/test/functional/ft_6_on_cancel.rb +9 -18
- data/test/functional/ft_71_retries.rb +25 -12
- data/test/functional/ft_79_attach.rb +138 -0
- data/test/functional/ft_7_tags.rb +27 -0
- data/test/functional/ft_80_pause_on_apply.rb +64 -0
- data/test/functional/ft_81_mutation.rb +417 -0
- data/test/functional/ft_82_await_attribute.rb +84 -0
- data/test/functional/ft_83_trackers.rb +79 -0
- data/test/functional/storage.rb +3 -4
- data/test/unit/ut_12_wait_logger.rb +41 -3
- data/test/unit/ut_15_util.rb +30 -0
- data/test/unit/ut_17_merge.rb +54 -53
- data/test/unit/ut_1_fei.rb +2 -2
- data/test/unit/ut_24_radial_reader.rb +7 -0
- data/test/unit/ut_26_deep.rb +14 -0
- data/test/unit/ut_5_tree.rb +38 -28
- metadata +206 -169
- data/couch_url.txt +0 -1
- data/lib/ruote/exp/merge.rb +0 -134
data/CHANGELOG.txt
CHANGED
@@ -2,6 +2,29 @@
|
|
2
2
|
= ruote - CHANGELOG.txt
|
3
3
|
|
4
4
|
|
5
|
+
== ruote - 2.3.1 not yet released
|
6
|
+
|
7
|
+
|
8
|
+
== ruote - 2.3.0.2 not yet released
|
9
|
+
|
10
|
+
- pin ruote.gemspec to ruby_parser ~>2.3 and parslet 1.4.0
|
11
|
+
- add Ruote.[de]camelize
|
12
|
+
- expose 'history_max_size' config option
|
13
|
+
- Dashboard#get_trackers
|
14
|
+
- Dashboard#remove_tracker(fei_sid_or_id, wfid)
|
15
|
+
- :await attribute
|
16
|
+
- let Ruote.deep_mutate mutate matching keys and their collection values
|
17
|
+
- stop tripping on empty tags ("")
|
18
|
+
- jump to 'x' where x in "participant 'x'" unlocked (thanks Tobias Neubert)
|
19
|
+
- unlock on_error more (on_error: "retry * 3, pass" for example)
|
20
|
+
- revise "$x" (literal dollars), now return nil instead of "$x" when missing
|
21
|
+
- _auto_remove (and _alter) for trackers (pause on apply++)
|
22
|
+
- pause on apply concept brought in
|
23
|
+
- Dashboard#add_tracker
|
24
|
+
- Dashboard#attach(fei, pdef)
|
25
|
+
- participant#on_error
|
26
|
+
|
27
|
+
|
5
28
|
== ruote - 2.3.0.1 released 2012/09/08
|
6
29
|
|
7
30
|
- pin ruote.gemspec to blankslate 2.1.2.4
|
data/CREDITS.txt
CHANGED
@@ -13,6 +13,8 @@ Kenneth Kalmer - http://www.opensourcery.co.za
|
|
13
13
|
|
14
14
|
== Contributors
|
15
15
|
|
16
|
+
Mario Camou - lots of help
|
17
|
+
Peter Brindisi - https://gitbhub.com/npj
|
16
18
|
Gvozden Neskovic - https://github.com/ladenBrain
|
17
19
|
Hartog C. de Mik - https://github.com/coffeeaddict
|
18
20
|
Doug Bryant - re_dispatch bug and 'dispatched' << 'workitem'
|
@@ -54,6 +56,8 @@ Richard Jennings
|
|
54
56
|
|
55
57
|
== Feedback
|
56
58
|
|
59
|
+
Larry Marburger - await attribute clarifications
|
60
|
+
Tobias Neubert - jump to participant 'x'
|
57
61
|
tsdeng - https://github.com/tsdeng
|
58
62
|
Klaus Schmidtmamn - sub_wf_name inspiration
|
59
63
|
chaofan - https://github.com/chaofoan
|
data/LICENSE.txt
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
Copyright (c) 2001-
|
2
|
+
Copyright (c) 2001-2013, 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
|
data/lib/ruote.rb
CHANGED
data/lib/ruote/context.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#--
|
2
|
-
# Copyright (c) 2005-
|
2
|
+
# Copyright (c) 2005-2013, 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
|
@@ -240,6 +240,7 @@ module Ruote
|
|
240
240
|
|
241
241
|
default_conf.merge(conf).each do |key, value|
|
242
242
|
|
243
|
+
key = key.to_s
|
243
244
|
add_service(key, *value) if SERVICE_PREFIX.match(key)
|
244
245
|
end
|
245
246
|
end
|
data/lib/ruote/dashboard.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#--
|
2
|
-
# Copyright (c) 2005-
|
2
|
+
# Copyright (c) 2005-2013, 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
|
@@ -25,6 +25,7 @@
|
|
25
25
|
require 'ruote/context'
|
26
26
|
require 'ruote/util/ometa'
|
27
27
|
require 'ruote/receiver/base'
|
28
|
+
require 'ruote/dboard/mutation'
|
28
29
|
require 'ruote/dboard/process_status'
|
29
30
|
|
30
31
|
|
@@ -178,6 +179,48 @@ module Ruote
|
|
178
179
|
wfid
|
179
180
|
end
|
180
181
|
|
182
|
+
# Given a flow expression id, locates the corresponding ruote
|
183
|
+
# expression and attaches a subprocess to it.
|
184
|
+
#
|
185
|
+
# Accepts the fei as a Hash or as a FlowExpressionId instance.
|
186
|
+
#
|
187
|
+
# By default, the workitem of the expression you attach to provides
|
188
|
+
# the initial workitem for the attached branch. By using the
|
189
|
+
# :fields/:workitem or :merge_fields options, one can change that.
|
190
|
+
#
|
191
|
+
# Returns the fei of the attached [root] expression
|
192
|
+
# (as a FlowExpressionId instance).
|
193
|
+
#
|
194
|
+
def attach(fei_or_fe, definition, opts={})
|
195
|
+
|
196
|
+
fe = Ruote.extract_fexp(@context, fei_or_fe).h
|
197
|
+
fei = fe['fei']
|
198
|
+
|
199
|
+
cfei = fei.merge(
|
200
|
+
'expid' => "#{fei['expid']}_0",
|
201
|
+
'subid' => Ruote.generate_subid(fei.inspect))
|
202
|
+
|
203
|
+
tree = @context.reader.read(definition)
|
204
|
+
tree[0] = 'sequence'
|
205
|
+
|
206
|
+
fields = fe['applied_workitem']['fields']
|
207
|
+
if fs = opts[:fields] || opts[:workitem]
|
208
|
+
fields = fs
|
209
|
+
elsif fs = opts[:merge_fields]
|
210
|
+
fields.merge!(fs)
|
211
|
+
end
|
212
|
+
|
213
|
+
@context.storage.put_msg(
|
214
|
+
'launch', # "apply" is OK, but "launch" stands out better
|
215
|
+
'parent_id' => fei,
|
216
|
+
'fei' => cfei,
|
217
|
+
'tree' => tree,
|
218
|
+
'workitem' => { 'fields' => fields },
|
219
|
+
'attached' => true)
|
220
|
+
|
221
|
+
Ruote::FlowExpressionId.new(cfei)
|
222
|
+
end
|
223
|
+
|
181
224
|
# Given a workitem or a fei, will do a cancel_expression,
|
182
225
|
# else it's a wfid and it does a cancel_process.
|
183
226
|
#
|
@@ -325,6 +368,11 @@ module Ruote
|
|
325
368
|
# dashboard.re_apply(
|
326
369
|
# fei, :fields => { 'customer' => 'bob' })
|
327
370
|
#
|
371
|
+
# :workitem is ok too
|
372
|
+
#
|
373
|
+
# dashboard.re_apply(
|
374
|
+
# fei, :workitem => { 'fields' => { 'customer' => 'bob' } })
|
375
|
+
#
|
328
376
|
# :merge_in_fields is used to add / override fields
|
329
377
|
#
|
330
378
|
# dashboard.re_apply(
|
@@ -338,6 +386,28 @@ module Ruote
|
|
338
386
|
're_apply' => Ruote.keys_to_s(opts))
|
339
387
|
end
|
340
388
|
|
389
|
+
# Returns a Mutation instance listing all the operations necessary
|
390
|
+
# to transform the current tree of the process (wfid) into
|
391
|
+
# the given definition tree (pdef).
|
392
|
+
#
|
393
|
+
# See also #apply_mutation
|
394
|
+
#
|
395
|
+
def compute_mutation(wfid, pdef)
|
396
|
+
|
397
|
+
Mutation.new(self, wfid, @context.reader.read(pdef))
|
398
|
+
end
|
399
|
+
|
400
|
+
# Computes mutation and immediately applies it...
|
401
|
+
#
|
402
|
+
# See #compute_mutation
|
403
|
+
#
|
404
|
+
# Return the mutation instance (forensic?)
|
405
|
+
#
|
406
|
+
def apply_mutation(wfid, pdef)
|
407
|
+
|
408
|
+
Mutation.new(self, wfid, @context.reader.read(pdef)).apply
|
409
|
+
end
|
410
|
+
|
341
411
|
# This method re_apply all the leaves of a process instance. It's meant
|
342
412
|
# to be used against stalled workflows to give them back the spark of
|
343
413
|
# life.
|
@@ -676,11 +746,11 @@ module Ruote
|
|
676
746
|
# def initialize(opts)
|
677
747
|
# @name = opts['name']
|
678
748
|
# end
|
679
|
-
# def
|
749
|
+
# def on_workitem
|
680
750
|
# workitem.fields['rocket_name'] = @name
|
681
751
|
# send_to_the_moon(workitem)
|
682
752
|
# end
|
683
|
-
# def
|
753
|
+
# def on_cancel
|
684
754
|
# # do nothing
|
685
755
|
# end
|
686
756
|
# end
|
@@ -693,11 +763,14 @@ module Ruote
|
|
693
763
|
# class TotalParticipant
|
694
764
|
# include Ruote::LocalParticipant
|
695
765
|
#
|
696
|
-
# def
|
766
|
+
# def on_workitem
|
697
767
|
# workitem['total'] = workitem.fields['items'].inject(0.0) { |t, item|
|
698
768
|
# t + item['count'] * PricingService.lookup(item['id'])
|
699
769
|
# }
|
700
|
-
#
|
770
|
+
# reply
|
771
|
+
# end
|
772
|
+
#
|
773
|
+
# def on_cancel
|
701
774
|
# end
|
702
775
|
# end
|
703
776
|
# dashboard.register_participant 'total', TotalParticipant
|
@@ -1077,15 +1150,72 @@ module Ruote
|
|
1077
1150
|
#
|
1078
1151
|
def on_terminate=(target)
|
1079
1152
|
|
1153
|
+
msg = {
|
1154
|
+
'action' => 'launch',
|
1155
|
+
'tree' => target.is_a?(String) ?
|
1156
|
+
[ 'define', {}, [ [ target, {}, [] ] ] ] : target,
|
1157
|
+
'workitem' => 'replace' }
|
1158
|
+
|
1080
1159
|
@context.tracker.add_tracker(
|
1081
|
-
nil,
|
1082
|
-
'terminated',
|
1083
|
-
'on_terminate',
|
1084
|
-
nil,
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1160
|
+
nil, # do not track a specific wfid
|
1161
|
+
'terminated', # react on 'error_intercepted' msgs
|
1162
|
+
'on_terminate', # the identifier
|
1163
|
+
nil, # no specific condition
|
1164
|
+
msg) # the message that gets triggered
|
1165
|
+
end
|
1166
|
+
|
1167
|
+
# /!\ warning: advanced method.
|
1168
|
+
#
|
1169
|
+
# Adds a tracker to the ruote engine.
|
1170
|
+
#
|
1171
|
+
# === Arguments
|
1172
|
+
#
|
1173
|
+
# * wfid:
|
1174
|
+
# When nil will track any workflow execution, when set will only
|
1175
|
+
# react on msgs for the given wfid.
|
1176
|
+
# * action:
|
1177
|
+
# A string like "apply", "reply" or "receive", the action being tracked
|
1178
|
+
# May begin with a "pre_" prefix.
|
1179
|
+
# * tracker_id:
|
1180
|
+
# When nil, ruote chooses a tracker_id, else its the unique identifier
|
1181
|
+
# for the new tracker.
|
1182
|
+
# * conditions:
|
1183
|
+
# A Hash of keys pointing to arrays of expected values.
|
1184
|
+
# For example { 'tree.0' ~=> [ 'alfred', 'knuth' ] } will trigger
|
1185
|
+
# if the first element of msg['tree'] equals alfred or knuth.
|
1186
|
+
# * msg:
|
1187
|
+
# The msg to place in the msg queue if the tracker matches the msg,
|
1188
|
+
# the reaction.
|
1189
|
+
#
|
1190
|
+
# Returns the tracker_id.
|
1191
|
+
#
|
1192
|
+
def add_tracker(wfid, action, tracker_id, conditions, msg)
|
1193
|
+
|
1194
|
+
@context.tracker.add_tracker(wfid, action, tracker_id, conditions, msg)
|
1195
|
+
end
|
1196
|
+
|
1197
|
+
# /!\ warning: advanced method.
|
1198
|
+
#
|
1199
|
+
# Removes a tracker from the ruote system.
|
1200
|
+
#
|
1201
|
+
# The first arg is a FlowExpressionId, in its instance form, hash form or
|
1202
|
+
# shortened (sid) string form. It can also be any string (any tracker id).
|
1203
|
+
#
|
1204
|
+
# The second arg is optional, it's a wfid. It's useful for some storage
|
1205
|
+
# implementations (like ruote-swf) and helps determine how to grab
|
1206
|
+
# the tracker list. Most of the ruote deployments don't need that arg set.
|
1207
|
+
#
|
1208
|
+
def remove_tracker(fei_sid_or_id, wfid=nil)
|
1209
|
+
|
1210
|
+
@context.tracker.remove_tracker(fei_sid_or_id, wfid)
|
1211
|
+
end
|
1212
|
+
|
1213
|
+
# Returns a hash { tracker_id => tracker_hash } enumerating all
|
1214
|
+
# the trackers in the ruote system.
|
1215
|
+
#
|
1216
|
+
def get_trackers(wfid=nil)
|
1217
|
+
|
1218
|
+
@context.storage.get_trackers(wfid)['trackers']
|
1089
1219
|
end
|
1090
1220
|
|
1091
1221
|
# A debug helper :
|
@@ -1100,6 +1230,32 @@ module Ruote
|
|
1100
1230
|
@context.logger.noisy = b
|
1101
1231
|
end
|
1102
1232
|
|
1233
|
+
# Warning: advanced method.
|
1234
|
+
#
|
1235
|
+
# Currently only used by mutations.
|
1236
|
+
#
|
1237
|
+
def update_expression(fei, opts)
|
1238
|
+
|
1239
|
+
fei = Ruote.extract_fei(fei)
|
1240
|
+
fexp = Ruote::Exp::FlowExpression::fetch(@context, fei)
|
1241
|
+
|
1242
|
+
raise ArgumentError.new(
|
1243
|
+
"no expression found with fei #{fei.sid}"
|
1244
|
+
) unless fexp
|
1245
|
+
|
1246
|
+
if t = opts[:tree]
|
1247
|
+
fexp.h.updated_tree = opts[:tree]
|
1248
|
+
end
|
1249
|
+
|
1250
|
+
r = @context.storage.put(fexp.h)
|
1251
|
+
|
1252
|
+
raise ArgumentError.new(
|
1253
|
+
"expression #{fei.sid} is gone"
|
1254
|
+
) if r == true
|
1255
|
+
|
1256
|
+
return update_expression(fei, opts) unless r.nil?
|
1257
|
+
end
|
1258
|
+
|
1103
1259
|
protected
|
1104
1260
|
|
1105
1261
|
# Used by #pause and #resume.
|
@@ -0,0 +1,282 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2005-2013, 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
|
27
|
+
|
28
|
+
#
|
29
|
+
# Gathers info about a possible mutation. The point of application (fei),
|
30
|
+
# the new tree (tree) and if it's a re_apply or an update (only changing
|
31
|
+
# the tree of the expression behind (fei)).
|
32
|
+
#
|
33
|
+
class MutationPoint
|
34
|
+
|
35
|
+
attr_reader :fei
|
36
|
+
attr_reader :tree
|
37
|
+
attr_reader :type # :re_apply or :update
|
38
|
+
|
39
|
+
def initialize(fei, tree, type)
|
40
|
+
|
41
|
+
@fei = fei
|
42
|
+
@tree = tree
|
43
|
+
@type = type
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_s
|
47
|
+
|
48
|
+
s = []
|
49
|
+
s << self.class.name
|
50
|
+
s << " at: #{@fei.sid} (#{@fei.expid})"
|
51
|
+
s << " action: #{@type.inspect}"
|
52
|
+
s << " tree:"
|
53
|
+
|
54
|
+
s.concat(
|
55
|
+
Ruote::Reader.to_radial(@tree).split("\n").map { |l| " | #{l}" })
|
56
|
+
|
57
|
+
s.join("\n")
|
58
|
+
end
|
59
|
+
|
60
|
+
def apply(dboard, option=nil)
|
61
|
+
|
62
|
+
option ||= @type
|
63
|
+
option = option.to_sym
|
64
|
+
|
65
|
+
return if option != :force_update && option != @type
|
66
|
+
|
67
|
+
type = option == :force_update ? :update : @type
|
68
|
+
|
69
|
+
if type == :re_apply
|
70
|
+
dboard.re_apply(@fei, :tree => @tree)
|
71
|
+
else
|
72
|
+
dboard.update_expression(@fei, :tree => @tree)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# A set of mutation points.
|
79
|
+
#
|
80
|
+
# Initialized by Ruote::Dashboard#compute_mutation
|
81
|
+
#
|
82
|
+
class Mutation
|
83
|
+
|
84
|
+
# ProcessStatus instance (advanced stuff).
|
85
|
+
#
|
86
|
+
attr_reader :ps
|
87
|
+
|
88
|
+
attr_reader :points
|
89
|
+
|
90
|
+
def initialize(dboard, wfid, tree)
|
91
|
+
|
92
|
+
@dboard = dboard
|
93
|
+
@points = []
|
94
|
+
@ps = @dboard.ps(wfid)
|
95
|
+
|
96
|
+
walk(@ps.root_expression, Ruote.compact_tree(tree))
|
97
|
+
|
98
|
+
@points = @points.sort_by { |point| point.fei.expid }
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_a
|
102
|
+
|
103
|
+
@points.collect { |pt|
|
104
|
+
{ 'fei' => pt.fei, 'action' => pt.type, 'tree' => pt.tree }
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
def to_s
|
109
|
+
|
110
|
+
@points.collect(&:to_s).join("\n")
|
111
|
+
end
|
112
|
+
|
113
|
+
# Applies the mutation, :update points first then :re_apply points.
|
114
|
+
#
|
115
|
+
# Accepts an option, nil means apply all, :update means apply only
|
116
|
+
# update mutations points, :re_apply means apply on re_apply points,
|
117
|
+
# :force_update means apply all but turn re_apply points into update
|
118
|
+
# points.
|
119
|
+
#
|
120
|
+
def apply(option=nil)
|
121
|
+
|
122
|
+
updates, re_applies = @points.partition { |pt| pt.type == :update }
|
123
|
+
points = updates + re_applies
|
124
|
+
|
125
|
+
points.each { |pt| pt.apply(@dboard, option) }
|
126
|
+
|
127
|
+
self
|
128
|
+
end
|
129
|
+
|
130
|
+
protected
|
131
|
+
|
132
|
+
def register(point)
|
133
|
+
|
134
|
+
pt = @points.find { |p| p.fei == point.fei }
|
135
|
+
|
136
|
+
if pt && point.type == :re_apply
|
137
|
+
@points.delete(pt)
|
138
|
+
@points << point
|
139
|
+
else
|
140
|
+
@points << point
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Look for mutation points in an expression and its children.
|
145
|
+
#
|
146
|
+
def walk(fexp, tree)
|
147
|
+
|
148
|
+
ftree = Ruote.compact_tree(@ps.current_tree(fexp))
|
149
|
+
|
150
|
+
if ftree[0] != tree[0] || ftree[1] != tree[1]
|
151
|
+
#
|
152
|
+
# if there is anything different between the current tree and the
|
153
|
+
# desired tree, let's force a re-apply
|
154
|
+
|
155
|
+
register(MutationPoint.new(fexp.fei, tree, :re_apply))
|
156
|
+
|
157
|
+
elsif ftree[2] == tree[2]
|
158
|
+
#
|
159
|
+
# else, if the tree children are the same, exit, there is nothing to do
|
160
|
+
|
161
|
+
return
|
162
|
+
|
163
|
+
else
|
164
|
+
|
165
|
+
register(MutationPoint.new(fexp.fei, tree, :update))
|
166
|
+
#
|
167
|
+
# NOTE: maybe a switch for this mutation not to be added would
|
168
|
+
# be necessary...
|
169
|
+
|
170
|
+
if fexp.is_concurrent?
|
171
|
+
#
|
172
|
+
# concurrent expressions follow a different heuristic
|
173
|
+
|
174
|
+
walk_concurrence(fexp, ftree, tree)
|
175
|
+
|
176
|
+
else
|
177
|
+
#
|
178
|
+
# all other expressions are considered sequence-like
|
179
|
+
|
180
|
+
walk_sequence(fexp, ftree, tree)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Look for mutation points in a concurrent expression (concurrence or
|
186
|
+
# concurrent-iterator).
|
187
|
+
#
|
188
|
+
def walk_concurrence(fexp, ftree, tree)
|
189
|
+
|
190
|
+
if ftree[2].size != tree[2].size
|
191
|
+
#
|
192
|
+
# that's lazy, but why not?
|
193
|
+
#
|
194
|
+
# we could add/apply a new child...
|
195
|
+
|
196
|
+
register(MutationPoint.new(fexp.fei, tree, :re_apply))
|
197
|
+
|
198
|
+
else
|
199
|
+
#
|
200
|
+
# if there is a least one child that replied and whose
|
201
|
+
# tree must be changes, then re-apply the whole concurrence
|
202
|
+
#
|
203
|
+
# else try to re-apply only the necessary branch (walk them)
|
204
|
+
|
205
|
+
branches = ftree[2].zip(tree[2]).each_with_object([]) { |(ft, t), a|
|
206
|
+
#
|
207
|
+
# match child expressions (if not yet replied) with current tree (ft)
|
208
|
+
# and desired tree (t)
|
209
|
+
#
|
210
|
+
cfei = fexp.children[a.size]
|
211
|
+
cexp = cfei ? @ps.fexp(cfei) : nil
|
212
|
+
a << [ cexp, ft, t ]
|
213
|
+
#
|
214
|
+
}.select { |cexp, ft, t|
|
215
|
+
#
|
216
|
+
# only keep diverging branches
|
217
|
+
#
|
218
|
+
ft != t
|
219
|
+
}
|
220
|
+
|
221
|
+
branches.each do |cexp, ft, t|
|
222
|
+
|
223
|
+
next if cexp
|
224
|
+
|
225
|
+
# there is at least one branch that replied,
|
226
|
+
# this forces re-apply for the whole concurrence
|
227
|
+
|
228
|
+
register(MutationPoint.new(fexp.fei, tree, :re_apply))
|
229
|
+
return
|
230
|
+
end
|
231
|
+
|
232
|
+
branches.each do |cexp, ft, t|
|
233
|
+
#
|
234
|
+
# we're left with divering branches that haven't yet replied,
|
235
|
+
# let's walk to register the mutation point deep into it
|
236
|
+
|
237
|
+
walk(cexp, t)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
# Look for mutation points in any non-concurrent expression.
|
243
|
+
#
|
244
|
+
def walk_sequence(fexp, ftree, tree)
|
245
|
+
|
246
|
+
i = fexp.child_ids.first
|
247
|
+
|
248
|
+
ehead = ftree[2].take(i)
|
249
|
+
ecurrent = ftree[2][i]
|
250
|
+
etail = ftree[2].drop(i + 1)
|
251
|
+
head = tree[2].take(i)
|
252
|
+
current = tree[2][i]
|
253
|
+
tail = tree[2].drop(i + 1)
|
254
|
+
|
255
|
+
if ehead != head
|
256
|
+
#
|
257
|
+
# if the name and/or attributes of the exp are supposed to change
|
258
|
+
# then we have to reapply it
|
259
|
+
#
|
260
|
+
register(MutationPoint.new(fexp.fei, tree, :re_apply))
|
261
|
+
return
|
262
|
+
end
|
263
|
+
|
264
|
+
if ecurrent != current
|
265
|
+
#
|
266
|
+
# if the child currently applied is supposed to change, let's walk
|
267
|
+
# it down
|
268
|
+
#
|
269
|
+
walk(@ps.fexp(fexp.children.first), current)
|
270
|
+
end
|
271
|
+
|
272
|
+
#if etail != tail
|
273
|
+
# #
|
274
|
+
# # if elements are added at the end of the sequence, let's register
|
275
|
+
# # a mutation that simply changes the tree (no need to re-apply)
|
276
|
+
# #
|
277
|
+
# register(MutationPoint.new(fexp.fei, tree, :update))
|
278
|
+
#end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|