flor 0.18.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (163) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +3 -0
  3. data/CREDITS.md +1 -0
  4. data/LICENSE.txt +1 -1
  5. data/Makefile +1 -1
  6. data/README.md +15 -1
  7. data/flor.gemspec +1 -2
  8. data/lib/flor.rb +2 -2
  9. data/lib/flor/colours.rb +5 -3
  10. data/lib/flor/conf.rb +1 -0
  11. data/lib/flor/core.rb +9 -8
  12. data/lib/flor/core/executor.rb +20 -16
  13. data/lib/flor/core/node.rb +1 -13
  14. data/lib/flor/core/procedure.rb +10 -1
  15. data/lib/flor/core/texecutor.rb +9 -2
  16. data/lib/flor/djan.rb +1 -0
  17. data/lib/flor/errors.rb +1 -0
  18. data/lib/flor/flor.rb +144 -15
  19. data/lib/flor/id.rb +1 -0
  20. data/lib/flor/log.rb +2 -1
  21. data/lib/flor/migrations/0001_tables.rb +1 -0
  22. data/lib/flor/migrations/0002_cunit_and_munit.rb +1 -0
  23. data/lib/flor/migrations/0003_timer_onid_bnid.rb +1 -0
  24. data/lib/flor/migrations/0004_trap_bnid.rb +1 -0
  25. data/lib/flor/migrations/0005_pointer_content.rb +1 -0
  26. data/lib/flor/parser.rb +19 -11
  27. data/lib/flor/pcore/_apply.rb +1 -0
  28. data/lib/flor/pcore/_arr.rb +1 -0
  29. data/lib/flor/pcore/_atom.rb +1 -0
  30. data/lib/flor/pcore/_att.rb +1 -0
  31. data/lib/flor/pcore/_coll.rb +1 -0
  32. data/lib/flor/pcore/_dmute.rb +1 -0
  33. data/lib/flor/pcore/_dol.rb +1 -0
  34. data/lib/flor/pcore/_dqs.rb +1 -0
  35. data/lib/flor/pcore/_dump.rb +1 -0
  36. data/lib/flor/pcore/_err.rb +1 -0
  37. data/lib/flor/pcore/_head.rb +1 -0
  38. data/lib/flor/pcore/_obj.rb +1 -0
  39. data/lib/flor/pcore/_pat_.rb +1 -0
  40. data/lib/flor/pcore/_pat_arr.rb +1 -0
  41. data/lib/flor/pcore/_pat_guard.rb +1 -0
  42. data/lib/flor/pcore/_pat_obj.rb +1 -0
  43. data/lib/flor/pcore/_pat_or.rb +1 -0
  44. data/lib/flor/pcore/_pat_regex.rb +1 -0
  45. data/lib/flor/pcore/_ref.rb +1 -0
  46. data/lib/flor/pcore/_rxs.rb +1 -0
  47. data/lib/flor/pcore/_skip.rb +1 -0
  48. data/lib/flor/pcore/_val.rb +1 -0
  49. data/lib/flor/pcore/all.rb +1 -0
  50. data/lib/flor/pcore/andor.rb +1 -0
  51. data/lib/flor/pcore/any.rb +1 -0
  52. data/lib/flor/pcore/apply.rb +1 -0
  53. data/lib/flor/pcore/arith.rb +1 -0
  54. data/lib/flor/pcore/array_qmark.rb +1 -0
  55. data/lib/flor/pcore/break.rb +1 -0
  56. data/lib/flor/pcore/case.rb +1 -0
  57. data/lib/flor/pcore/cmp.rb +1 -0
  58. data/lib/flor/pcore/collect.rb +2 -1
  59. data/lib/flor/pcore/cond.rb +1 -0
  60. data/lib/flor/pcore/cursor.rb +45 -3
  61. data/lib/flor/pcore/define.rb +1 -0
  62. data/lib/flor/pcore/detect.rb +1 -0
  63. data/lib/flor/pcore/do_return.rb +1 -0
  64. data/lib/flor/pcore/each.rb +1 -0
  65. data/lib/flor/pcore/echo.rb +1 -0
  66. data/lib/flor/pcore/empty.rb +1 -0
  67. data/lib/flor/pcore/fail.rb +1 -0
  68. data/lib/flor/pcore/filter.rb +1 -0
  69. data/lib/flor/pcore/find.rb +1 -0
  70. data/lib/flor/pcore/flatten.rb +1 -0
  71. data/lib/flor/pcore/for_each.rb +1 -0
  72. data/lib/flor/pcore/if.rb +1 -0
  73. data/lib/flor/pcore/includes.rb +1 -0
  74. data/lib/flor/pcore/inject.rb +1 -0
  75. data/lib/flor/pcore/iterator.rb +1 -0
  76. data/lib/flor/pcore/keys.rb +1 -0
  77. data/lib/flor/pcore/length.rb +1 -0
  78. data/lib/flor/pcore/loop.rb +1 -0
  79. data/lib/flor/pcore/map.rb +1 -0
  80. data/lib/flor/pcore/match.rb +1 -0
  81. data/lib/flor/pcore/matchr.rb +1 -0
  82. data/lib/flor/pcore/max.rb +1 -0
  83. data/lib/flor/pcore/merge.rb +1 -0
  84. data/lib/flor/pcore/move.rb +1 -0
  85. data/lib/flor/pcore/noeval.rb +1 -0
  86. data/lib/flor/pcore/noret.rb +1 -0
  87. data/lib/flor/pcore/not.rb +1 -0
  88. data/lib/flor/pcore/on.rb +4 -3
  89. data/lib/flor/pcore/on_cancel.rb +1 -0
  90. data/lib/flor/pcore/on_error.rb +1 -0
  91. data/lib/flor/pcore/push.rb +1 -0
  92. data/lib/flor/pcore/rand.rb +1 -0
  93. data/lib/flor/pcore/range.rb +1 -0
  94. data/lib/flor/pcore/reduce.rb +1 -0
  95. data/lib/flor/pcore/return.rb +1 -0
  96. data/lib/flor/pcore/reverse.rb +1 -0
  97. data/lib/flor/pcore/select.rb +1 -0
  98. data/lib/flor/pcore/sequence.rb +1 -0
  99. data/lib/flor/pcore/set.rb +1 -0
  100. data/lib/flor/pcore/shuffle.rb +1 -0
  101. data/lib/flor/pcore/slice.rb +1 -0
  102. data/lib/flor/pcore/sort.rb +1 -0
  103. data/lib/flor/pcore/sort_by.rb +1 -0
  104. data/lib/flor/pcore/split.rb +1 -0
  105. data/lib/flor/pcore/stall.rb +1 -0
  106. data/lib/flor/pcore/strings.rb +1 -0
  107. data/lib/flor/pcore/timestamp.rb +1 -0
  108. data/lib/flor/pcore/to_array.rb +1 -0
  109. data/lib/flor/pcore/twig.rb +1 -0
  110. data/lib/flor/pcore/type_of.rb +1 -0
  111. data/lib/flor/pcore/until.rb +1 -0
  112. data/lib/flor/punit/abort.rb +50 -0
  113. data/lib/flor/punit/c_collect.rb +1 -0
  114. data/lib/flor/punit/c_each.rb +19 -0
  115. data/lib/flor/punit/c_for_each.rb +2 -1
  116. data/lib/flor/punit/c_iterator.rb +1 -0
  117. data/lib/flor/punit/c_map.rb +1 -0
  118. data/lib/flor/punit/cancel.rb +1 -0
  119. data/lib/flor/punit/concurrence.rb +1 -0
  120. data/lib/flor/punit/cron.rb +1 -0
  121. data/lib/flor/punit/do_trap.rb +1 -0
  122. data/lib/flor/punit/every.rb +1 -0
  123. data/lib/flor/punit/graft.rb +1 -0
  124. data/lib/flor/punit/m_ram.rb +1 -0
  125. data/lib/flor/punit/on_timeout.rb +1 -0
  126. data/lib/flor/punit/part.rb +1 -0
  127. data/lib/flor/punit/schedule.rb +1 -0
  128. data/lib/flor/punit/signal.rb +1 -0
  129. data/lib/flor/punit/sleep.rb +1 -0
  130. data/lib/flor/punit/task.rb +1 -0
  131. data/lib/flor/punit/trace.rb +1 -0
  132. data/lib/flor/punit/trap.rb +10 -1
  133. data/lib/flor/to_string.rb +1 -0
  134. data/lib/flor/tools/env.rb +1 -0
  135. data/lib/flor/tools/shell.rb +1 -0
  136. data/lib/flor/tools/shell_out.rb +1 -0
  137. data/lib/flor/tt.rb +98 -0
  138. data/lib/flor/unit.rb +2 -0
  139. data/lib/flor/unit/caller.rb +152 -19
  140. data/lib/flor/unit/caller_jruby.rb +132 -0
  141. data/lib/flor/unit/dump.rb +36 -0
  142. data/lib/flor/unit/executor.rb +1 -0
  143. data/lib/flor/unit/ganger.rb +6 -12
  144. data/lib/flor/unit/hloader.rb +34 -7
  145. data/lib/flor/unit/hook.rb +3 -0
  146. data/lib/flor/unit/hooker.rb +32 -15
  147. data/lib/flor/unit/journal.rb +23 -0
  148. data/lib/flor/unit/loader.rb +102 -7
  149. data/lib/flor/unit/logger.rb +24 -5
  150. data/lib/flor/unit/models.rb +8 -1
  151. data/lib/flor/unit/models/execution.rb +51 -0
  152. data/lib/flor/unit/models/message.rb +1 -0
  153. data/lib/flor/unit/models/pointer.rb +1 -0
  154. data/lib/flor/unit/models/timer.rb +1 -0
  155. data/lib/flor/unit/models/trace.rb +1 -0
  156. data/lib/flor/unit/models/trap.rb +3 -2
  157. data/lib/flor/unit/scheduler.rb +35 -28
  158. data/lib/flor/unit/spooler.rb +1 -0
  159. data/lib/flor/unit/storage.rb +68 -44
  160. data/lib/flor/unit/taskers.rb +2 -1
  161. data/lib/flor/unit/waiter.rb +2 -1
  162. data/lib/flor/unit/wlist.rb +10 -8
  163. metadata +13 -10
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  require 'flor/punit/schedule'
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  class Flor::Pro::Graft < Flor::Procedure
3
4
  #
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  # Module extracted out of "concurrence", deals with receivers and mergers.
3
4
  #
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  class Flor::Pro::OnTimeout < Flor::Procedure
3
4
  #
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  class Flor::Pro::Part < Flor::Procedure
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  class Flor::Pro::Schedule < Flor::Procedure
3
4
  #
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  class Flor::Pro::Signal < Flor::Procedure
3
4
  #
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  class Flor::Pro::Sleep < Flor::Procedure
3
4
  #
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  class Flor::Pro::Task < Flor::Procedure
3
4
  #
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  class Flor::Pro::Trace < Flor::Procedure
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  # Would it be worth the pain implementing bind:?
3
4
 
