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/lib/ruote/fei.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 'digest/md5'
|
26
26
|
|
27
27
|
require 'ruote/version'
|
28
|
+
require 'ruote/extract'
|
28
29
|
require 'ruote/workitem'
|
29
30
|
require 'ruote/util/misc'
|
30
31
|
require 'ruote/util/hashdot'
|
@@ -32,63 +33,6 @@ require 'ruote/util/hashdot'
|
|
32
33
|
|
33
34
|
module Ruote
|
34
35
|
|
35
|
-
# A shortcut for
|
36
|
-
#
|
37
|
-
# Ruote::FlowExpressionId.to_storage_id(fei)
|
38
|
-
#
|
39
|
-
def self.to_storage_id(fei)
|
40
|
-
|
41
|
-
Ruote::FlowExpressionId.to_storage_id(fei)
|
42
|
-
end
|
43
|
-
|
44
|
-
# A shorter shortcut for
|
45
|
-
#
|
46
|
-
# Ruote::FlowExpressionId.to_storage_id(fei)
|
47
|
-
#
|
48
|
-
def self.sid(fei)
|
49
|
-
|
50
|
-
Ruote::FlowExpressionId.to_storage_id(fei)
|
51
|
-
end
|
52
|
-
|
53
|
-
# A shortcut for
|
54
|
-
#
|
55
|
-
# Ruote::FlowExpressionId.is_a_fei?(o)
|
56
|
-
#
|
57
|
-
def self.is_a_fei?(o)
|
58
|
-
|
59
|
-
Ruote::FlowExpressionId.is_a_fei?(o)
|
60
|
-
end
|
61
|
-
|
62
|
-
# Will do its best to return a wfid (String) or a fei (Hash instance)
|
63
|
-
# extract from the given o argument.
|
64
|
-
#
|
65
|
-
def self.extract_id(o)
|
66
|
-
|
67
|
-
return o if o.is_a?(String) and o.index('!').nil? # wfid
|
68
|
-
|
69
|
-
Ruote::FlowExpressionId.extract_h(o)
|
70
|
-
end
|
71
|
-
|
72
|
-
# Given something, tries to return the fei (Ruote::FlowExpressionId) in it.
|
73
|
-
#
|
74
|
-
def self.extract_fei(o)
|
75
|
-
|
76
|
-
Ruote::FlowExpressionId.extract(o)
|
77
|
-
end
|
78
|
-
|
79
|
-
# Given an object, will return the wfid (workflow instance id) nested into
|
80
|
-
# it (or nil if it can't find or doesn't know how to find).
|
81
|
-
#
|
82
|
-
# The wfid is a String instance.
|
83
|
-
#
|
84
|
-
def self.extract_wfid(o)
|
85
|
-
|
86
|
-
return o.strip == '' ? nil : o if o.is_a?(String)
|
87
|
-
return o.wfid if o.respond_to?(:wfid)
|
88
|
-
return o['wfid'] || o.fetch('fei', {})['wfid'] if o.respond_to?(:[])
|
89
|
-
nil
|
90
|
-
end
|
91
|
-
|
92
36
|
# This function is used to generate the subids. Each flow
|
93
37
|
# expression receives such an id (it's useful for cursors, loops and
|
94
38
|
# forgotten branches).
|
@@ -162,11 +106,13 @@ module Ruote
|
|
162
106
|
|
163
107
|
def self.to_storage_id(hfei)
|
164
108
|
|
165
|
-
hfei.respond_to?(:to_storage_id) ?
|
166
|
-
hfei.to_storage_id :
|
167
|
-
"#{hfei['expid']}!#{hfei['subid'] || hfei['sub_wfid']}!#{hfei['wfid']}"
|
168
|
-
|
169
109
|
# TODO : for 2.1.13, remove the subid || sub_wfid trick
|
110
|
+
|
111
|
+
if hfei.respond_to?(:to_storage_id)
|
112
|
+
hfei.to_storage_id
|
113
|
+
else
|
114
|
+
"#{hfei['expid']}!#{hfei['subid'] || hfei['sub_wfid']}!#{hfei['wfid']}"
|
115
|
+
end
|
170
116
|
end
|
171
117
|
|
172
118
|
# Turns the result of to_storage_id back to a FlowExpressionId instance.
|
@@ -217,16 +163,6 @@ module Ruote
|
|
217
163
|
|
218
164
|
alias eql? ==
|
219
165
|
|
220
|
-
SUBS = %w[ subid sub_wfid ]
|
221
|
-
IDS = %w[ engine_id expid wfid ]
|
222
|
-
|
223
|
-
# Returns true if the h is a representation of a FlowExpressionId instance.
|
224
|
-
#
|
225
|
-
def self.is_a_fei?(h)
|
226
|
-
|
227
|
-
h.respond_to?(:keys) && (h.keys - SUBS).sort == IDS
|
228
|
-
end
|
229
|
-
|
230
166
|
# Returns child_id... For an expid of '0_1_4', this will be 4.
|
231
167
|
#
|
232
168
|
def self.child_id(h)
|
@@ -285,7 +221,8 @@ module Ruote
|
|
285
221
|
'engine_id' => ss[-4] || 'engine',
|
286
222
|
'expid' => ss[-3],
|
287
223
|
'subid' => ss[-2],
|
288
|
-
'wfid' => ss[-1]
|
224
|
+
'wfid' => ss[-1]
|
225
|
+
}
|
289
226
|
end
|
290
227
|
|
291
228
|
raise ArgumentError.new(
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -33,12 +33,14 @@ module Ruote
|
|
33
33
|
#
|
34
34
|
# NOTE:
|
35
35
|
#
|
36
|
-
#
|
36
|
+
# This default history is worthless when there are multiple workers.
|
37
37
|
# It only keeps track of the msgs processed by the worker in the same
|
38
38
|
# context. Msgs processed by other workers (in different Ruby runtimes) are
|
39
39
|
# not seen (they are tracked by the DefaultHistory next to those workers).
|
40
40
|
#
|
41
41
|
# By default, this history keeps track of the latest 1'000 msgs.
|
42
|
+
# This can be changed by passing a 'history_max_size' option to the storage
|
43
|
+
# when initializing ruote ('history_max_size' => 0) is acceptable.
|
42
44
|
#
|
43
45
|
class DefaultHistory
|
44
46
|
|
@@ -52,6 +54,8 @@ module Ruote
|
|
52
54
|
@context = context
|
53
55
|
@options = options
|
54
56
|
|
57
|
+
@max_size = context['history_max_size'] || DEFAULT_MAX_SIZE
|
58
|
+
|
55
59
|
@history = []
|
56
60
|
end
|
57
61
|
|
@@ -122,14 +126,24 @@ module Ruote
|
|
122
126
|
#
|
123
127
|
def on_msg(msg)
|
124
128
|
|
129
|
+
return if @max_size < 1
|
130
|
+
|
125
131
|
msg = Ruote.fulldup(msg)
|
126
132
|
msg['seen_at'] = Ruote.now_to_utc_s
|
127
133
|
|
128
134
|
@history << msg
|
129
135
|
|
130
|
-
while (@history.size >
|
136
|
+
while (@history.size > @max_size) do
|
131
137
|
@history.shift
|
132
138
|
end
|
139
|
+
|
140
|
+
rescue => e
|
141
|
+
|
142
|
+
$stderr.puts '>' + '-' * 79
|
143
|
+
$stderr.puts "#{self.class} issue, skipping"
|
144
|
+
$stderr.puts e.inspect
|
145
|
+
$stderr.puts e.backtrace[0, 2]
|
146
|
+
$stderr.puts '<' + '-' * 79
|
133
147
|
end
|
134
148
|
end
|
135
149
|
end
|
@@ -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
|
@@ -77,29 +77,6 @@ class Ruote::WaitLogger
|
|
77
77
|
].join('!')
|
78
78
|
end
|
79
79
|
|
80
|
-
def insp(o, opts={})
|
81
|
-
|
82
|
-
case o
|
83
|
-
when nil
|
84
|
-
'nil'
|
85
|
-
when Hash
|
86
|
-
trim = opts[:trim] || []
|
87
|
-
'{' +
|
88
|
-
o.reject { |k, v|
|
89
|
-
v.nil? && trim.include?(k.to_s)
|
90
|
-
}.collect { |k, v|
|
91
|
-
"#{k}: #{insp(v)}"
|
92
|
-
}.join(', ') +
|
93
|
-
'}'
|
94
|
-
when Array
|
95
|
-
'[' + o.collect { |e| insp(e) }.join(', ') + ']'
|
96
|
-
when String
|
97
|
-
o.match(/\s/) ? o.inspect : o
|
98
|
-
else
|
99
|
-
o.inspect
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
80
|
def radial_tree(msg)
|
104
81
|
|
105
82
|
_, t = Ruote::Exp::DefineExpression.reorganize(msg['tree'])
|
@@ -117,10 +94,13 @@ class Ruote::WaitLogger
|
|
117
94
|
|
118
95
|
@count = (@count + 1) % 10
|
119
96
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
97
|
+
wo = Ruote.current_worker
|
98
|
+
woi = [ wo.object_id.to_s[-2..-1] ]
|
99
|
+
if wo.class != Ruote::Worker || wo.name != 'worker'
|
100
|
+
woi << wo.class.name.split('::').last[0, 2]
|
101
|
+
woi << wo.name[0, 2]
|
102
|
+
end
|
103
|
+
woi = woi.join(':')
|
124
104
|
|
125
105
|
fei = msg['fei']
|
126
106
|
depth = fei ? fei['expid'].split('_').size : 0
|
@@ -210,7 +190,7 @@ class Ruote::WaitLogger
|
|
210
190
|
#
|
211
191
|
# display backtraces
|
212
192
|
|
213
|
-
rst = insp(
|
193
|
+
rst = Ruote.insp(
|
214
194
|
rest.reject { |k, v| %w[ error msg ].include?(k) },
|
215
195
|
:trim => %[ updated_tree ])[1..-2]
|
216
196
|
|
@@ -225,7 +205,7 @@ class Ruote::WaitLogger
|
|
225
205
|
|
226
206
|
color(
|
227
207
|
@color,
|
228
|
-
"#{@count} #{tm} #{
|
208
|
+
"#{@count} #{tm} #{woi} #{' ' * depth}#{act} * #{i} #{rst}",
|
229
209
|
true
|
230
210
|
) +
|
231
211
|
"\n" +
|
@@ -244,11 +224,11 @@ class Ruote::WaitLogger
|
|
244
224
|
''
|
245
225
|
end
|
246
226
|
|
247
|
-
rest = insp(rest, :trim => %[ updated_tree ])[1..-2]
|
227
|
+
rest = Ruote.insp(rest, :trim => %[ updated_tree ])[1..-2]
|
248
228
|
|
249
229
|
color(
|
250
230
|
@color,
|
251
|
-
"#{@count} #{tm} #{
|
231
|
+
"#{@count} #{tm} #{woi} #{' ' * depth}#{act} * #{i} #{pa}#{rest}",
|
252
232
|
true)
|
253
233
|
end
|
254
234
|
|
@@ -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
|
@@ -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
|
@@ -38,7 +38,7 @@ module Ruote
|
|
38
38
|
#
|
39
39
|
# The logic behind Ruote::Dashboard#wait_for is implemented here.
|
40
40
|
#
|
41
|
-
# This logger keeps track of the last
|
41
|
+
# This logger keeps track of the last 56 events. This number can
|
42
42
|
# be tweaked via the 'wait_logger_max' storage option
|
43
43
|
# (http://ruote.rubyforge.org/configuration.html)
|
44
44
|
#
|
@@ -51,7 +51,7 @@ module Ruote
|
|
51
51
|
# === options (storage initialization options)
|
52
52
|
#
|
53
53
|
# wait_logger_max(Integer)::
|
54
|
-
# defaults to
|
54
|
+
# defaults to 77, max number of recent records to keep track of
|
55
55
|
# wait_logger_timeout(Integer)::
|
56
56
|
# defaults to 60 (seconds), #wait_for times out after how many seconds?
|
57
57
|
#
|
@@ -84,7 +84,7 @@ module Ruote
|
|
84
84
|
@color = 33
|
85
85
|
@noisy = false
|
86
86
|
|
87
|
-
@log_max = context['wait_logger_max'] ||
|
87
|
+
@log_max = context['wait_logger_max'] || 77
|
88
88
|
@timeout = context['wait_logger_timeout'] || 60 # in seconds
|
89
89
|
|
90
90
|
@check_mutex = Mutex.new
|
@@ -178,8 +178,12 @@ module Ruote
|
|
178
178
|
while @waiting.any? and msg = @seen.shift
|
179
179
|
|
180
180
|
@waiting.delete_if do |thread, interests|
|
181
|
-
|
182
|
-
|
181
|
+
if matches(interests, msg)
|
182
|
+
thread['__result__'] = msg
|
183
|
+
true
|
184
|
+
else
|
185
|
+
false
|
186
|
+
end
|
183
187
|
end
|
184
188
|
end
|
185
189
|
end
|
@@ -267,7 +271,11 @@ module Ruote
|
|
267
271
|
interests.delete(interest) if satisfied
|
268
272
|
end
|
269
273
|
|
270
|
-
|
274
|
+
if interests.include?(:or_error)
|
275
|
+
(interests.size < 2)
|
276
|
+
else
|
277
|
+
(interests.size < 1)
|
278
|
+
end
|
271
279
|
end
|
272
280
|
end
|
273
281
|
end
|
data/lib/ruote/merge.rb
ADDED
@@ -0,0 +1,123 @@
|
|
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
|
+
# Gathering methods for merging workitems.
|
30
|
+
#++
|
31
|
+
|
32
|
+
# Merge workitem 'source' into workitem 'target'.
|
33
|
+
#
|
34
|
+
# If type is 'override', the source will prevail and be returned.
|
35
|
+
#
|
36
|
+
# If type is 'mix', the source fields will be merged into the target fields.
|
37
|
+
#
|
38
|
+
# If type is 'isolate', the source fields will be placed in a separte field
|
39
|
+
# in the target workitem. The name of this field is the child_id of the
|
40
|
+
# source workitem (a string from '0' to '99999' and beyond)
|
41
|
+
#
|
42
|
+
# The 'concat' type merges hashes and concats arrays. The 'union' type
|
43
|
+
# behaves much like 'concat', but it makes sure to remove duplicates.
|
44
|
+
#
|
45
|
+
# Warning: 'union' will remove duplicates that were present _before_ the
|
46
|
+
# merge.
|
47
|
+
#
|
48
|
+
def self.merge_workitem(index, target, source, merge_type)
|
49
|
+
|
50
|
+
if merge_type == 'override'
|
51
|
+
|
52
|
+
return source
|
53
|
+
end
|
54
|
+
|
55
|
+
if target == nil
|
56
|
+
|
57
|
+
case merge_type
|
58
|
+
|
59
|
+
when 'stack'
|
60
|
+
|
61
|
+
source['fields'] = { 'stack' => [ source['fields'] ] }
|
62
|
+
|
63
|
+
when 'isolate'
|
64
|
+
|
65
|
+
source['fields'] = { (index || 0).to_s => source['fields'] }
|
66
|
+
|
67
|
+
#when 'mix'
|
68
|
+
# do nothing
|
69
|
+
#when 'union', 'concat'
|
70
|
+
# do nothing
|
71
|
+
end
|
72
|
+
|
73
|
+
return source
|
74
|
+
end
|
75
|
+
|
76
|
+
# else, regular merge
|
77
|
+
|
78
|
+
case merge_type
|
79
|
+
|
80
|
+
when 'mix'
|
81
|
+
|
82
|
+
target['fields'].merge!(source['fields'])
|
83
|
+
|
84
|
+
when 'stack'
|
85
|
+
|
86
|
+
target['fields']['stack'] << source['fields']
|
87
|
+
|
88
|
+
when 'isolate'
|
89
|
+
|
90
|
+
index ||= target['fields'].keys.select { |k| k.match(/^\d+$/) }.size
|
91
|
+
target['fields'][index.to_s] = source['fields']
|
92
|
+
|
93
|
+
when 'union', 'concat', 'deep'
|
94
|
+
|
95
|
+
source['fields'].each do |k, sv|
|
96
|
+
|
97
|
+
tv = target['fields'][k]
|
98
|
+
|
99
|
+
if sv.is_a?(Array) and tv.is_a?(Array)
|
100
|
+
tv.concat(sv)
|
101
|
+
tv.uniq! if merge_type == 'union'
|
102
|
+
elsif sv.is_a?(Hash) and tv.is_a?(Hash)
|
103
|
+
merge_type == 'deep' ? deep_merge!(tv, sv) : tv.merge!(sv)
|
104
|
+
else
|
105
|
+
target['fields'][k] = sv
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
target
|
111
|
+
end
|
112
|
+
|
113
|
+
# Inspired by the one found in ActiveSupport, though not strictly
|
114
|
+
# equivalent.
|
115
|
+
#
|
116
|
+
def self.deep_merge!(target, source)
|
117
|
+
|
118
|
+
target.merge!(source) do |k, o, n|
|
119
|
+
o.is_a?(Hash) && n.is_a?(Hash) ? deep_merge!(o, n) : n
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|