flor 0.15.0 → 0.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +14 -1
- data/CREDITS.md +1 -0
- data/LICENSE.txt +1 -1
- data/Makefile +6 -2
- data/README.md +2 -1
- data/flor.gemspec +12 -2
- data/lib/flor.rb +3 -3
- data/lib/flor/colours.rb +1 -1
- data/lib/flor/conf.rb +3 -4
- data/lib/flor/core/executor.rb +31 -61
- data/lib/flor/core/node.rb +213 -96
- data/lib/flor/core/procedure.rb +194 -75
- data/lib/flor/core/texecutor.rb +6 -7
- data/lib/flor/djan.rb +41 -22
- data/lib/flor/flor.rb +137 -42
- data/lib/flor/id.rb +77 -59
- data/lib/flor/log.rb +43 -22
- data/lib/flor/migrations/0001_tables.rb +7 -7
- data/lib/flor/parser.rb +271 -74
- data/lib/flor/pcore/_apply.rb +108 -0
- data/lib/flor/pcore/_atom.rb +2 -4
- data/lib/flor/pcore/_att.rb +54 -37
- data/lib/flor/pcore/_dmute.rb +18 -0
- data/lib/flor/pcore/_dol.rb +17 -0
- data/lib/flor/pcore/_dqs.rb +35 -0
- data/lib/flor/pcore/_head.rb +25 -0
- data/lib/flor/pcore/_obj.rb +1 -3
- data/lib/flor/pcore/_pat_guard.rb +1 -1
- data/lib/flor/pcore/_pat_obj.rb +11 -3
- data/lib/flor/pcore/_pat_regex.rb +16 -2
- data/lib/flor/pcore/_ref.rb +51 -0
- data/lib/flor/pcore/_rxs.rb +27 -0
- data/lib/flor/pcore/_val.rb +11 -6
- data/lib/flor/pcore/{logo.rb → andor.rb} +4 -6
- data/lib/flor/pcore/apply.rb +72 -2
- data/lib/flor/pcore/arith.rb +16 -4
- data/lib/flor/pcore/array_qmark.rb +100 -0
- data/lib/flor/pcore/break.rb +1 -2
- data/lib/flor/pcore/case.rb +1 -1
- data/lib/flor/pcore/cmp.rb +3 -2
- data/lib/flor/pcore/collect.rb +2 -2
- data/lib/flor/pcore/cond.rb +19 -1
- data/lib/flor/pcore/cursor.rb +12 -11
- data/lib/flor/pcore/define.rb +30 -4
- data/lib/flor/pcore/do_return.rb +3 -0
- data/lib/flor/pcore/flatten.rb +39 -0
- data/lib/flor/pcore/if.rb +15 -5
- data/lib/flor/pcore/includes.rb +5 -2
- data/lib/flor/pcore/inject.rb +1 -1
- data/lib/flor/pcore/iterator.rb +28 -18
- data/lib/flor/pcore/keys.rb +2 -2
- data/lib/flor/pcore/map.rb +19 -1
- data/lib/flor/pcore/match.rb +2 -2
- data/lib/flor/pcore/matchr.rb +18 -5
- data/lib/flor/pcore/max.rb +51 -0
- data/lib/flor/pcore/merge.rb +134 -0
- data/lib/flor/pcore/move.rb +1 -1
- data/lib/flor/pcore/noret.rb +1 -1
- data/lib/flor/pcore/not.rb +15 -1
- data/lib/flor/pcore/on.rb +11 -0
- data/lib/flor/pcore/on_cancel.rb +5 -1
- data/lib/flor/pcore/on_error.rb +69 -4
- data/lib/flor/pcore/push.rb +4 -9
- data/lib/flor/pcore/range.rb +5 -5
- data/lib/flor/pcore/reduce.rb +5 -18
- data/lib/flor/pcore/return.rb +26 -0
- data/lib/flor/pcore/reverse.rb +4 -0
- data/lib/flor/pcore/sequence.rb +8 -1
- data/lib/flor/pcore/set.rb +74 -15
- data/lib/flor/pcore/shuffle.rb +71 -0
- data/lib/flor/pcore/slice.rb +137 -0
- data/lib/flor/pcore/sort.rb +244 -0
- data/lib/flor/pcore/sort_by.rb +67 -0
- data/lib/flor/pcore/split.rb +39 -0
- data/lib/flor/pcore/stall.rb +1 -1
- data/lib/flor/pcore/strings.rb +123 -0
- data/lib/flor/pcore/timestamp.rb +34 -0
- data/lib/flor/pcore/to_array.rb +2 -3
- data/lib/flor/pcore/twig.rb +1 -1
- data/lib/flor/pcore/type_of.rb +37 -0
- data/lib/flor/pcore/until.rb +3 -3
- data/lib/flor/punit/cancel.rb +3 -3
- data/lib/flor/punit/ccollect.rb +29 -0
- data/lib/flor/punit/cmap.rb +76 -20
- data/lib/flor/punit/concurrence.rb +440 -33
- data/lib/flor/punit/cron.rb +1 -1
- data/lib/flor/punit/every.rb +1 -1
- data/lib/flor/punit/graft.rb +2 -3
- data/lib/flor/punit/on_timeout.rb +5 -1
- data/lib/flor/punit/part.rb +63 -0
- data/lib/flor/punit/schedule.rb +1 -1
- data/lib/flor/punit/task.rb +52 -10
- data/lib/flor/punit/trap.rb +4 -5
- data/lib/flor/tools/shell.rb +37 -18
- data/lib/flor/unit/caller.rb +23 -11
- data/lib/flor/unit/executor.rb +33 -12
- data/lib/flor/unit/ganger.rb +10 -1
- data/lib/flor/unit/hook.rb +2 -1
- data/lib/flor/unit/hooker.rb +13 -2
- data/lib/flor/unit/loader.rb +7 -7
- data/lib/flor/unit/logger.rb +15 -17
- data/lib/flor/unit/models.rb +4 -2
- data/lib/flor/unit/models/execution.rb +83 -38
- data/lib/flor/unit/models/message.rb +16 -0
- data/lib/flor/unit/models/pointer.rb +24 -0
- data/lib/flor/unit/models/timer.rb +25 -4
- data/lib/flor/unit/models/trace.rb +14 -0
- data/lib/flor/unit/models/trap.rb +39 -14
- data/lib/flor/unit/scheduler.rb +11 -7
- data/lib/flor/unit/storage.rb +55 -39
- data/lib/flor/unit/taskers.rb +17 -14
- data/lib/flor/unit/waiter.rb +4 -3
- metadata +40 -10
- data/lib/flor/changes.rb +0 -26
- data/lib/flor/dollar.rb +0 -224
- data/lib/flor/unit/hooks.rb +0 -37
data/lib/flor/punit/cron.rb
CHANGED
data/lib/flor/punit/every.rb
CHANGED
data/lib/flor/punit/graft.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
class Flor::Pro::Graft < Flor::Procedure
|
3
3
|
#
|
4
|
-
# Graft a subtree into the current
|
4
|
+
# Graft a subtree into the current execution.
|
5
5
|
#
|
6
6
|
# Given
|
7
7
|
# ```
|
@@ -65,8 +65,7 @@ class Flor::Pro::Graft < Flor::Procedure
|
|
65
65
|
# graft subtree into parent node
|
66
66
|
|
67
67
|
parent_tree = lookup_tree(parent)
|
68
|
-
|
69
|
-
parent_tree[1][cid] = tree
|
68
|
+
parent_tree[1][child_id] = tree
|
70
69
|
|
71
70
|
# re-apply self with subtree
|
72
71
|
|
@@ -0,0 +1,63 @@
|
|
1
|
+
|
2
|
+
class Flor::Pro::Part < Flor::Procedure
|
3
|
+
|
4
|
+
names %w[ part flank ]
|
5
|
+
|
6
|
+
# +-------------------+--------------------+
|
7
|
+
# ruote flor | replies to parent | cancellable |
|
8
|
+
# +-------+-------+------------+-------------------+--------------------+
|
9
|
+
# | fork | part | part | immediately | no (not reachable) |
|
10
|
+
# | | flunk | r: false | never | no (not reachable) |
|
11
|
+
# | flank | flank | flank | immediately | yes |
|
12
|
+
# | lose | norep | r: false | never | yes |
|
13
|
+
# +-------+-------+------------+-------------------+--------------------+
|
14
|
+
#
|
15
|
+
# reply/r: false, cancellable/c: false
|
16
|
+
#
|
17
|
+
# to part, to flank, the subject is the diverging branch
|
18
|
+
|
19
|
+
def pre_execute
|
20
|
+
|
21
|
+
@node['atts'] = []
|
22
|
+
end
|
23
|
+
|
24
|
+
def receive_last_att
|
25
|
+
|
26
|
+
@node['tree'] = Flor.dup(tree)
|
27
|
+
@node['replyto'] = nil
|
28
|
+
|
29
|
+
rep = true
|
30
|
+
@node['can'] = (heap == 'flank')
|
31
|
+
|
32
|
+
if (r = att('reply', 'rep', 'r')) != nil
|
33
|
+
rep = r
|
34
|
+
end
|
35
|
+
if (c = att('cancellable', 'can', 'c')) != nil
|
36
|
+
@node['can'] = c
|
37
|
+
end
|
38
|
+
|
39
|
+
fla = @node['can'] ? 'flank' : 'part'
|
40
|
+
# so it is possible to have `flank r: true c: false` (iow: `part`)...
|
41
|
+
|
42
|
+
(rep ? wrap('flavour' => fla, 'nid' => parent, 'ret' => nid) : []) +
|
43
|
+
super
|
44
|
+
end
|
45
|
+
|
46
|
+
def cancel
|
47
|
+
|
48
|
+
if @node['can'] || from != parent
|
49
|
+
#
|
50
|
+
# if the node is cancellable or the cancel comes not from the parent
|
51
|
+
# the cancelling goes on...
|
52
|
+
#
|
53
|
+
super
|
54
|
+
|
55
|
+
else
|
56
|
+
#
|
57
|
+
# else the cancelling does not proceed further (down)
|
58
|
+
#
|
59
|
+
[]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
data/lib/flor/punit/schedule.rb
CHANGED
data/lib/flor/punit/task.rb
CHANGED
@@ -1,5 +1,39 @@
|
|
1
1
|
|
2
2
|
class Flor::Pro::Task < Flor::Procedure
|
3
|
+
#
|
4
|
+
# Tasks a tasker with a task.
|
5
|
+
#
|
6
|
+
# ```
|
7
|
+
# task 'clean up' by: 'alan'
|
8
|
+
# task 'clean up' for: 'alan'
|
9
|
+
# task 'clean up' assign: 'alan'
|
10
|
+
# task 'alan' with: 'clean up'
|
11
|
+
# alan task: 'clean up' # {tasker} task: {taskname}
|
12
|
+
# task 'alan'
|
13
|
+
# # ...
|
14
|
+
# ```
|
15
|
+
#
|
16
|
+
# Note that the quotes are the tasker name can be omitted:
|
17
|
+
# ```
|
18
|
+
# task 'clean up' by: alan
|
19
|
+
# task 'clean up' for: alan
|
20
|
+
# task 'clean up' assign: alan
|
21
|
+
# task alan with: 'clean up'
|
22
|
+
# alan task: 'clean up' # {tasker} task: {taskname}
|
23
|
+
# task alan
|
24
|
+
# # ...
|
25
|
+
# ```
|
26
|
+
#
|
27
|
+
# Tasking hands a task (a message hash serializable to JSON) to a tasker.
|
28
|
+
# See [tasks](../tasks.md) for more information.
|
29
|
+
#
|
30
|
+
# Since routing tasks among taskers is flor essential "task", this "task"
|
31
|
+
# procedure is very important.
|
32
|
+
#
|
33
|
+
# Note that "task" can be implicit, as in:
|
34
|
+
# ```
|
35
|
+
# alan task: 'clean up'
|
36
|
+
# ```
|
3
37
|
|
4
38
|
name 'task'
|
5
39
|
|
@@ -10,11 +44,15 @@ class Flor::Pro::Task < Flor::Procedure
|
|
10
44
|
|
11
45
|
def receive
|
12
46
|
|
13
|
-
return
|
14
|
-
if point == 'receive' && from == nil
|
15
|
-
|
16
|
-
super
|
47
|
+
return super if point != 'receive' || from != nil
|
17
48
|
# which goes to #receive or #receive_when_status
|
49
|
+
|
50
|
+
pl = determine_reply_payload
|
51
|
+
#pl['ret'] = node_payload_ret
|
52
|
+
# No, let's leave it at last f.ret wins...
|
53
|
+
|
54
|
+
wrap_reply('payload' => pl)
|
55
|
+
# "task" done, reply to parent node
|
18
56
|
end
|
19
57
|
|
20
58
|
alias receive_when_closed receive_from_child_when_closed
|
@@ -25,21 +63,25 @@ class Flor::Pro::Task < Flor::Procedure
|
|
25
63
|
# task 'clean up' for: 'alan'
|
26
64
|
# task 'clean up' assign: 'alan'
|
27
65
|
# task 'alan' with: 'clean up'
|
66
|
+
# alan task: 'clean up'
|
67
|
+
#
|
28
68
|
# clean_up assign: 'alan'
|
29
69
|
# "clean up" assign: 'alan'
|
30
|
-
# alan task: 'clean up'
|
31
70
|
|
32
|
-
|
71
|
+
nis = atts(nil)
|
33
72
|
ta = att('by', 'for', 'assign')
|
34
73
|
tn = att('with', 'task')
|
35
74
|
|
36
|
-
tasker = ta ||
|
37
|
-
|
38
|
-
|
39
|
-
taskname =
|
75
|
+
tasker = ta || nis.shift
|
76
|
+
tasker = tasker[1]['tasker'] if Flor.is_tasker_tree?(tasker)
|
77
|
+
#
|
78
|
+
taskname = tn || nis.shift
|
40
79
|
|
41
80
|
attl, attd = determine_atts
|
42
81
|
|
82
|
+
@node['task'] =
|
83
|
+
{ 'tasker' => tasker, 'name' => taskname }
|
84
|
+
|
43
85
|
wrap(
|
44
86
|
'point' => 'task',
|
45
87
|
'exid' => exid, 'nid' => nid,
|
data/lib/flor/punit/trap.rb
CHANGED
@@ -8,9 +8,9 @@ class Flor::Pro::Trap < Flor::Procedure
|
|
8
8
|
#
|
9
9
|
# Once the trap is set (once the execution interprets its branch), it
|
10
10
|
# will trigger for any matching message, unless the `count:` attribute
|
11
|
-
# is set.
|
11
|
+
# is set (it will trigger for the first {count} matching messages).
|
12
12
|
#
|
13
|
-
# When the execution terminates, the trap is removed
|
13
|
+
# When the execution terminates, the trap is removed.
|
14
14
|
#
|
15
15
|
# By default, the observation range is the execution, only messages
|
16
16
|
# in the execution where the trap was set are considered.
|
@@ -195,8 +195,6 @@ class Flor::Pro::Trap < Flor::Procedure
|
|
195
195
|
|
196
196
|
def receive_non_att
|
197
197
|
|
198
|
-
return execute_child(@ncid) if children[@ncid]
|
199
|
-
|
200
198
|
fun = @fcid > 0 ? payload['ret'] : nil
|
201
199
|
|
202
200
|
points = att_a('point', 'points', nil)
|
@@ -237,7 +235,7 @@ class Flor::Pro::Trap < Flor::Procedure
|
|
237
235
|
count = 1 if fun == nil # blocking mode implies count: 1
|
238
236
|
tra['count'] = count if count
|
239
237
|
|
240
|
-
tra['range'] = att('range'
|
238
|
+
tra['range'] = att('range', 'scope') || 'subnid'
|
241
239
|
|
242
240
|
@node['trapped'] = true
|
243
241
|
|
@@ -253,6 +251,7 @@ class Flor::Pro::Trap < Flor::Procedure
|
|
253
251
|
def receive
|
254
252
|
|
255
253
|
return [] if @node['trapped']
|
254
|
+
|
256
255
|
super
|
257
256
|
end
|
258
257
|
|
data/lib/flor/tools/shell.rb
CHANGED
@@ -217,9 +217,30 @@ module Flor::Tools
|
|
217
217
|
def arg(line, index=1); args(line)[index]; end
|
218
218
|
alias fname arg
|
219
219
|
|
220
|
+
def choose_execution_path(frag, option)
|
221
|
+
|
222
|
+
fail ArgumentError.new("'frag' argument missing") \
|
223
|
+
unless frag
|
224
|
+
|
225
|
+
exe = lookup_execution(frag)
|
226
|
+
|
227
|
+
fail ArgumentError.new("execution matching \"%#{frag}%\" not found") \
|
228
|
+
unless exe
|
229
|
+
|
230
|
+
d = File.join(@root, 'tmp')
|
231
|
+
FileUtils.mkdir_p(d)
|
232
|
+
fn = File.join(d, "exe__#{exe.exid}.rb")
|
233
|
+
|
234
|
+
File.open(fn, 'wb') { |f| PP.pp(exe.data, f, 79) } \
|
235
|
+
if ! File.exist?(fn) || option == 'force'
|
236
|
+
|
237
|
+
fn
|
238
|
+
end
|
239
|
+
|
220
240
|
def choose_path(line)
|
221
241
|
|
222
242
|
b = arg(line, 2)
|
243
|
+
c = arg(line, 3)
|
223
244
|
|
224
245
|
case fname(line)
|
225
246
|
when /\Av/,
|
@@ -232,14 +253,7 @@ module Flor::Tools
|
|
232
253
|
when /\Ar/
|
233
254
|
@ra_flow_path
|
234
255
|
when /\Ae/
|
235
|
-
|
236
|
-
#p exe
|
237
|
-
#pp exe.data
|
238
|
-
#puts JSON.pretty_generate(exe.data)
|
239
|
-
#puts JSON.dump(exe.data)
|
240
|
-
#puts JSON.generate(exe.data, indent: ' ', space: ' ', object_nl: "\n", array_nl: "\n")
|
241
|
-
puts Flor.to_d(exe.data, width: true, colours: true)
|
242
|
-
fail NotImplementedError
|
256
|
+
choose_execution_path(b, c)
|
243
257
|
else
|
244
258
|
@flow_path
|
245
259
|
end
|
@@ -442,8 +456,8 @@ fail NotImplementedError
|
|
442
456
|
def cmd_launch(line)
|
443
457
|
|
444
458
|
flow = File.read(@flow_path)
|
445
|
-
variables = Flor::ConfExecutor.
|
446
|
-
payload = Flor::ConfExecutor.
|
459
|
+
variables = Flor::ConfExecutor.interpret_path(@variables_path)
|
460
|
+
payload = Flor::ConfExecutor.interpret_path(@payload_path)
|
447
461
|
domain = 'shell'
|
448
462
|
|
449
463
|
vars = Flor::ConfExecutor.interpret_line("\n" + line[6..-1]) rescue {}
|
@@ -480,7 +494,7 @@ fail NotImplementedError
|
|
480
494
|
o.puts " #{@c.dark_gray(l)}"
|
481
495
|
end
|
482
496
|
end
|
483
|
-
rescue
|
497
|
+
rescue
|
484
498
|
fail ArgumentError.new("no 'manual' for #{cmd.inspect}")
|
485
499
|
end
|
486
500
|
|
@@ -489,9 +503,9 @@ fail NotImplementedError
|
|
489
503
|
o.puts
|
490
504
|
o.puts "## available commands:"
|
491
505
|
o.puts
|
492
|
-
COMMANDS.each do |
|
493
|
-
o.print "* #{@c.yellow(
|
494
|
-
if hlp = (send("hlp_#{
|
506
|
+
COMMANDS.each do |c|
|
507
|
+
o.print "* #{@c.yellow(c)}"
|
508
|
+
if hlp = (send("hlp_#{c}") rescue nil)
|
495
509
|
o.print " - #{hlp.strip}"
|
496
510
|
end
|
497
511
|
o.puts
|
@@ -555,6 +569,10 @@ fail NotImplementedError
|
|
555
569
|
edits the payload for the next execution
|
556
570
|
* edit [f|flow]
|
557
571
|
edits the flow for the next execution
|
572
|
+
* edit [e|exe] frag
|
573
|
+
edits the execution data
|
574
|
+
* edit [e|exe] frag force
|
575
|
+
edits the execution data (redump the data)
|
558
576
|
* edit t|task frag
|
559
577
|
edits a task currently under var/tasks/
|
560
578
|
* edit r
|
@@ -583,7 +601,7 @@ fail NotImplementedError
|
|
583
601
|
def cmd_cat(line)
|
584
602
|
|
585
603
|
puts " # #{@flow_path}\n"
|
586
|
-
File.readlines(@flow_path).each { |
|
604
|
+
File.readlines(@flow_path).each { |l| puts " #{l}" }
|
587
605
|
end
|
588
606
|
|
589
607
|
def hlp_conf
|
@@ -624,7 +642,8 @@ fail NotImplementedError
|
|
624
642
|
%{ prints the file hierarchy for #{@root} }
|
625
643
|
end
|
626
644
|
def cmd_t(line)
|
627
|
-
page(`tree -C #{@root}`)
|
645
|
+
#page(`tree -C #{@root}`)
|
646
|
+
page(`tree #{@root}`)
|
628
647
|
end
|
629
648
|
|
630
649
|
def hlp_tasks
|
@@ -829,7 +848,7 @@ fail NotImplementedError
|
|
829
848
|
def cmd_hook(line)
|
830
849
|
|
831
850
|
@hook =
|
832
|
-
case
|
851
|
+
case arg(line)
|
833
852
|
when 'true', 'on'
|
834
853
|
'on'
|
835
854
|
when 'false', 'off'
|
@@ -1140,7 +1159,7 @@ Once edited (or not), a nato tasker task can be returned to flor (to the schedul
|
|
1140
1159
|
}
|
1141
1160
|
#Readline.completion_append_character =
|
1142
1161
|
# " "
|
1143
|
-
rescue LoadError
|
1162
|
+
rescue LoadError
|
1144
1163
|
def prompt_and_read
|
1145
1164
|
print(prompt)
|
1146
1165
|
($stdin.readline rescue false)
|
data/lib/flor/unit/caller.rb
CHANGED
@@ -26,7 +26,7 @@ module Flor
|
|
26
26
|
|
27
27
|
rescue => err
|
28
28
|
|
29
|
-
[ Flor.
|
29
|
+
[ Flor.to_error_message(message, err) ]
|
30
30
|
end
|
31
31
|
|
32
32
|
protected
|
@@ -52,31 +52,36 @@ module Flor
|
|
52
52
|
fail ArgumentError.new('".." not allowed in paths') if pa =~ /\.\./
|
53
53
|
load(fjoin(root, pa)) }
|
54
54
|
|
55
|
+
#
|
56
|
+
# initialize
|
57
|
+
|
55
58
|
k = Flor.const_lookup(conf['class'] || conf['module'])
|
56
59
|
|
57
60
|
o =
|
58
61
|
if k.class == Module
|
59
62
|
k
|
60
63
|
else
|
61
|
-
case k.instance_method(:initialize).arity
|
62
|
-
when 1 then k.new(
|
63
|
-
|
64
|
-
when 3 then k.new(service, conf, message)
|
64
|
+
case i = k.instance_method(:initialize).arity
|
65
|
+
when 1, 2, 3 then k.new(
|
66
|
+
*[ service, conf, message ][0, i])
|
65
67
|
when -1 then k.new({
|
66
68
|
service: service, configuration: conf, message: message })
|
67
69
|
else k.new
|
68
70
|
end
|
69
71
|
end
|
70
72
|
|
73
|
+
#
|
74
|
+
# call
|
75
|
+
|
71
76
|
p = message['point']
|
72
|
-
|
73
|
-
|
74
|
-
m =
|
75
|
-
m = :cancel if m == 'detask' && ! o.respond_to?(m)
|
77
|
+
ms = [ "on_#{p}", :on_message, :on, p ]
|
78
|
+
ms = ms + [ :on_cancel, :cancel ] if p == 'detask'
|
79
|
+
m = ms.find { |mm| o.respond_to?(mm) }
|
76
80
|
|
77
81
|
fail(
|
78
|
-
"#{k.class.to_s.downcase} #{k} doesn't respond to
|
79
|
-
|
82
|
+
"#{k.class.to_s.downcase} #{k} doesn't respond to " +
|
83
|
+
ms[0..-2].collect { |e| "##{e}" }.join(', ') + ", or ##{ms[-1]}"
|
84
|
+
) unless m
|
80
85
|
|
81
86
|
r =
|
82
87
|
case o.method(m).arity
|
@@ -88,6 +93,9 @@ module Flor
|
|
88
93
|
else o.send(m)
|
89
94
|
end
|
90
95
|
|
96
|
+
#
|
97
|
+
# reply
|
98
|
+
|
91
99
|
to_messages(r)
|
92
100
|
end
|
93
101
|
|
@@ -146,6 +154,10 @@ module Flor
|
|
146
154
|
fail SpawnError.new(status, i.read, f.read) if status.exitstatus != 0
|
147
155
|
|
148
156
|
[ i.read, status ]
|
157
|
+
|
158
|
+
ensure
|
159
|
+
|
160
|
+
[ i, o, f, e, r, w ].each { |x| x.close rescue nil }
|
149
161
|
end
|
150
162
|
|
151
163
|
class SpawnError < StandardError
|