flor 0.18.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +29 -0
  3. data/CREDITS.md +1 -0
  4. data/LICENSE.txt +1 -1
  5. data/Makefile +1 -1
  6. data/README.md +16 -2
  7. data/flor.gemspec +1 -2
  8. data/lib/flor.rb +3 -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 +35 -3
  16. data/lib/flor/djan.rb +1 -0
  17. data/lib/flor/errors.rb +1 -0
  18. data/lib/flor/flor.rb +172 -26
  19. data/lib/flor/id.rb +5 -1
  20. data/lib/flor/log.rb +14 -8
  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 +2 -1
  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 +7 -5
  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 → m_receive_and_merge.rb} +5 -4
  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/firb.rb +33 -0
  136. data/lib/flor/tools/shell.rb +13 -3
  137. data/lib/flor/tools/shell_out.rb +1 -0
  138. data/lib/flor/tt.rb +98 -0
  139. data/lib/flor/unit.rb +3 -0
  140. data/lib/flor/unit/caller.rb +158 -23
  141. data/lib/flor/unit/caller_jruby.rb +133 -0
  142. data/lib/flor/unit/dump.rb +36 -0
  143. data/lib/flor/unit/executor.rb +19 -13
  144. data/lib/flor/unit/ganger.rb +9 -12
  145. data/lib/flor/unit/gangers.rb +125 -0
  146. data/lib/flor/unit/hloader.rb +34 -7
  147. data/lib/flor/unit/hook.rb +3 -0
  148. data/lib/flor/unit/hooker.rb +32 -15
  149. data/lib/flor/unit/journal.rb +23 -0
  150. data/lib/flor/unit/loader.rb +142 -15
  151. data/lib/flor/unit/logger.rb +35 -7
  152. data/lib/flor/unit/models.rb +8 -1
  153. data/lib/flor/unit/models/execution.rb +51 -0
  154. data/lib/flor/unit/models/message.rb +6 -0
  155. data/lib/flor/unit/models/pointer.rb +21 -1
  156. data/lib/flor/unit/models/timer.rb +1 -0
  157. data/lib/flor/unit/models/trace.rb +1 -0
  158. data/lib/flor/unit/models/trap.rb +3 -2
  159. data/lib/flor/unit/scheduler.rb +51 -36
  160. data/lib/flor/unit/spooler.rb +1 -0
  161. data/lib/flor/unit/storage.rb +113 -84
  162. data/lib/flor/unit/taskers.rb +70 -1
  163. data/lib/flor/unit/waiter.rb +22 -17
  164. data/lib/flor/unit/wlist.rb +19 -8
  165. metadata +16 -11
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  require 'flor/punit/c_iterator'
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  class Flor::Pro::Cancel < Flor::Procedure
3
4
  #
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
1
2
 
2
- require 'flor/punit/m_ram'
3
+ require 'flor/punit/m_receive_and_merge'
3
4
 
4
5
 
5
6
  class Flor::Pro::Concurrence < Flor::Procedure
@@ -386,10 +387,11 @@ class Flor::Pro::Concurrence < Flor::Procedure
386
387
 
387
388
  REWRITE_AS_ATTS = %w[
388
389
  on_receive on_merge
389
- child_on_error children_on_error ]
390
- #
391
- # heads of the child nodes that should get rewritten as attributes
392
- # of the concurrence ...
390
+ child_on_error children_on_error
391
+ ].freeze
392
+ #
393
+ # heads of the child nodes that should get rewritten as attributes
394
+ # of the concurrence ...
393
395
 
394
396
  def pre_execute_rewrite
395
397
 
@@ -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::DoTrap < Flor::Macro
3
4
  #
@@ -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
  #
@@ -178,15 +179,15 @@ module Flor::Pro::ReceiveAndMerge
178
179
  # * ignore
179
180
  # * stack
180
181
 
181
- STACK_REX = /\Astack(?::([-_a-zA-Z0-9]+))?\z/
182
+ STACK_REX = /\Astack(?::([-_a-zA-Z0-9]+))?\z/.freeze
182
183
 
183
- TRANSLATORS = { STACK_REX => 'k', /\Atail\z/ => 'a' }
184
+ TRANSLATORS = { STACK_REX => 'k', /\Atail\z/ => 'a' }.freeze
184
185
 
185
186
  MORDERS = {
186
- 'f' => :first, 'l' => :last, /[tnh]/ => :north, /[bsa]/ => :south }
187
+ 'f' => :first, 'l' => :last, /[tnh]/ => :north, /[bsa]/ => :south }.freeze
187
188
  MMERGERS = {
188
189
  'd' => :deep, /[mp]/ => :mix, 'o' => :override, 'i' => :ignore,
189
- 'k' => :stack }
190
+ 'k' => :stack }.freeze
190
191
 
191
192
  def default_merger
192
193
 