@@ -168,6 +169,12 @@ class Flor::Pro::Trap < Flor::Procedure
168
169
  # (org.acme.accounting, org.acme.engineering, org.acme.whatever.x.y.z, ...)
169
170
  #
170
171
  #
172
+ # ## the bnid: directive
173
+ #
174
+ # "bound nid" by default is the nid of the parent expression to the "trap".
175
+ # With `bnid:`, one can bind a trap to another expression.
176
+ #
177
+ #
171
178
  # ## the count: limit
172
179
  #
173
180
  # ```
@@ -244,6 +251,8 @@ class Flor::Pro::Trap < Flor::Procedure
244
251
  points = points.uniq if points
245
252
  names = names.uniq if names
246
253
 
254
+ bnid = att('bnid', 'bind') || parent || '0'
255
+
247
256
  msg =
248
257
  if fun
249
258
  apply(fun, [], tree[2], anid: false).first
@@ -253,7 +262,7 @@ class Flor::Pro::Trap < Flor::Procedure
253
262
 
254
263
  tra = {}
255
264
  tra['nid'] = nid
256
- tra['bnid'] = parent || '0'
265
+ tra['bnid'] = bnid
257
266
  tra['points'] = points
258
267
  tra['tags'] = tags
259
268
  tra['heaps'] = heaps
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  module Flor
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  require 'fileutils'
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  require 'io/console'
3
4
  require 'terminal-table'
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  module Flor::Tools
3
4
 
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'io/console'
4
+ require 'terminal-table'
5
+
6
+ module Flor
7
+
8
+ class << self
9
+
10
+ def to_tt(o, opts={})
11
+
12
+ h = o.is_a?(Hash) ? o : nil
13
+
14
+ if h && h['status'].is_a?(Array) && h['nid'].is_a?(String)
15
+ node_tt(h, opts)
16
+ elsif h && h['point'].is_a?(String) && h['sm'].is_a?(Integer)
17
+ message_tt(h, opts)
18
+ elsif h
19
+ djan_tt(h, opts)
20
+ else
21
+ pp_tt(o, opts)
22
+ end
23
+ end
24
+
25
+ def tt(o, opts={})
26
+
27
+ puts(to_tt(o, opts))
28
+ end
29
+
30
+ protected
31
+
32
+ #def columns; `tput cols`.to_i rescue 80; end
33
+ def columns; IO.console.winsize[1] rescue 80; end
34
+
35
+ def make_tt_table(opts, &block)
36
+
37
+ c = Flor.colours(opts)
38
+
39
+ table = Terminal::Table.new
40
+ table.style.border_x = opts[:border_x] || c.dg('-')
41
+ table.style.border_i = opts[:border_i] || c.dg('.')
42
+ table.style.border_y = opts[:border_y] || c.dg('|')
43
+
44
+ block.call(table) if block
45
+
46
+ table
47
+ end
48
+
49
+ def message_tt(m, opts)
50
+
51
+ cols = columns.to_f
52
+ w = (cols * 0.49).to_i
53
+
54
+ west =
55
+ "** message **\n\n" +
56
+ Flor.to_d(m.select { |k, _| k != 'cause' }, width: w)
57
+ east =
58
+ "cause:\n\n" +
59
+ Flor.to_d(m['cause'], width: w)
60
+
61
+ make_tt_table(opts) { |t| t.add_row([ west, east ]) }.to_s
62
+ end
63
+
64
+ def node_tt(n, opts)
65
+
66
+ cols = columns.to_f
67
+ w = (cols * 0.49).to_i
68
+
69
+ west =
70
+ "** node **\n\n" +
71
+ Flor.to_d(n.select { |k, _| k != 'status' && k != 'tree' }, width: w)
72
+ east =
73
+ "status:\n\n" +
74
+ Flor.to_d(n['status'], width: w)
75
+
76
+ make_tt_table(opts) { |t| t.add_row([ west, east ]) }.to_s
77
+ end
78
+
79
+ def pp_tt(o, opts)
80
+
81
+ make_tt_table(opts) { |t| t.add_row([ pp_s(o, opts) ]) }.to_s
82
+ end
83
+
84
+ def djan_tt(o, opts)
85
+
86
+ make_tt_table(opts) { |t| t.add_row([ Flor.to_d(o) ]) }.to_s
87
+ end
88
+
89
+ def pp_s(o, opts)
90
+
91
+ s = StringIO.new
92
+ PP.pp(o, s)
93
+
94
+ s.string
95
+ end
96
+ end
97
+ end
98
+
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  require 'sequel'
3
4
  require 'sequel/extensions/migration'
