flor 0.0.1 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +13 -0
- data/LICENSE.txt +1 -1
- data/Makefile +66 -0
- data/README.md +57 -0
- data/fail.txt +7 -0
- data/flor.gemspec +12 -9
- data/intercepted.txt +123 -0
- data/lib/flor/colours.rb +140 -0
- data/lib/flor/conf.rb +88 -0
- data/lib/flor/core/executor.rb +473 -0
- data/lib/flor/core/node.rb +397 -0
- data/lib/flor/core/procedure.rb +600 -0
- data/lib/flor/core/texecutor.rb +209 -0
- data/lib/flor/core.rb +93 -0
- data/lib/flor/dollar.rb +248 -0
- data/lib/flor/errors.rb +36 -0
- data/lib/flor/flor.rb +556 -0
- data/lib/flor/log.rb +336 -0
- data/lib/flor/migrations/0001_tables.rb +122 -0
- data/lib/flor/parser.rb +414 -0
- data/lib/flor/pcore/_arr.rb +49 -0
- data/lib/flor/pcore/_atom.rb +43 -0
- data/lib/flor/pcore/_att.rb +160 -0
- data/lib/flor/pcore/_dump.rb +60 -0
- data/lib/flor/pcore/_err.rb +30 -0
- data/lib/flor/pcore/_happly.rb +73 -0
- data/lib/flor/pcore/_obj.rb +65 -0
- data/lib/flor/pcore/_skip.rb +63 -0
- data/lib/flor/pcore/apply.rb +60 -0
- data/lib/flor/pcore/arith.rb +46 -0
- data/lib/flor/pcore/break.rb +71 -0
- data/lib/flor/pcore/cmp.rb +72 -0
- data/lib/flor/pcore/cond.rb +57 -0
- data/lib/flor/pcore/cursor.rb +223 -0
- data/lib/flor/pcore/define.rb +96 -0
- data/lib/flor/pcore/fail.rb +45 -0
- data/lib/flor/pcore/ife.rb +56 -0
- data/lib/flor/pcore/loop.rb +53 -0
- data/lib/flor/pcore/map.rb +75 -0
- data/lib/flor/pcore/match.rb +70 -0
- data/lib/flor/pcore/move.rb +65 -0
- data/lib/flor/pcore/noeval.rb +46 -0
- data/lib/flor/pcore/noret.rb +47 -0
- data/lib/flor/pcore/push.rb +69 -0
- data/lib/flor/pcore/sequence.rb +39 -0
- data/lib/flor/pcore/set.rb +76 -0
- data/lib/flor/pcore/stall.rb +35 -0
- data/lib/flor/pcore/until.rb +122 -0
- data/lib/flor/pcore/val.rb +40 -0
- data/lib/flor/punit/cancel.rb +69 -0
- data/lib/flor/punit/cmap.rb +76 -0
- data/lib/flor/punit/concurrence.rb +149 -0
- data/lib/flor/punit/every.rb +46 -0
- data/lib/flor/punit/on.rb +81 -0
- data/lib/flor/punit/schedule.rb +68 -0
- data/lib/flor/punit/signal.rb +47 -0
- data/lib/flor/punit/sleep.rb +53 -0
- data/lib/flor/punit/task.rb +109 -0
- data/lib/flor/punit/trace.rb +51 -0
- data/lib/flor/punit/trap.rb +100 -0
- data/lib/flor/to_string.rb +81 -0
- data/lib/flor/tools/env.rb +103 -0
- data/lib/flor/tools/repl.rb +231 -0
- data/lib/flor/unit/executor.rb +260 -0
- data/lib/flor/unit/hooker.rb +186 -0
- data/lib/flor/unit/journal.rb +52 -0
- data/lib/flor/unit/loader.rb +181 -0
- data/lib/flor/unit/logger.rb +181 -0
- data/lib/flor/unit/models/execution.rb +105 -0
- data/lib/flor/unit/models/pointer.rb +31 -0
- data/lib/flor/unit/models/timer.rb +52 -0
- data/lib/flor/unit/models/trace.rb +31 -0
- data/lib/flor/unit/models/trap.rb +130 -0
- data/lib/flor/unit/models.rb +106 -0
- data/lib/flor/unit/scheduler.rb +419 -0
- data/lib/flor/unit/storage.rb +633 -0
- data/lib/flor/unit/tasker.rb +191 -0
- data/lib/flor/unit/waiter.rb +146 -0
- data/lib/flor/unit/wlist.rb +77 -0
- data/lib/flor/unit.rb +50 -0
- data/lib/flor.rb +40 -3
- metadata +152 -22
- checksums.yaml +0 -7
- data/Rakefile +0 -52
@@ -0,0 +1,81 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2015-2017, John Mettraux, jmettraux+flor@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 Flor
|
27
|
+
|
28
|
+
def self.to_s(o=nil, k=nil)
|
29
|
+
|
30
|
+
return 'FlorModule' if o == nil && k == nil
|
31
|
+
# should it emerge somewhere...
|
32
|
+
|
33
|
+
return o.collect { |e| Flor.to_s(e, k) }.join("\n") if o.is_a?(Array)
|
34
|
+
|
35
|
+
if o.is_a?(Hash)
|
36
|
+
|
37
|
+
return send("message_#{k}_to_s", o) if k && o['point'].is_a?(String)
|
38
|
+
return message_to_s(o) if o['point'].is_a?(String)
|
39
|
+
|
40
|
+
return send("node_#{k}_to_s", o) if k && o.has_key?('parent')
|
41
|
+
return node_to_s(o) if o['parent'].is_a?(String)
|
42
|
+
end
|
43
|
+
|
44
|
+
return [ o, k ].inspect if k
|
45
|
+
o.inspect
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.message_to_s(m)
|
49
|
+
|
50
|
+
s = StringIO.new
|
51
|
+
s << '(msg ' << m['nid'] << ' ' << m['point']
|
52
|
+
%w[ from flavour ].each { |k|
|
53
|
+
s << ' ' << k << ':' << m[k].to_s if m.has_key?(k) }
|
54
|
+
s << ')'
|
55
|
+
|
56
|
+
s.string
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.node_status_to_s(n)
|
60
|
+
|
61
|
+
s = StringIO.new
|
62
|
+
stas = n['status'].reverse
|
63
|
+
while sta = stas.shift
|
64
|
+
s << '(status ' << (sta['status'] || 'o') # o for open
|
65
|
+
s << ' pt:' << sta['point']
|
66
|
+
if f = sta['flavour']; s << ' fla:' << f; end
|
67
|
+
if f = sta['from']; s << ' fro:' << f; end
|
68
|
+
if m = sta['m']; s << ' m:' << m; end
|
69
|
+
s << ')'
|
70
|
+
s << "\n" if stas.any?
|
71
|
+
end
|
72
|
+
|
73
|
+
s.string
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.node_to_s(n) # there is already a .node_to_s in log.rb
|
77
|
+
|
78
|
+
n.inspect
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
@@ -0,0 +1,103 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2015-2017, John Mettraux, jmettraux+flor@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
|
+
require 'fileutils'
|
26
|
+
|
27
|
+
|
28
|
+
module Flor; end
|
29
|
+
module Flor::Tools; end
|
30
|
+
|
31
|
+
module Flor::Tools::Env
|
32
|
+
|
33
|
+
def self.make(path, envname=nil, opts={})
|
34
|
+
|
35
|
+
if envname.is_a?(Hash)
|
36
|
+
opts = envname
|
37
|
+
envname = nil
|
38
|
+
end
|
39
|
+
|
40
|
+
opts[:env] = envname || 'production'
|
41
|
+
opts[:sto_uri] ||= 'sqlite://tmp/dev.db'
|
42
|
+
opts[:gitkeep] = true unless opts.has_key?(:gitkeep)
|
43
|
+
|
44
|
+
mkdir(path, envname) if envname
|
45
|
+
|
46
|
+
mk_etc(path, envname, opts)
|
47
|
+
mk_lib(path, envname, opts)
|
48
|
+
mk_usr(path, envname, opts)
|
49
|
+
mk_var(path, envname, opts)
|
50
|
+
end
|
51
|
+
|
52
|
+
# protected, somehow
|
53
|
+
|
54
|
+
def self.mk_etc(*ps, opts)
|
55
|
+
|
56
|
+
mkdir(*ps, 'etc')
|
57
|
+
mkdir(*ps, 'etc', 'variables')
|
58
|
+
touch(*ps, 'etc', 'variables', '.gitkeep') if opts[:gitkeep]
|
59
|
+
|
60
|
+
write(*ps, 'etc', 'conf.json') do
|
61
|
+
"env: #{opts[:env]}\n" +
|
62
|
+
"sto_uri: #{opts[:sto_uri].inspect}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.mk_lib(*ps, opts)
|
67
|
+
|
68
|
+
mkdir(*ps, 'lib')
|
69
|
+
mkdir(*ps, 'lib', 'flows')
|
70
|
+
touch(*ps, 'lib', 'flows', '.gitkeep') if opts[:gitkeep]
|
71
|
+
mkdir(*ps, 'lib', 'taskers')
|
72
|
+
touch(*ps, 'lib', 'taskers', '.gitkeep') if opts[:gitkeep]
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.mk_usr(*ps, opts)
|
76
|
+
|
77
|
+
mkdir(*ps, 'usr')
|
78
|
+
|
79
|
+
if opts[:acme] == false
|
80
|
+
touch(*ps, 'usr', '.gitkeep') if opts[:gitkeep]
|
81
|
+
else
|
82
|
+
mkdir(*ps, 'usr', 'com.acme')
|
83
|
+
mk_etc(*ps, 'usr', 'com.acme', opts)
|
84
|
+
mk_lib(*ps, 'usr', 'com.acme', opts)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.mk_var(*ps, opts)
|
89
|
+
|
90
|
+
mkdir(*ps, 'var')
|
91
|
+
mkdir(*ps, 'var', 'log')
|
92
|
+
touch(*ps, 'var', 'log', '.gitkeep') if opts[:gitkeep]
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.mkdir(*ps); FileUtils.mkdir(File.join(*ps.compact)); end
|
96
|
+
def self.touch(*ps); FileUtils.touch(File.join(*ps.compact)); end
|
97
|
+
|
98
|
+
def self.write(*ps, &block)
|
99
|
+
|
100
|
+
File.open(File.join(*ps.compact), 'wb') { |f| f.write(block.call) }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
@@ -0,0 +1,231 @@
|
|
1
|
+
|
2
|
+
require 'flor'
|
3
|
+
require 'flor/unit'
|
4
|
+
|
5
|
+
|
6
|
+
module Flor::Tools
|
7
|
+
|
8
|
+
class Repl
|
9
|
+
|
10
|
+
def initialize(env)
|
11
|
+
|
12
|
+
@unit = Flor::Unit.new("envs/#{env}/etc/conf.json")
|
13
|
+
|
14
|
+
#pp @unit.conf
|
15
|
+
@unit.conf[:unit] = 'repl'
|
16
|
+
|
17
|
+
#unit.hooker.add('journal', Flor::Journal)
|
18
|
+
if @unit.conf['sto_uri'].match(/memory/)
|
19
|
+
@unit.storage.delete_tables
|
20
|
+
@unit.storage.migrate
|
21
|
+
end
|
22
|
+
@unit.start
|
23
|
+
|
24
|
+
@lines = []
|
25
|
+
@payload = {}
|
26
|
+
@vars = {}
|
27
|
+
|
28
|
+
@outcome = nil
|
29
|
+
|
30
|
+
@unit.hook do |message|
|
31
|
+
|
32
|
+
if ! message['consumed']
|
33
|
+
# do nothing
|
34
|
+
elsif %w[ terminated failed ].include?(message['point'])
|
35
|
+
@outcome = message
|
36
|
+
out = Flor.to_pretty_s(@outcome)
|
37
|
+
col = message['point'] == 'failed' ? _rd : _gr
|
38
|
+
out = out.gsub(/"point"=>"([^"]+)"/, "\"point\"=>\"#{col}\\1#{_yl}\"")
|
39
|
+
out = "\n" + _yl + out + _rs
|
40
|
+
out = out.split("\n").collect { |l| ' ' + l }.join("\n")
|
41
|
+
print(out)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
@_c, @_dg, @_yl, @_bl, @_lg, @_gr, @_lr, @_rd = Flor.colours({})
|
46
|
+
|
47
|
+
do_loop
|
48
|
+
end
|
49
|
+
|
50
|
+
# reset dark_grey light_yellow blue light_grey light_green light_red red
|
51
|
+
attr_reader :_rs, :_dg, :_yl, :_bl, :_lg, :_gr, :_lr, :_rd
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
def prompt
|
56
|
+
|
57
|
+
"flor l#{@lines.size} > "
|
58
|
+
end
|
59
|
+
|
60
|
+
def do_loop
|
61
|
+
|
62
|
+
loop do
|
63
|
+
|
64
|
+
line = prompt_and_read
|
65
|
+
|
66
|
+
break unless line
|
67
|
+
next if line.strip == ''
|
68
|
+
|
69
|
+
cmd = "cmd_#{line.split(/\s/).first}".to_sym
|
70
|
+
|
71
|
+
if cmd.size > 4 && methods.include?(cmd)
|
72
|
+
begin
|
73
|
+
send(cmd, line)
|
74
|
+
rescue StandardError, NotImplementedError => err
|
75
|
+
p err
|
76
|
+
err.backtrace[0, 7].each { |l| puts " #{l}" }
|
77
|
+
end
|
78
|
+
else
|
79
|
+
@lines << line
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
$stdout.puts
|
84
|
+
end
|
85
|
+
|
86
|
+
def hlp_launch
|
87
|
+
%{ launches the current execution code }
|
88
|
+
end
|
89
|
+
def cmd_launch(line)
|
90
|
+
|
91
|
+
exid = @unit.launch(@lines.join("\n"), vars: @vars, payload: @payload)
|
92
|
+
puts " launched #{_yl}#{exid}#{_rs}"
|
93
|
+
end
|
94
|
+
|
95
|
+
alias hlp_run hlp_launch
|
96
|
+
alias cmd_run cmd_launch
|
97
|
+
|
98
|
+
def hlp_help
|
99
|
+
%{ displays this help }
|
100
|
+
end
|
101
|
+
def cmd_help(line)
|
102
|
+
|
103
|
+
puts
|
104
|
+
puts "## available commands:"
|
105
|
+
puts
|
106
|
+
COMMANDS.each do |cmd|
|
107
|
+
print "* #{cmd}"
|
108
|
+
if hlp = (send("hlp_#{cmd}") rescue nil); print " - #{hlp.strip}"; end
|
109
|
+
puts
|
110
|
+
end
|
111
|
+
puts
|
112
|
+
end
|
113
|
+
|
114
|
+
def hlp_exit
|
115
|
+
%{ exits this repl, with the given int exit code or 0 }
|
116
|
+
end
|
117
|
+
def cmd_exit(line)
|
118
|
+
|
119
|
+
exit(line.split(/\s+/)[1].to_i)
|
120
|
+
end
|
121
|
+
|
122
|
+
def hlp_list
|
123
|
+
%{ lists the lines of the current execution code }
|
124
|
+
end
|
125
|
+
def do_list(lines)
|
126
|
+
|
127
|
+
lw = [ 2, lines.size.to_s.length ].max
|
128
|
+
sw = 5 - lw
|
129
|
+
|
130
|
+
lines.each_with_index do |l, i|
|
131
|
+
puts "#{_dg}% #{sw}s%0#{lw}i #{_yl}%s#{_rs}" % [ '', i + 1, l ]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
def cmd_list(line)
|
135
|
+
|
136
|
+
do_list(@lines)
|
137
|
+
end
|
138
|
+
|
139
|
+
def hlp_parse
|
140
|
+
%{ parses the current execution code and displays its tree }
|
141
|
+
end
|
142
|
+
def cmd_parse(line)
|
143
|
+
|
144
|
+
Flor.print_tree(
|
145
|
+
Flor::Lang.parse(@lines.join("\n"), nil, {}),
|
146
|
+
'0',
|
147
|
+
headers: false)
|
148
|
+
end
|
149
|
+
|
150
|
+
def hlp_new
|
151
|
+
%w{ erases current execution code, vars and payload }
|
152
|
+
end
|
153
|
+
def cmd_new(line)
|
154
|
+
|
155
|
+
@lines = []
|
156
|
+
@vars = {}
|
157
|
+
@payload = {}
|
158
|
+
end
|
159
|
+
|
160
|
+
def fname(line)
|
161
|
+
|
162
|
+
line.split(/\s+/)[1]
|
163
|
+
end
|
164
|
+
|
165
|
+
def hlp_save
|
166
|
+
%{ saves the current execution code to the given file }
|
167
|
+
end
|
168
|
+
def cmd_save(line)
|
169
|
+
|
170
|
+
File.open(fname(line), 'wb') { |f| f.puts @lines }
|
171
|
+
end
|
172
|
+
|
173
|
+
def hlp_cat
|
174
|
+
%{ outputs the content of the give file }
|
175
|
+
end
|
176
|
+
def cmd_cat(line)
|
177
|
+
|
178
|
+
do_list(File.readlines(fname(line)).collect(&:chomp))
|
179
|
+
end
|
180
|
+
|
181
|
+
def hlp_load
|
182
|
+
%{ loads a file as execution code }
|
183
|
+
end
|
184
|
+
def cmd_load(line)
|
185
|
+
|
186
|
+
@lines = File.readlines(fname(line)).collect(&:chomp)
|
187
|
+
end
|
188
|
+
|
189
|
+
def cmd_cont(line)
|
190
|
+
|
191
|
+
fail NotImplementedError
|
192
|
+
end
|
193
|
+
|
194
|
+
def hlp_edit
|
195
|
+
%{ saves the current execution code to .tmp.flo and opens it for edition }
|
196
|
+
end
|
197
|
+
def cmd_edit(line)
|
198
|
+
|
199
|
+
cmd_save('save .tmp.flo')
|
200
|
+
system('$EDITOR .tmp.flo')
|
201
|
+
cmd_load('load .tmp.flo')
|
202
|
+
FileUtils.rm('.tmp.flo')
|
203
|
+
end
|
204
|
+
|
205
|
+
#
|
206
|
+
# use Readline if possible
|
207
|
+
|
208
|
+
COMMANDS = self.allocate.methods \
|
209
|
+
.select { |m| m.to_s.match(/^cmd_/) }.collect { |m| m[4..-1] }.sort
|
210
|
+
|
211
|
+
begin
|
212
|
+
require 'readline'
|
213
|
+
def prompt_and_read
|
214
|
+
Readline.readline(prompt, true)
|
215
|
+
end
|
216
|
+
Readline.completion_proc =
|
217
|
+
proc { |s|
|
218
|
+
r = /^#{Regexp.escape(s)}/
|
219
|
+
COMMANDS.grep(r) + Dir["#{s}*"].grep(r)
|
220
|
+
}
|
221
|
+
#Readline.completion_append_character =
|
222
|
+
# " "
|
223
|
+
rescue LoadError => le
|
224
|
+
def prompt_and_read
|
225
|
+
print(prompt)
|
226
|
+
($stdin.readline rescue false)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
@@ -0,0 +1,260 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2015-2017, John Mettraux, jmettraux+flor@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 Flor
|
27
|
+
|
28
|
+
class UnitExecutor < Flor::Executor
|
29
|
+
|
30
|
+
attr_reader :exid
|
31
|
+
|
32
|
+
def initialize(unit, exid)
|
33
|
+
|
34
|
+
super(
|
35
|
+
unit,
|
36
|
+
unit.storage.fetch_traps(exid),
|
37
|
+
unit.storage.load_execution(exid))
|
38
|
+
|
39
|
+
@exid = exid
|
40
|
+
@messages = unit.storage.fetch_messages(exid)
|
41
|
+
@consumed = []
|
42
|
+
|
43
|
+
@alive = true
|
44
|
+
@shutdown = false
|
45
|
+
|
46
|
+
@thread = nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def alive?; @alive; end
|
50
|
+
|
51
|
+
def run
|
52
|
+
|
53
|
+
@thread = Thread.new { do_run }
|
54
|
+
#p [ :unit_executor, :thread, @thread.object_id ]
|
55
|
+
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def shutdown
|
60
|
+
|
61
|
+
@shutdown = true
|
62
|
+
@thread.join
|
63
|
+
end
|
64
|
+
|
65
|
+
protected
|
66
|
+
|
67
|
+
def do_run
|
68
|
+
|
69
|
+
log_run_start if @unit.conf['log_run']
|
70
|
+
|
71
|
+
counter_next('runs')
|
72
|
+
|
73
|
+
t0 = Time.now
|
74
|
+
|
75
|
+
(@unit.conf['exe_max_messages'] || 77).times do |i|
|
76
|
+
|
77
|
+
m = @messages.shift
|
78
|
+
break unless m
|
79
|
+
break if @shutdown
|
80
|
+
|
81
|
+
if m['point'] == 'terminated' && @messages.any?
|
82
|
+
#
|
83
|
+
# try to handle 'terminated' last
|
84
|
+
#
|
85
|
+
@messages << m
|
86
|
+
m = @messages.shift
|
87
|
+
end
|
88
|
+
|
89
|
+
point = m['point']
|
90
|
+
|
91
|
+
ms = process(m)
|
92
|
+
|
93
|
+
@consumed << m
|
94
|
+
#
|
95
|
+
#@consumed << m unless ms.include?(m)
|
96
|
+
# TODO what if msg is held / pushed back?
|
97
|
+
|
98
|
+
ims, oms = ms.partition { |m| m['exid'] == @exid }
|
99
|
+
# qui est "in", qui est "out"?
|
100
|
+
|
101
|
+
counter_add('omsgs', oms.size)
|
102
|
+
# keep track of "out" messages, messages to other executions
|
103
|
+
|
104
|
+
@messages.concat(ims)
|
105
|
+
@unit.storage.put_messages(oms)
|
106
|
+
end
|
107
|
+
|
108
|
+
@unit.storage.consume(@consumed)
|
109
|
+
|
110
|
+
@alive = false
|
111
|
+
|
112
|
+
@unit.storage.put_execution(@execution)
|
113
|
+
@unit.storage.put_messages(@messages)
|
114
|
+
|
115
|
+
log_run_end(t0) if @unit.conf['log_run']
|
116
|
+
|
117
|
+
@consumed.clear
|
118
|
+
|
119
|
+
rescue Exception => exc
|
120
|
+
|
121
|
+
# TODO eventually, have a dump dir
|
122
|
+
fn =
|
123
|
+
[
|
124
|
+
'flor',
|
125
|
+
@unit.conf['env'], @unit.identifier, @exid,
|
126
|
+
'r' + counter('runs').to_s
|
127
|
+
].collect(&:to_s).join('_') + '.dump'
|
128
|
+
|
129
|
+
@unit.logger.error(
|
130
|
+
"#{self.class}#do_run()", exc, "(dumping to #{fn})")
|
131
|
+
|
132
|
+
File.open(fn, 'wb') do |f|
|
133
|
+
#f.puts(JSON.pretty_generate({
|
134
|
+
f.puts(Flor.to_pretty_s({
|
135
|
+
execution: @execution,
|
136
|
+
messages: @messages,
|
137
|
+
consumed: @consumed,
|
138
|
+
traps: @traps.collect(&:to_h),
|
139
|
+
exid: @exid,
|
140
|
+
alive: @alive,
|
141
|
+
shutdown: @shutdown,
|
142
|
+
thread: [ @thread.object_id, @thread.to_s ]
|
143
|
+
}))
|
144
|
+
f.puts('-' * 80)
|
145
|
+
f.puts(on_do_run_exc(exc))
|
146
|
+
end
|
147
|
+
|
148
|
+
puts on_do_run_exc(exc)
|
149
|
+
end
|
150
|
+
|
151
|
+
def schedule(message)
|
152
|
+
|
153
|
+
@unit.put_timer(message)
|
154
|
+
|
155
|
+
[]
|
156
|
+
end
|
157
|
+
|
158
|
+
def trigger(message)
|
159
|
+
|
160
|
+
m = message['message']
|
161
|
+
|
162
|
+
m['nid'] = Flor.sub_nid(m['nid'], counter_next('subs')) \
|
163
|
+
if m['point'] == 'execute'
|
164
|
+
|
165
|
+
[ m ]
|
166
|
+
end
|
167
|
+
|
168
|
+
def log_run_start
|
169
|
+
|
170
|
+
_c = Flor.colours
|
171
|
+
|
172
|
+
s = StringIO.new
|
173
|
+
|
174
|
+
s << _c.dg
|
175
|
+
s << " /--- #{_c.lg}run starts#{_c.dg} "
|
176
|
+
s << "#{self.class} #{self.object_id} #{@exid}"
|
177
|
+
s << "\n | "
|
178
|
+
s << { thread: Thread.current.object_id }.inspect
|
179
|
+
s << "\n | "
|
180
|
+
s << {
|
181
|
+
counters: @execution['counters'],
|
182
|
+
nodes: @execution['nodes'].size,
|
183
|
+
size: @execution['size']
|
184
|
+
}.inspect
|
185
|
+
s << _c.rs
|
186
|
+
|
187
|
+
puts s.string
|
188
|
+
end
|
189
|
+
|
190
|
+
def log_run_end(t0)
|
191
|
+
|
192
|
+
_c = Flor.colours
|
193
|
+
|
194
|
+
s = StringIO.new
|
195
|
+
|
196
|
+
s << _c.dg
|
197
|
+
s << " | run ends #{self.class} #{self.object_id} #{@exid}"
|
198
|
+
s << "\n | "; s << { took: Time.now - t0 }.inspect
|
199
|
+
s << "\n | "; s << {
|
200
|
+
thread: Thread.current.object_id,
|
201
|
+
consumed: @consumed.size,
|
202
|
+
traps: @traps.size,
|
203
|
+
}.inspect
|
204
|
+
s << "\n | "; s << {
|
205
|
+
#own_traps: @traps.reject { |t| t.texid == nil }.size, # FIXME
|
206
|
+
counters: @execution['counters'],
|
207
|
+
nodes: @execution['nodes'].size,
|
208
|
+
size: @execution['size']
|
209
|
+
}.inspect
|
210
|
+
if @unit.archive
|
211
|
+
s << "\n | "
|
212
|
+
s << {
|
213
|
+
archive_size: @unit.archive[@exid].size
|
214
|
+
}.inspect
|
215
|
+
end
|
216
|
+
s << "\n \\--- ."
|
217
|
+
s << _c.rs
|
218
|
+
|
219
|
+
puts s.string
|
220
|
+
end
|
221
|
+
|
222
|
+
def on_do_run_exc(e)
|
223
|
+
|
224
|
+
io = StringIO.new
|
225
|
+
thr = Thread.current
|
226
|
+
|
227
|
+
head, kind =
|
228
|
+
e.is_a?(StandardError) ? [ '=exe', 'error' ] : [ '!exe', 'exception' ]
|
229
|
+
|
230
|
+
t = head[0, 2] + Time.now.to_f.to_s.split('.').last
|
231
|
+
io.puts ' /' + t + ' ' + head * 17
|
232
|
+
io.puts " |#{t} + in #{self.class}#do_run"
|
233
|
+
io.puts " |#{t} #{kind}: #{e.inspect}"
|
234
|
+
io.puts " |#{t} db: #{@unit.storage.db.class} #{@unit.storage.db.hash}"
|
235
|
+
io.puts " |#{t} thread: t#{thr.object_id} #{thr.inspect}"
|
236
|
+
if @execution
|
237
|
+
io.puts " |#{t} exe:"
|
238
|
+
io.puts " |#{t} exid: #{@execution['exid'].inspect}"
|
239
|
+
io.puts " |#{t} counters: #{@execution['counters'].inspect}"
|
240
|
+
end
|
241
|
+
if @messages
|
242
|
+
io.puts " |#{t} messages:"
|
243
|
+
io.puts " |#{t} #{@messages.map { |m| [ m['mid'], m['point'] ] }.inspect}"
|
244
|
+
end
|
245
|
+
if is = Thread.current[:sto_errored_items]
|
246
|
+
io.puts " |#{t} storage errored items/models:"
|
247
|
+
is.each do |i|
|
248
|
+
io.puts " |#{t} * #{i.inspect}"
|
249
|
+
end
|
250
|
+
end
|
251
|
+
io.puts " |#{t} #{kind}: #{e.inspect}"
|
252
|
+
io.puts " |#{t} backtrace:"
|
253
|
+
e.backtrace.each { |l| io.puts "|#{t} #{l}" }
|
254
|
+
io.puts ' \\' + t + ' ' + (head * 17) + ' .'
|
255
|
+
|
256
|
+
io.string
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|