@@ -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
 
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'irb'
4
+
5
+ #require 'sequel'
6
+ require 'flor'
7
+ require 'flor/unit'
8
+
9
+
10
+ p [ RUBY_VERSION, RUBY_PLATFORM ]
11
+
12
+ puts
13
+
14
+ ENV.each do |k, v|
15
+ next unless k.match?(/RUBY|GEM/)
16
+ puts "* #{k}: #{v}"
17
+ end
18
+
19
+ ARGV.each do |arg|
20
+ if arg.match(/:/)
21
+ DB = Sequel.connect(arg)
22
+ p DB
23
+ end
24
+ end
25
+
26
+ #MODELS = [ :executions, :timers, :traces, :traps, :pointers, :messages ]
27
+ if defined?(DB)
28
+ Flor::Message.dataset = DB[:flor_messages]
29
+ end
30
+
31
+ ARGV.clear
32
+ IRB.start
33
+
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  require 'io/console'
3
4
  require 'terminal-table'
@@ -14,11 +15,16 @@ module Flor::Tools
14
15
  def initialize(argv=nil)
15
16
 
16
17
  env = ENV['FLOR_ENV'] || 'shell'
17
- @root = "envs/#{env}"
18
+
19
+ @root = File.directory?(env) ? env : "envs/#{env}"
18
20
 
19
21
  prepare_home
20
22
 
21
- @unit = Flor::Unit.new("#{@root}/etc/conf.json")
23
+ over_conf = {}
24
+ #
25
+ c = ENV['FLOR_STO_URI']; over_conf['sto_uri'] = c if c
26
+
27
+ @unit = Flor::Unit.new("#{@root}/etc/conf.json", over_conf)
22
28
 
23
29
  @unit.conf['unit'] = 'cli'
24
30
  #unit.hooker.add('journal', Flor::Journal)
@@ -30,7 +36,11 @@ module Flor::Tools
30
36
  @mute = false
31
37
  @paging = true
32
38
 
33
- @unit.start
39
+ if ENV['FLOR_NO_START']
40
+ @unit.check_migration_version
41
+ else
42
+ @unit.start
43
+ end
34
44
 
35
45
  @flow_path = File.join(@root, 'home/scratch.flo')
36
46
  @ra_flow_path = File.join(@root, 'home/ra_scratch.flo')
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  module Flor::Tools
3
4
 
data/lib/flor/tt.rb ADDED
@@ -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
+
data/lib/flor/unit.rb CHANGED
@@ -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'
@@ -20,6 +22,7 @@ require 'flor/unit/loader'
20
22
  require 'flor/unit/hloader'
21
23
  require 'flor/unit/ganger'
22
24
  require 'flor/unit/spooler'
25
+ require 'flor/unit/gangers'
23
26
  require 'flor/unit/taskers'
24
27
 
25
28
  Flor.load_procedures('punit')
@@ -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
45
55
 
46
56
  root = File.dirname(conf['_path'] || '.')
47
57
 
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)) }
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
73
+
74
+ root = File.dirname(conf['_path'] || '.')
75
+
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
@@ -78,9 +107,11 @@ module Flor
78
107
  #
79
108
  # call
80
109
 
81
- p = message['point']
82
- ms = [ "on_#{p}", :on_message, :on, p ]
83
- ms = ms + [ :on_cancel, :cancel ] if p == 'detask'
110
+ pt = message['point']
111
+
112
+ ms = [ "call_#{pt}", "on_#{pt}", :on_message, :on, pt ]
113
+ ms = ms + [ :on_cancel, :cancel ] if pt == 'detask'
114
+
84
115
  m = ms.find { |mm| o.respond_to?(mm) }
85
116
 