@@ -5,6 +6,7 @@ require 'sequel/extensions/migration'
5
6
  require 'fugit'
6
7
 
7
8
  require 'flor'
9
+ require 'flor/unit/dump'
8
10
  require 'flor/unit/hook'
9
11
  require 'flor/unit/hooker'
10
12
  require 'flor/unit/caller'
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  module Flor
3
4
 
@@ -41,16 +42,44 @@ module Flor
41
42
  root == '.' ? path : File.join(root, path)
42
43
  end
43
44
 
44
- def ruby_call(service, conf, message)
45
+ def do_require(conf, path)
46
+
47
+ fail ArgumentError.new('".." not allowed in paths') \
48
+ if path =~ /\.\./
49
+
50
+ begin
51
+ require(path)
52
+ return
53
+ rescue LoadError => le
54
+ end
55
+
56
+ root = File.dirname(conf['_path'] || '.')
57
+
58
+ require(fjoin(root, path))
59
+ end
60
+
61
+ def do_load(conf, path)
62
+
63
+ fail ArgumentError.new('".." not allowed in paths') \
64
+ if path =~ /\.\./
65
+
66
+ path += '.rb' unless path.match(/\.rb\z/)
67
+
68
+ begin
69
+ load(path)
70
+ return
71
+ rescue LoadError
72
+ end
45
73
 
