flor 0.17.0 → 0.18.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -0
- data/README.md +19 -1
- data/flor.gemspec +7 -7
- data/lib/flor.rb +1 -1
- data/lib/flor/core/executor.rb +5 -6
- data/lib/flor/core/procedure.rb +47 -11
- data/lib/flor/flor.rb +1 -3
- data/lib/flor/log.rb +6 -0
- data/lib/flor/pcore/_dmute.rb +4 -1
- data/lib/flor/pcore/break.rb +12 -1
- data/lib/flor/pcore/on.rb +30 -6
- data/lib/flor/pcore/sequence.rb +48 -0
- data/lib/flor/punit/cancel.rb +9 -1
- data/lib/flor/punit/signal.rb +29 -5
- data/lib/flor/punit/task.rb +8 -2
- data/lib/flor/punit/trap.rb +31 -0
- data/lib/flor/unit/executor.rb +9 -5
- data/lib/flor/unit/ganger.rb +21 -6
- data/lib/flor/unit/models.rb +40 -9
- data/lib/flor/unit/models/trap.rb +12 -4
- data/lib/flor/unit/scheduler.rb +186 -51
- data/lib/flor/unit/storage.rb +20 -5
- data/lib/flor/unit/taskers.rb +46 -1
- metadata +13 -37
data/lib/flor/punit/task.rb
CHANGED
@@ -68,6 +68,9 @@ class Flor::Pro::Task < Flor::Procedure
|
|
68
68
|
# clean_up assign: 'alan'
|
69
69
|
# "clean up" assign: 'alan'
|
70
70
|
|
71
|
+
@node['message'] = message
|
72
|
+
# keep copy for Executor#return integrity enforcement
|
73
|
+
|
71
74
|
nis = atts(nil)
|
72
75
|
ta = att('by', 'for', 'assign')
|
73
76
|
tn = att('with', 'task')
|
@@ -79,8 +82,7 @@ class Flor::Pro::Task < Flor::Procedure
|
|
79
82
|
|
80
83
|
attl, attd = determine_atts
|
81
84
|
|
82
|
-
@node['task'] =
|
83
|
-
{ 'tasker' => tasker, 'name' => taskname }
|
85
|
+
@node['task'] = { 'tasker' => tasker, 'name' => taskname }
|
84
86
|
|
85
87
|
wrap(
|
86
88
|
'point' => 'task',
|
@@ -96,6 +98,9 @@ class Flor::Pro::Task < Flor::Procedure
|
|
96
98
|
|
97
99
|
close_node
|
98
100
|
|
101
|
+
@node['message'] = message
|
102
|
+
# keep copy for Executor#return integrity enforcement
|
103
|
+
|
99
104
|
attl, attd = determine_atts
|
100
105
|
|
101
106
|
wrap(
|
@@ -103,6 +108,7 @@ class Flor::Pro::Task < Flor::Procedure
|
|
103
108
|
'exid' => exid, 'nid' => nid,
|
104
109
|
'tags' => list_tags,
|
105
110
|
'tasker' => att(nil),
|
111
|
+
'taskname' => @node['task']['name'],
|
106
112
|
'attl' => attl, 'attd' => attd,
|
107
113
|
'payload' => determine_payload)
|
108
114
|
end
|
data/lib/flor/punit/trap.rb
CHANGED
@@ -178,6 +178,37 @@ class Flor::Pro::Trap < Flor::Procedure
|
|
178
178
|
# twice.
|
179
179
|
#
|
180
180
|
#
|
181
|
+
# ## the payload: directive
|
182
|
+
#
|
183
|
+
# The `payload:` attribute tells the trap what payload to hand to the
|
184
|
+
# trap handler.
|
185
|
+
#
|
186
|
+
# When `payload: 'event'`, the handler is given a copy of the fields as
|
187
|
+
# seen in the event that triggered the trap.
|
188
|
+
#
|
189
|
+
# When `payload: 'trap'`, the handler is given a copy of the fields as
|
190
|
+
# they were when and where the trap was set.
|
191
|
+
#
|
192
|
+
# When `payload: { a: 'A' }`, the given payload is used (here
|
193
|
+
# `{ 'a' => 'A' }`).
|
194
|
+
#
|
195
|
+
#
|
196
|
+
# ## blocking trap
|
197
|
+
#
|
198
|
+
# One can use a trap without a function. It will block the execution branch
|
199
|
+
# until the expect event, signal, etc occurs
|
200
|
+
# ```
|
201
|
+
# sequence
|
202
|
+
# technical_writer 'prepare documentation'
|
203
|
+
# team_manager 'submit documentation to review team'
|
204
|
+
# trap signal: 'green' # go on after the 'green' signal...
|
205
|
+
# team_manager 'deploy documentation'
|
206
|
+
# ```
|
207
|
+
# Here the execution will block after the team manager submits the doc
|
208
|
+
# for review. When the 'green' signal comes, the flow resumes to 'deploy
|
209
|
+
# documentation'.
|
210
|
+
#
|
211
|
+
#
|
181
212
|
# ## see also
|
182
213
|
#
|
183
214
|
# On and signal.
|
data/lib/flor/unit/executor.rb
CHANGED
@@ -153,11 +153,15 @@ module Flor
|
|
153
153
|
|
154
154
|
def return(message)
|
155
155
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
156
|
+
n = @execution['nodes'][message['nid']] || {}
|
157
|
+
m = n['message'] || {}
|
158
|
+
c = m['cause']
|
159
|
+
|
160
|
+
rm = message.dup
|
161
|
+
rm['point'] = 'receive'
|
162
|
+
rm['cause'] = c if c # preserve 'cause' for routing
|
163
|
+
|
164
|
+
[ rm ]
|
161
165
|
end
|
162
166
|
|
163
167
|
def schedule(message)
|
data/lib/flor/unit/ganger.rb
CHANGED
@@ -22,6 +22,12 @@ module Flor
|
|
22
22
|
def shutdown
|
23
23
|
end
|
24
24
|
|
25
|
+
# Used by flor when it looks up for a variable and finds nothing.
|
26
|
+
# The last step is to ask the ganger if it knows about a tasker under
|
27
|
+
# the given (domain and) name.
|
28
|
+
#
|
29
|
+
# If it returns true, flor knows there is a tasker under that name.
|
30
|
+
#
|
25
31
|
def has_tasker?(exid, name)
|
26
32
|
|
27
33
|
#return false if RESERVED_NAMES.include?(name)
|
@@ -34,6 +40,9 @@ module Flor
|
|
34
40
|
@unit.loader.tasker(d, name))
|
35
41
|
end
|
36
42
|
|
43
|
+
# Called by Flor::Scheduler. The ganger then has to hand the task
|
44
|
+
# (the message) to the proper tasker.
|
45
|
+
#
|
37
46
|
def task(executor, message)
|
38
47
|
|
39
48
|
domain = message['exid'].split('-', 2).first
|
@@ -50,12 +59,11 @@ module Flor
|
|
50
59
|
) unless tconf
|
51
60
|
|
52
61
|
if tconf.is_a?(Array)
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
if message['point'] == 'detask'
|
62
|
+
|
63
|
+
points = [ nil, message['point'] ]
|
64
|
+
points << 'detask' if points.include?('cancel')
|
65
|
+
|
66
|
+
tconf = tconf.find { |h| points.include?(h['point']) }
|
59
67
|
end
|
60
68
|
|
61
69
|
message['tconf'] = tconf unless tconf['include_tconf'] == false
|
@@ -74,6 +82,9 @@ module Flor
|
|
74
82
|
# especially if it's a domain tasker
|
75
83
|
end
|
76
84
|
|
85
|
+
# Called by the tasker implementations when they're done with a task
|
86
|
+
# and want to hand it back to flor. It might be a failure message.
|
87
|
+
#
|
77
88
|
def return(message)
|
78
89
|
|
79
90
|
@unit.return(message)
|
@@ -120,6 +131,10 @@ module Flor
|
|
120
131
|
}.compact
|
121
132
|
end
|
122
133
|
|
134
|
+
# By default, taskers don't see the flor variables in the execution.
|
135
|
+
# If 'include_vars' or 'exclude_vars' is present in the configuration
|
136
|
+
# of the tasker, some or all of the variables are passed.
|
137
|
+
#
|
123
138
|
def gather_vars(executor, tconf, message)
|
124
139
|
|
125
140
|
# try to return before a potentially costly call to executor.vars(nid)
|
data/lib/flor/unit/models.rb
CHANGED
@@ -42,9 +42,9 @@ module Flor
|
|
42
42
|
|
43
43
|
exid = @values[:exid]; return nil unless exid
|
44
44
|
|
45
|
-
@
|
45
|
+
@flor_model_cache_execution = nil if reload
|
46
46
|
|
47
|
-
@
|
47
|
+
@flor_model_cache_execution ||= unit.executions[exid: exid]
|
48
48
|
end
|
49
49
|
|
50
50
|
# Returns the node hash linked to this model
|
@@ -64,17 +64,20 @@ module Flor
|
|
64
64
|
nod ? nod['payload'] : nil
|
65
65
|
end
|
66
66
|
|
67
|
-
def
|
68
|
-
|
69
|
-
d = Flor::Storage.from_blob(content)
|
70
|
-
d['id'] = id if d.is_a?(Hash)
|
67
|
+
def data(cache=true)
|
71
68
|
|
72
|
-
|
69
|
+
cache ? (@flor_model_cache_data = _data) : _data
|
73
70
|
end
|
74
71
|
|
75
|
-
def
|
72
|
+
def refresh
|
76
73
|
|
77
|
-
|
74
|
+
instance_variables
|
75
|
+
.each do |k|
|
76
|
+
instance_variable_set(k, nil) \
|
77
|
+
if k.to_s.start_with?('@flor_model_cache_')
|
78
|
+
end
|
79
|
+
|
80
|
+
super
|
78
81
|
end
|
79
82
|
|
80
83
|
def to_h
|
@@ -88,6 +91,34 @@ module Flor
|
|
88
91
|
h
|
89
92
|
end
|
90
93
|
end
|
94
|
+
|
95
|
+
class << self
|
96
|
+
|
97
|
+
def from_h(h)
|
98
|
+
|
99
|
+
cols = columns
|
100
|
+
|
101
|
+
h
|
102
|
+
.inject({}) { |r, (k, v)|
|
103
|
+
k = k.to_sym
|
104
|
+
if k == :data
|
105
|
+
r[:content] = Flor.to_blob(v)
|
106
|
+
elsif cols.include?(k)
|
107
|
+
r[k] = v
|
108
|
+
end
|
109
|
+
r }
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
protected
|
114
|
+
|
115
|
+
def _data
|
116
|
+
|
117
|
+
d = Flor::Storage.from_blob(content)
|
118
|
+
d['id'] = id if d.is_a?(Hash)
|
119
|
+
|
120
|
+
d
|
121
|
+
end
|
91
122
|
end
|
92
123
|
|
93
124
|
MODELS = [ :executions, :timers, :traces, :traps, :pointers, :messages ]
|
@@ -88,9 +88,11 @@ module Flor
|
|
88
88
|
|
89
89
|
c = c - 1
|
90
90
|
data['count'] = c
|
91
|
-
self[:status] = c > 0 ? 'active' : 'consumed'
|
91
|
+
self[:status] = s = (c > 0) ? 'active' : 'consumed'
|
92
92
|
|
93
|
-
self.update(
|
93
|
+
self.update(
|
94
|
+
content: Flor::Storage.to_blob(@flor_model_cache_data),
|
95
|
+
status: s)
|
94
96
|
|
95
97
|
c < 1
|
96
98
|
end
|
@@ -107,9 +109,15 @@ module Flor
|
|
107
109
|
if sig = (message['point'] == 'signal' && message['name'])
|
108
110
|
args << [ 'sig', sig ]
|
109
111
|
end
|
110
|
-
|
112
|
+
|
113
|
+
case pl = dat['pl']
|
114
|
+
when 'event'
|
111
115
|
args << [ 'payload', msg['payload'] ]
|
112
|
-
msg['payload'] = Flor.dup(message['payload'])
|
116
|
+
msg['payload'] = Flor.dup(message['payload'])
|
117
|
+
#when 'trap'
|
118
|
+
when Hash
|
119
|
+
msg['payload'] = Flor.dup(pl)
|
120
|
+
#else
|
113
121
|
end
|
114
122
|
|
115
123
|
{ 'point' => 'trigger',
|
data/lib/flor/unit/scheduler.rb
CHANGED
@@ -238,7 +238,7 @@ module Flor
|
|
238
238
|
message
|
239
239
|
else
|
240
240
|
message
|
241
|
-
.select { |k, _| %w[ exid nid payload tasker ].include?(k) }
|
241
|
+
.select { |k, _| %w[ exid nid payload tasker cause ].include?(k) }
|
242
242
|
.merge!('point' => 'return')
|
243
243
|
end
|
244
244
|
|
@@ -249,12 +249,21 @@ module Flor
|
|
249
249
|
|
250
250
|
def cancel(exid, *as)
|
251
251
|
|
252
|
-
|
252
|
+
msg, opts = prepare_message('cancel', [ exid, *as ])
|
253
|
+
msg['nid'] ||= '0'
|
254
|
+
|
255
|
+
queue(msg, opts)
|
253
256
|
end
|
254
257
|
|
255
258
|
def kill(exid, *as)
|
256
259
|
|
257
|
-
|
260
|
+
msg, opts = prepare_message('kill', [ exid, *as ])
|
261
|
+
|
262
|
+
msg['point'] = 'cancel'
|
263
|
+
msg['flavour'] = 'kill'
|
264
|
+
msg['nid'] ||= '0'
|
265
|
+
|
266
|
+
queue(msg, opts)
|
258
267
|
end
|
259
268
|
|
260
269
|
def signal(name, h={})
|
@@ -262,21 +271,38 @@ module Flor
|
|
262
271
|
h[:payload] ||= {}
|
263
272
|
h[:name] ||= name
|
264
273
|
|
265
|
-
|
274
|
+
msg, opts = prepare_message('signal', [ h ])
|
275
|
+
|
276
|
+
fail ArgumentError.new('missing :name string key') \
|
277
|
+
unless msg['name'].is_a?(String)
|
278
|
+
|
279
|
+
queue(msg, opts)
|
266
280
|
end
|
267
281
|
|
268
282
|
def re_apply(exid, *as)
|
269
283
|
|
270
|
-
|
284
|
+
msg, opts = prepare_message('cancel', [ exid, *as ])
|
285
|
+
|
286
|
+
msg['on_receive_last'] = prepare_re_apply_messages(msg, opts)
|
287
|
+
|
288
|
+
queue(msg, opts)
|
271
289
|
end
|
272
290
|
alias reapply re_apply
|
273
291
|
|
274
292
|
def add_branches(exid, *as)
|
275
293
|
|
276
|
-
|
294
|
+
msg, opts = prepare_message('add-branches', [ exid, *as ])
|
295
|
+
|
296
|
+
msg['point'] = 'add'
|
297
|
+
msg['trees'] = prepare_trees(opts)
|
277
298
|
|
278
|
-
|
279
|
-
|
299
|
+
msg['tnid'] = tnid =
|
300
|
+
opts.delete(:tnid) || msg.delete('nid')
|
301
|
+
msg['nid'] =
|
302
|
+
msg.delete('nid') || opts.delete(:pnid) || Flor.parent_nid(tnid)
|
303
|
+
|
304
|
+
exe = @storage.executions[exid: msg['exid']]
|
305
|
+
pnid = msg['nid']
|
280
306
|
ptree = exe.lookup_tree(pnid)
|
281
307
|
|
282
308
|
fail ArgumentError.new(
|
@@ -286,7 +312,7 @@ module Flor
|
|
286
312
|
# not likely to happen, since leaves reply immediately
|
287
313
|
|
288
314
|
size = ptree[1].size
|
289
|
-
tnid = (
|
315
|
+
tnid = (msg['tnid'] ||= Flor.make_child_nid(pnid, size))
|
290
316
|
|
291
317
|
cid = Flor.child_id(tnid)
|
292
318
|
|
@@ -306,19 +332,23 @@ module Flor
|
|
306
332
|
"node #{pnid} has #{size} branch#{size == 1 ? '' : 'es'}"
|
307
333
|
) if cid > size
|
308
334
|
|
309
|
-
queue(
|
335
|
+
queue(msg, opts)
|
310
336
|
end
|
311
337
|
alias add_branch add_branches
|
312
338
|
|
313
339
|
def add_iterations(exid, *as)
|
314
340
|
|
315
|
-
|
341
|
+
msg, opts = prepare_message('add-iterations', [ exid, *as ])
|
342
|
+
|
343
|
+
msg['point'] = 'add'
|
344
|
+
msg['elements'] = prepare_elements(opts)
|
345
|
+
msg['nid'] = msg.delete('nid') || opts.delete(:pnid)
|
316
346
|
|
317
|
-
exe = @storage.executions[exid:
|
318
|
-
nid =
|
347
|
+
exe = @storage.executions[exid: msg['exid']]
|
348
|
+
nid = msg['nid']
|
319
349
|
|
320
350
|
fail ArgumentError.new(
|
321
|
-
"cannot add iteration to missing execution #{
|
351
|
+
"cannot add iteration to missing execution #{msg['exid'].inspect}"
|
322
352
|
) unless exe
|
323
353
|
|
324
354
|
fail ArgumentError.new(
|
@@ -329,7 +359,7 @@ module Flor
|
|
329
359
|
"cannot add iteration to missing node #{nid.inspect}"
|
330
360
|
) unless exe.lookup_tree(nid)
|
331
361
|
|
332
|
-
queue(
|
362
|
+
queue(msg, opts)
|
333
363
|
end
|
334
364
|
alias add_iteration add_iterations
|
335
365
|
|
@@ -387,6 +417,136 @@ module Flor
|
|
387
417
|
ex ? ex.execution : nil
|
388
418
|
end
|
389
419
|
|
420
|
+
DUMP_KEYS = %w[ timestamp executions timers traps pointers ]
|
421
|
+
|
422
|
+
# Dumps all or some of the executions to a JSON string.
|
423
|
+
# See Scheduler#load for importing.
|
424
|
+
#
|
425
|
+
# unit.dump -> string # returns a JSON string of all executions
|
426
|
+
# unit.dump(io) -> io # dumps the JSON to the given IO instance
|
427
|
+
#
|
428
|
+
# unit.dump(exid: i) # dumps only the given execution
|
429
|
+
# unit.dump(exids: [ i0, i1 ]) # dumps only the givens executions
|
430
|
+
# unit.dump(domain: d) # dumps exes from domains,
|
431
|
+
# unit.dump(domains: [ d0, d1 ]) # and their subdomains
|
432
|
+
# unit.dump(sdomain: d) # dumps strictly from given domains,
|
433
|
+
# unit.dump(sdomains: [ d0, d1 ]) # doesn't look at subdomains
|
434
|
+
#
|
435
|
+
# unit.dump() { |h| ... } # modify the has right before it's turned to JSON
|
436
|
+
#
|
437
|
+
def dump(io=nil, opts=nil, &block)
|
438
|
+
|
439
|
+
io, opts = nil, io if io.is_a?(Hash)
|
440
|
+
opts ||= {}
|
441
|
+
|
442
|
+
o = lambda { |k| v = opts[k] || opts["#{k}s".to_sym]; v ? Array(v) : nil }
|
443
|
+
#
|
444
|
+
exis = o[:exid]
|
445
|
+
doms = o[:domain]
|
446
|
+
sdms = o[:strict_domain] || o[:sdomain]
|
447
|
+
#
|
448
|
+
filter = lambda { |q|
|
449
|
+
q = q.where(
|
450
|
+
exid: exis) if exis
|
451
|
+
q = q.where {
|
452
|
+
Sequel.|(*doms
|
453
|
+
.inject([]) { |a, d|
|
454
|
+
a.concat([
|
455
|
+
{ domain: d },
|
456
|
+
Sequel.like(:domain, d + '.%') ]) }) } if doms
|
457
|
+
q = q.where(
|
458
|
+
domain: sdms) if sdms
|
459
|
+
q }
|
460
|
+
|
461
|
+
exs, tms, tps, pts =
|
462
|
+
storage.db.transaction {
|
463
|
+
[ filter[executions].collect(&:to_h),
|
464
|
+
filter[timers].collect(&:to_h),
|
465
|
+
filter[traps].collect(&:to_h),
|
466
|
+
filter[pointers].collect(&:to_h) ] }
|
467
|
+
|
468
|
+
o = io ? io : StringIO.new
|
469
|
+
|
470
|
+
h = {
|
471
|
+
timestamp: Flor.tstamp,
|
472
|
+
executions: exs,
|
473
|
+
timers: tms,
|
474
|
+
traps: tps,
|
475
|
+
pointers: pts }
|
476
|
+
|
477
|
+
block.call(h) if block
|
478
|
+
|
479
|
+
JSON.dump(h, o)
|
480
|
+
|
481
|
+
io ? io : o.string
|
482
|
+
end
|
483
|
+
|
484
|
+
# Read a previous JSON dump and loads it into the storage.
|
485
|
+
# Can be useful when testing, dumping once and reloading multiple times
|
486
|
+
# to test variants.
|
487
|
+
#
|
488
|
+
# load(string) -> h # load all executions from given JSON string
|
489
|
+
# # returns object inserted stat hash
|
490
|
+
# load(io) # load all executions from the given IO
|
491
|
+
# load(io, close: true) # load from the given IO and close it after read
|
492
|
+
#
|
493
|
+
# load(x, exid: i) # load only given executions,
|
494
|
+
# load(x, exids: [ i0, i1 ]) # ignore the rest of the data in the source
|
495
|
+
# load(x, domain: d) # load only exes from given domains,
|
496
|
+
# load(x, domains: [ d0, d1 ]) # and their subdomains
|
497
|
+
# load(x, sdomain: d) # load only exes from strict domains,
|
498
|
+
# load(x, sdomains: [ d0, d1 ]) # ignores exes in their subdomains
|
499
|
+
#
|
500
|
+
def load(string_or_io, opts={}, &block)
|
501
|
+
|
502
|
+
s = string_or_io
|
503
|
+
s = s.read if s.respond_to?(:read)
|
504
|
+
string_or_io.close if string_or_io.respond_to?(:close) && opts[:close]
|
505
|
+
h = JSON.load(s)
|
506
|
+
|
507
|
+
mks = DUMP_KEYS - h.keys
|
508
|
+
fail Flor::FlorError.new("missing keys #{mks.inspect}") if mks.any?
|
509
|
+
|
510
|
+
o = lambda { |k| v = opts[k] || opts["#{k}s".to_sym]; v ? Array(v) : nil }
|
511
|
+
#
|
512
|
+
exis = o[:exid]
|
513
|
+
doms = o[:domain]
|
514
|
+
sdms = o[:strict_domain] || o[:sdomain]
|
515
|
+
#
|
516
|
+
doms = doms.collect { |d| /\A#{d}(\.#{Flor::NAME_REX})*\z/ } if doms
|
517
|
+
|
518
|
+
counts = { executions: 0, timers: 0, traps: 0, pointers: 0, total: 0 }
|
519
|
+
|
520
|
+
storage.db.transaction do
|
521
|
+
|
522
|
+
(DUMP_KEYS - %w[ timestamp ]).each do |k|
|
523
|
+
|
524
|
+
y = k.to_sym
|
525
|
+
cla = storage.send(k)
|
526
|
+
cols = cla.columns
|
527
|
+
|
528
|
+
rows = h[k]
|
529
|
+
.inject([]) { |a, hh|
|
530
|
+
|
531
|
+
next a if exis && ! exis.include?(hh['exid'])
|
532
|
+
next a if doms && ! doms.find { |d| d.match(hh['domain']) }
|
533
|
+
next a if sdms && ! sdms.include?(hh['domain'])
|
534
|
+
|
535
|
+
counts[y] += 1
|
536
|
+
counts[:total] += 1
|
537
|
+
|
538
|
+
vals = cla.from_h(hh)
|
539
|
+
a << cols.collect { |c| vals[c] } }
|
540
|
+
|
541
|
+
cla.import(cols, rows) if rows.any?
|
542
|
+
end
|
543
|
+
|
544
|
+
block.call(h) if block
|
545
|
+
end
|
546
|
+
|
547
|
+
counts
|
548
|
+
end
|
549
|
+
|
390
550
|
protected
|
391
551
|
|
392
552
|
def tick
|
@@ -412,8 +572,11 @@ module Flor
|
|
412
572
|
notify(nil, make_idle_message)
|
413
573
|
end
|
414
574
|
|
415
|
-
|
416
|
-
|
575
|
+
if @idle_count < 1
|
576
|
+
sleep 0.001
|
577
|
+
else
|
578
|
+
sleep([ @heart_rate - (Time.now - t0), 0.001 ].max)
|
579
|
+
end
|
417
580
|
|
418
581
|
rescue Exception => ex
|
419
582
|
|
@@ -422,8 +585,8 @@ module Flor
|
|
422
585
|
|
423
586
|
def prepare_message(point, args)
|
424
587
|
|
425
|
-
h =
|
426
|
-
|
588
|
+
h = args
|
589
|
+
.inject({}) { |hh, a|
|
427
590
|
if a.is_a?(Hash) then a.each { |k, v| hh[k.to_s] = v }
|
428
591
|
elsif ! hh.has_key?('exid') then hh['exid'] = a
|
429
592
|
elsif ! hh.has_key?('nid') then hh['nid'] = a
|
@@ -441,37 +604,8 @@ module Flor
|
|
441
604
|
end
|
442
605
|
end
|
443
606
|
|
444
|
-
if point == 'kill'
|
445
|
-
|
446
|
-
msg['point'] = 'cancel'
|
447
|
-
msg['flavour'] = 'kill'
|
448
|
-
|
449
|
-
elsif point == 'add-branches'
|
450
|
-
|
451
|
-
msg['point'] = 'add'
|
452
|
-
msg['trees'] = prepare_trees(opts)
|
453
|
-
|
454
|
-
msg['tnid'] = tnid =
|
455
|
-
opts.delete(:tnid) || msg.delete('nid')
|
456
|
-
msg['nid'] =
|
457
|
-
msg.delete('nid') || opts.delete(:pnid) || Flor.parent_nid(tnid)
|
458
|
-
|
459
|
-
elsif point == 'add-iterations'
|
460
|
-
|
461
|
-
msg['point'] = 'add'
|
462
|
-
msg['elements'] = prepare_elements(opts)
|
463
|
-
msg['nid'] = msg.delete('nid') || opts.delete(:pnid)
|
464
|
-
end
|
465
|
-
|
466
|
-
if opts[:re_apply]
|
467
|
-
|
468
|
-
msg['on_receive_last'] = prepare_re_apply_messages(msg, opts)
|
469
|
-
end
|
470
|
-
|
471
607
|
fail ArgumentError.new('missing :exid key') \
|
472
608
|
unless msg['exid'].is_a?(String)
|
473
|
-
fail ArgumentError.new('missing :name string key') \
|
474
|
-
if point == 'signal' && ! msg['name'].is_a?(String)
|
475
609
|
|
476
610
|
[ msg, opts ]
|
477
611
|
end
|
@@ -503,8 +637,9 @@ module Flor
|
|
503
637
|
|
504
638
|
def prepare_re_apply_messages(msg, opts)
|
505
639
|
|
506
|
-
|
507
|
-
|
640
|
+
pl = msg['payload']
|
641
|
+
|
642
|
+
fail ArgumentError.new("missing 'payload' to re_apply") unless pl
|
508
643
|
|
509
644
|
t = Flor.parse(opts[:tree], Flor.caller_fname, {})
|
510
645
|
|
@@ -512,7 +647,7 @@ module Flor
|
|
512
647
|
'exid' => msg['exid'], 'nid' => msg['nid'],
|
513
648
|
'from' => 'parent',
|
514
649
|
'tree' => t,
|
515
|
-
'payload' =>
|
650
|
+
'payload' => pl } ]
|
516
651
|
end
|
517
652
|
|
518
653
|
def make_idle_message
|