ruote 2.3.0.1 → 2.3.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|