46
74
  root = File.dirname(conf['_path'] || '.')
47
75
 
48
- Flor.h_fetch_a(conf, 'require').each { |pa|
49
- fail ArgumentError.new('".." not allowed in paths') if pa =~ /\.\./
50
- require(fjoin(root, pa)) }
51
- Flor.h_fetch_a(conf, 'load').each { |pa|
52
- fail ArgumentError.new('".." not allowed in paths') if pa =~ /\.\./
53
- load(fjoin(root, pa)) }
76
+ load(fjoin(root, path))
77
+ end
78
+
79
+ def ruby_call(service, conf, message)
80
+
81
+ Flor.h_fetch_a(conf, 'require').each { |pa| do_require(conf, pa) }
82
+ Flor.h_fetch_a(conf, 'load').each { |pa| do_load(conf, pa) }
54
83
 
55
84
  #
56
85
  # initialize
@@ -112,10 +141,8 @@ module Flor
112
141
  h['v'] = message['vars']
113
142
  h['tag'] = (message['tags'] || []).first
114
143
 
115
- cmd = h['cmd']
116
-
117
144
  m = encode(conf, message)
118
- out, _ = spawn(cmd, m)
145
+ out, _ = spawn(conf, m)
119
146
  r = decode(conf, out)
120
147
 