86
117
  fail(
@@ -92,7 +123,7 @@ module Flor
92
123
  case o.method(m).arity
93
124
  when 1 then o.send(m, message)
94
125
  when 2 then o.send(m, conf, message)
95
- when 3 then o.send(m, executor, conf, message)
126
+ when 3 then o.send(m, service, conf, message)
96
127
  when -1 then o.send(m, {
97
128
  service: service, configuration: conf, message: message })
98
129
  else o.send(m)
@@ -112,10 +143,8 @@ module Flor
112
143
  h['v'] = message['vars']
113
144
  h['tag'] = (message['tags'] || []).first
114
145
 
115
- cmd = h['cmd']
116
-
117
146
  m = encode(conf, message)
118
- out, _ = spawn(cmd, m)
147
+ out, _ = spawn(conf, m)
119
148
  r = decode(conf, out)
120
149
 
121
150
  to_messages(r)
@@ -143,7 +172,32 @@ module Flor
143
172
  .load(data)
144
173
  end
145
174
 
146
- def spawn(cmd, data)
175
+ def timeout(t, &block)
176
+
177
+ #Timeout.timeout(t, &block)
178
+ # avoid using Ruby Timeout :-( It was 2008, is that still relevant?
179
+
180
+ t0 = Time.now
181
+ th = Thread.new { Thread.current[:return] = block.call }
182
+ while (Time.now - t0 < t) do
183
+ break if th.key?(:return)
184
+ sleep 0.014
185
+ end
186
+
187
+ fail TimeoutError.new('execution expired') unless th.key?(:return)
188
+
189
+ th[:return]
190
+ end
191
+
192
+ def spawn(conf, data)
193
+
194
+ t0 = Time.now
195
+
196
+ cmd = conf['cmd']
197
+
198
+ to = Fugit.parse(conf['timeout'] || '14s')
199
+ to = to.is_a?(Fugit::Duration) ? to.to_sec : 14
200
+ to = 0 if to < 0 # no timeout
147
201
 
148
202
  i, o = IO.pipe # _ / stdout
149
203
  f, e = IO.pipe # _ / stderr
@@ -154,12 +208,24 @@ module Flor
154
208
  w.close
155
209
  o.close
156
210
  e.close
157
- _, status = Process.wait2(pid)
158
211
 
159
- fail SpawnError.new(status, i.read, f.read) if status.exitstatus != 0
212
+ _, status = timeout(to) { Process.wait2(pid) }
213
+
214
+ fail SpawnNonZeroExitError.new(conf, { to: to, t0: t0 }, status, i, f) \
215
+ if status.exitstatus != 0
160
216
 
161
217
  [ i.read, status ]
162
218
 
219
+ rescue => err
220
+
221
+ Process.detach(pid) \
222
+ if pid
223
+ (Process.kill(9, pid) rescue nil) \
224
+ unless Flor.no?(conf['on_error_kill'])
225
+
226
+ raise err if err.is_a?(SpawnError)
227
+ raise WrappedSpawnError.new(conf, { to: to, t0: t0, pid: pid }, err)
228
+
163
229
  ensure
164
230
 
165
231
  [ i, o, f, e, r, w ].each { |x| x.close rescue nil }
@@ -167,17 +233,84 @@ module Flor
167
233
 
168
234
  class SpawnError < StandardError
169
235
 
236
+ attr_accessor :conf, :ctx
237
+
238
+ def initialize(conf, ctx, msg)
239
+
240
+ @conf = conf
241
+ @ctx = ctx
242
+
243
+ super(msg)
244
+ end
245
+
246
+ def details
247
+
248
+ ha = Flor.yes?(@conf['on_error_hide_all'])
249
+ hcd = Flor.yes?(@conf['on_error_hide_cmd'])
250
+ hcf = Flor.yes?(@conf['on_error_hide_conf'])
251
+
252
+ cd = (ha || hcd) ? '(hidden)' : @conf['cmd']
253
+ cf = (ha || hcf) ? '(hidden)' : @conf.dup
254
+ cf['cmd'] = '(hidden)' if hcd && cf.is_a?(Hash)
255
+
256
+ d = {
257
+ cmd: cd, conf: cf,
258
+ timeout: ctx[:to],
259
+ pid: ctx[:pid],
260
+ start: Flor.tstamp(ctx[:t0]),
261
+ duration: Fugit.parse(Time.now - ctx[:t0]).to_plain_s,
262
+ cause: cause }
263
+
264
+ add_details(d) \
265
+ if respond_to?(:add_details)
266
+
267
+ d
268
+ end
269
+ end
270
+
271
+ class TimeoutError < StandardError; end
272
+
273
+ class WrappedSpawnError < SpawnError
274
+
275
+ attr_reader :cause
276
+
277
+ def initialize(conf, ctx, err)
278
+
279
+ @cause = err
280
+
281
+ super(conf, ctx, "wrapped: #{err.class.name}: #{err.message}")
282
+
283
+ set_backtrace(err.backtrace)
284
+ end
285
+
286
+ def add_details(details)
287
+
288
+ details[:cause] = { kla: @cause.class.name, msg: @cause.message }
289
+ end
290
+ end
291
+
292
+ class SpawnNonZeroExitError < SpawnError
293
+
170
294
  attr_reader :status, :out, :err
171
295
 
172
- def initialize(status, out, err)
296
+ def initialize(conf, ctx, status, stdout, stderr)
297
+
298
+ @status = s = status
299
+ @stdout = stdout.read
300
+ @stderr = stderr.read
173
301
 
174
- @status = status
175
- @out = out
176
- @err = err
302
+ ctx[:pid] ||= status.pid
177
303
 
178
- msg = err.strip.split("\n").last
304
+ msg = @stderr.strip.split("\n").last
179
305
 
180
- super("(code: #{status.exitstatus}, pid: #{status.pid}) #{msg}")
306
+ super(conf, ctx, "(code: #{s.exitstatus}, pid: #{s.pid}) #{msg}")
307
+ end
308
+
309
+ def add_details(details)
310
+
311
+ details[:status] = status.to_s
312
+ details[:stdout] = @stdout
313
+ details[:stderr] = @stderr
181
314
  end
182
315
  end
183
316
 
@@ -189,6 +322,8 @@ module Flor
189
322
  else []
190
323
  end
191
324
  end
325
+
326
+ require 'flor/unit/caller_jruby' if RUBY_PLATFORM.match(/java/)
192
327
  end
193
328
  end
194
329