121
148
  to_messages(r)
@@ -143,7 +170,32 @@ module Flor
143
170
  .load(data)
144
171
  end
145
172
 
146
- def spawn(cmd, data)
173
+ def timeout(t, &block)
174
+
175
+ #Timeout.timeout(t, &block)
176
+ # avoid using Ruby Timeout :-( It was 2008, is that still relevant?
177
+
178
+ t0 = Time.now
179
+ th = Thread.new { Thread.current[:return] = block.call }
180
+ while (Time.now - t0 < t) do
181
+ break if th.key?(:return)
182
+ sleep 0.014
183
+ end
184
+
185
+ fail TimeoutError.new('execution expired') unless th.key?(:return)
186
+
187
+ th[:return]
188
+ end
189
+
190
+ def spawn(conf, data)
191
+
192
+ t0 = Time.now
193
+
194
+ cmd = conf['cmd']
195
+
196
+ to = Fugit.parse(conf['timeout'] || '14s')
197
+ to = to.is_a?(Fugit::Duration) ? to.to_sec : 14
198
+ to = 0 if to < 0 # no timeout
147
199
 
148
200
  i, o = IO.pipe # _ / stdout
149
201
  f, e = IO.pipe # _ / stderr
@@ -154,12 +206,24 @@ module Flor
154
206
  w.close
155
207
  o.close
156
208
  e.close
157
- _, status = Process.wait2(pid)
158
209
 
159
- fail SpawnError.new(status, i.read, f.read) if status.exitstatus != 0
210
+ _, status = timeout(to) { Process.wait2(pid) }
211
+
212
+ fail SpawnNonZeroExitError.new(conf, { to: to, t0: t0 }, status, i, f) \
213
+ if status.exitstatus != 0
160
214
 
161
215
  [ i.read, status ]
162
216
 
217
+ rescue => err
218
+
219
+ Process.detach(pid) \
220
+ if pid
221
+ (Process.kill(9, pid) rescue nil) \
222
+ unless Flor.no?(conf['on_error_kill'])
223
+
224
+ raise err if err.is_a?(SpawnError)
225
+ raise WrappedSpawnError.new(conf, { to: to, t0: t0, pid: pid }, err)
226
+
163
227
  ensure
164
228
 
165
229
  [ i, o, f, e, r, w ].each { |x| x.close rescue nil }
@@ -167,17 +231,84 @@ module Flor
167
231
 
168
232
  class SpawnError < StandardError
169
233
 
234
+ attr_accessor :conf, :ctx
235
+
236
+ def initialize(conf, ctx, msg)
237
+
238
+ @conf = conf
239
+ @ctx = ctx
240
+
241
+ super(msg)
242
+ end
243
+
244
+ def details
245
+
246
+ ha = Flor.yes?(@conf['on_error_hide_all'])
247
+ hcd = Flor.yes?(@conf['on_error_hide_cmd'])
248
+ hcf = Flor.yes?(@conf['on_error_hide_conf'])
249
+
250
+ cd = (ha || hcd) ? '(hidden)' : @conf['cmd']
251
+ cf = (ha || hcf) ? '(hidden)' : @conf.dup
252
+ cf['cmd'] = '(hidden)' if hcd && cf.is_a?(Hash)
253
+
254
+ d = {
255
+ cmd: cd, conf: cf,
256
+ timeout: ctx[:to],
257
+ pid: ctx[:pid],
258
+ start: Flor.tstamp(ctx[:t0]),
259
+ duration: Fugit.parse(Time.now - ctx[:t0]).to_plain_s,
260
+ cause: cause }
261
+
262
+ add_details(d) \
263
+ if respond_to?(:add_details)
264
+
265
+ d
266
+ end
267
+ end
268
+
269
+ class TimeoutError < StandardError; end
270
+
271
+ class WrappedSpawnError < SpawnError
272
+
273
+ attr_reader :cause
274
+
275
+ def initialize(conf, ctx, err)
276
+
277
+ @cause = err
278
+
279
+ super(conf, ctx, "wrapped: #{err.class.name}: #{err.message}")
280
+
281
+ set_backtrace(err.backtrace)
282
+ end
283
+
284
+ def add_details(details)
285
+
286
+ details[:cause] = { kla: @cause.class.name, msg: @cause.message }
287
+ end
288
+ end
289
+
290
+ class SpawnNonZeroExitError < SpawnError
291
+
170
292
  attr_reader :status, :out, :err
171
293
 
172
- def initialize(status, out, err)
294
+ def initialize(conf, ctx, status, stdout, stderr)
295
+
296
+ @status = s = status
297
+ @stdout = stdout.read
298
+ @stderr = stderr.read
173
299
 
174
- @status = status
175
- @out = out
176
- @err = err
300
+ ctx[:pid] ||= status.pid
177
301
 
178
- msg = err.strip.split("\n").last
302
+ msg = @stderr.strip.split("\n").last
179
303
 
180
- super("(code: #{status.exitstatus}, pid: #{status.pid}) #{msg}")
304
+ super(conf, ctx, "(code: #{s.exitstatus}, pid: #{s.pid}) #{msg}")
305
+ end
306
+
307
+ def add_details(details)
308
+
309
+ details[:status] = status.to_s
310
+ details[:stdout] = @stdout
311
+ details[:stderr] = @stderr
181
312
  end
182
313
  end
183
314
 
@@ -189,6 +320,8 @@ module Flor
189
320
  else []
190
321
  end
191
322
  end
323
+
324
+ require 'flor/unit/caller_jruby' if RUBY_PLATFORM.match(/java/)
192
325
  end
193
326
  end
194
327