nodo 1.6.5 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/nodo/core.rb +39 -39
- data/lib/nodo/version.rb +1 -1
- metadata +10 -11
- data/lib/nodo/nodo.js +0 -143
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 403cadfdfd344847d097f662540d78fd2db53099407de9391f7e453575a2dd21
|
4
|
+
data.tar.gz: 14cf5ec07e0dcf47556d1c606a632763614e17a6054ac818952eecd5112bbc7f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f14df956cfa21a1732b9a80df51826c32519b9cec0da7679f99846d08348b1d9985e774b6dd83f19eec1cebbb4c0ebf0d70f731f80c896d782b84ed4ec53b0a2
|
7
|
+
data.tar.gz: 03f7b885f5dca161dde7c9add5a46733b74d23d90c26180df30c4f75c4c3d2ec2fb6ccc000a651f0c1515a5e5d9b46e03fa73ed2aa4bf2f18ddfa257394ddaef
|
data/lib/nodo/core.rb
CHANGED
@@ -9,48 +9,48 @@ module Nodo
|
|
9
9
|
ARRAY_CLASS_ATTRIBUTES = %i[dependencies constants scripts].freeze
|
10
10
|
HASH_CLASS_ATTRIBUTES = %i[functions].freeze
|
11
11
|
CLASS_ATTRIBUTES = (ARRAY_CLASS_ATTRIBUTES + HASH_CLASS_ATTRIBUTES).freeze
|
12
|
-
|
12
|
+
|
13
13
|
@@node_pid = nil
|
14
14
|
@@tmpdir = nil
|
15
15
|
@@mutex = Mutex.new
|
16
16
|
@@exiting = nil
|
17
|
-
|
17
|
+
|
18
18
|
class << self
|
19
19
|
extend Forwardable
|
20
|
-
|
20
|
+
|
21
21
|
attr_accessor :class_defined
|
22
|
-
|
22
|
+
|
23
23
|
def inherited(subclass)
|
24
24
|
CLASS_ATTRIBUTES.each do |attr|
|
25
25
|
subclass.send "#{attr}=", send(attr).dup
|
26
26
|
end
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
def instance
|
30
30
|
@instance ||= new
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
def class_defined?
|
34
34
|
!!class_defined
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
def clsid
|
38
38
|
name || "Class:0x#{object_id.to_s(0x10)}"
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
CLASS_ATTRIBUTES.each do |attr|
|
42
42
|
define_method "#{attr}=" do |value|
|
43
43
|
instance_variable_set :"@#{attr}", value
|
44
44
|
end
|
45
45
|
protected "#{attr}="
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
ARRAY_CLASS_ATTRIBUTES.each do |attr|
|
49
49
|
define_method "#{attr}" do
|
50
50
|
instance_variable_get(:"@#{attr}") || instance_variable_set(:"@#{attr}", [])
|
51
51
|
end
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
54
|
HASH_CLASS_ATTRIBUTES.each do |attr|
|
55
55
|
define_method "#{attr}" do
|
56
56
|
instance_variable_get(:"@#{attr}") || instance_variable_set(:"@#{attr}", {})
|
@@ -60,15 +60,15 @@ module Nodo
|
|
60
60
|
def generate_core_code
|
61
61
|
<<~JS
|
62
62
|
global.nodo = require(#{nodo_js});
|
63
|
-
|
63
|
+
|
64
64
|
const socket = process.argv[1];
|
65
65
|
if (!socket) {
|
66
66
|
process.stderr.write('Socket path is required\\n');
|
67
67
|
process.exit(1);
|
68
68
|
}
|
69
|
-
|
69
|
+
|
70
70
|
process.title = `nodo-core ${socket}`;
|
71
|
-
|
71
|
+
|
72
72
|
const shutdown = () => {
|
73
73
|
nodo.core.close(() => { process.exit(0) });
|
74
74
|
};
|
@@ -92,9 +92,9 @@ module Nodo
|
|
92
92
|
})()
|
93
93
|
JS
|
94
94
|
end
|
95
|
-
|
95
|
+
|
96
96
|
protected
|
97
|
-
|
97
|
+
|
98
98
|
def finalize_context(context_id)
|
99
99
|
proc do
|
100
100
|
if not @@exiting and core = Nodo::Core.instance
|
@@ -102,9 +102,9 @@ module Nodo
|
|
102
102
|
end
|
103
103
|
end
|
104
104
|
end
|
105
|
-
|
105
|
+
|
106
106
|
private
|
107
|
-
|
107
|
+
|
108
108
|
def require(*mods)
|
109
109
|
deps = mods.last.is_a?(Hash) ? mods.pop : {}
|
110
110
|
mods = mods.map { |m| [m, m] }.to_h
|
@@ -118,7 +118,7 @@ module Nodo
|
|
118
118
|
self.functions = functions.merge(name => Function.new(name, _code || code, source_location, timeout, &block))
|
119
119
|
define_method(name) { |*args| call_js_method(name, args) }
|
120
120
|
end
|
121
|
-
|
121
|
+
|
122
122
|
def class_function(*methods)
|
123
123
|
singleton_class.def_delegators(:instance, *methods)
|
124
124
|
end
|
@@ -126,20 +126,20 @@ module Nodo
|
|
126
126
|
def const(name, value)
|
127
127
|
self.constants = constants + [Constant.new(name, value)]
|
128
128
|
end
|
129
|
-
|
129
|
+
|
130
130
|
def script(code = nil, &block)
|
131
131
|
self.scripts = scripts + [Script.new(code, &block)]
|
132
132
|
end
|
133
|
-
|
133
|
+
|
134
134
|
def nodo_js
|
135
|
-
Pathname.new(__FILE__).dirname.join('nodo.
|
135
|
+
Pathname.new(__FILE__).dirname.join('nodo.cjs').to_s.to_json
|
136
136
|
end
|
137
|
-
|
137
|
+
|
138
138
|
def reserved_method_name?(name)
|
139
139
|
Nodo::Core.method_defined?(name, false) || Nodo::Core.private_method_defined?(name, false) || name.to_s == DEFINE_METHOD
|
140
140
|
end
|
141
141
|
end
|
142
|
-
|
142
|
+
|
143
143
|
def initialize
|
144
144
|
raise ClassError, :new if self.class == Nodo::Core
|
145
145
|
@@mutex.synchronize do
|
@@ -148,18 +148,18 @@ module Nodo
|
|
148
148
|
ensure_class_is_defined
|
149
149
|
end
|
150
150
|
end
|
151
|
-
|
151
|
+
|
152
152
|
def evaluate(code)
|
153
153
|
ensure_context_is_defined
|
154
154
|
call_js_method(EVALUATE_METHOD, code)
|
155
155
|
end
|
156
|
-
|
156
|
+
|
157
157
|
private
|
158
|
-
|
158
|
+
|
159
159
|
def node_pid
|
160
160
|
@@node_pid
|
161
161
|
end
|
162
|
-
|
162
|
+
|
163
163
|
def tmpdir
|
164
164
|
@@tmpdir
|
165
165
|
end
|
@@ -167,33 +167,33 @@ module Nodo
|
|
167
167
|
def socket_path
|
168
168
|
tmpdir && tmpdir.join(SOCKET_NAME)
|
169
169
|
end
|
170
|
-
|
170
|
+
|
171
171
|
def clsid
|
172
172
|
self.class.clsid
|
173
173
|
end
|
174
|
-
|
174
|
+
|
175
175
|
def context_defined?
|
176
176
|
@context_defined
|
177
177
|
end
|
178
|
-
|
178
|
+
|
179
179
|
def log_exception(e)
|
180
180
|
return unless logger = Nodo.logger
|
181
181
|
message = "\n#{e.class} (#{e.message})"
|
182
182
|
message << ":\n\n#{e.backtrace.join("\n")}" if e.backtrace
|
183
183
|
logger.error message
|
184
184
|
end
|
185
|
-
|
185
|
+
|
186
186
|
def ensure_process_is_spawned
|
187
187
|
return if node_pid
|
188
188
|
spawn_process
|
189
189
|
end
|
190
|
-
|
190
|
+
|
191
191
|
def ensure_class_is_defined
|
192
192
|
return if self.class.class_defined?
|
193
193
|
call_js_method(DEFINE_METHOD, self.class.generate_class_code)
|
194
194
|
self.class.class_defined = true
|
195
195
|
end
|
196
|
-
|
196
|
+
|
197
197
|
def ensure_context_is_defined
|
198
198
|
return if context_defined?
|
199
199
|
@@mutex.synchronize do
|
@@ -202,7 +202,7 @@ module Nodo
|
|
202
202
|
@context_defined = true
|
203
203
|
end
|
204
204
|
end
|
205
|
-
|
205
|
+
|
206
206
|
def spawn_process
|
207
207
|
@@tmpdir = Pathname.new(Dir.mktmpdir('nodo'))
|
208
208
|
env = Nodo.env.merge('NODE_PATH' => Nodo.modules_root.to_s)
|
@@ -215,7 +215,7 @@ module Nodo
|
|
215
215
|
FileUtils.remove_entry(tmpdir) if File.directory?(tmpdir)
|
216
216
|
end
|
217
217
|
end
|
218
|
-
|
218
|
+
|
219
219
|
def wait_for_socket
|
220
220
|
start = Time.now
|
221
221
|
socket = nil
|
@@ -257,7 +257,7 @@ module Nodo
|
|
257
257
|
# TODO: restart or something? If this happens the process is completely broken
|
258
258
|
raise Error, 'Node process failed'
|
259
259
|
end
|
260
|
-
|
260
|
+
|
261
261
|
def handle_error(response, function)
|
262
262
|
if response.body
|
263
263
|
result = parse_response(response)
|
@@ -270,12 +270,12 @@ module Nodo
|
|
270
270
|
log_exception(error)
|
271
271
|
raise error
|
272
272
|
end
|
273
|
-
|
273
|
+
|
274
274
|
def parse_response(response)
|
275
275
|
data = response.body.force_encoding('UTF-8')
|
276
276
|
JSON.parse(data) unless data == ''
|
277
277
|
end
|
278
|
-
|
278
|
+
|
279
279
|
def with_tempfile(name)
|
280
280
|
ext = File.extname(name)
|
281
281
|
result = nil
|
@@ -284,6 +284,6 @@ module Nodo
|
|
284
284
|
end
|
285
285
|
result
|
286
286
|
end
|
287
|
-
|
287
|
+
|
288
288
|
end
|
289
289
|
end
|
data/lib/nodo/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nodo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthias Grosser
|
@@ -11,56 +11,56 @@ cert_chain: []
|
|
11
11
|
date: 2021-10-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
14
15
|
requirement: !ruby/object:Gem::Requirement
|
15
16
|
requirements:
|
16
17
|
- - ">="
|
17
18
|
- !ruby/object:Gem::Version
|
18
19
|
version: '0'
|
19
|
-
name: bundler
|
20
|
-
prerelease: false
|
21
20
|
type: :development
|
21
|
+
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
28
29
|
requirement: !ruby/object:Gem::Requirement
|
29
30
|
requirements:
|
30
31
|
- - ">="
|
31
32
|
- !ruby/object:Gem::Version
|
32
33
|
version: '0'
|
33
|
-
name: rake
|
34
|
-
prerelease: false
|
35
34
|
type: :development
|
35
|
+
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
+
name: byebug
|
42
43
|
requirement: !ruby/object:Gem::Requirement
|
43
44
|
requirements:
|
44
45
|
- - ">="
|
45
46
|
- !ruby/object:Gem::Version
|
46
47
|
version: '0'
|
47
|
-
name: byebug
|
48
|
-
prerelease: false
|
49
48
|
type: :development
|
49
|
+
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
56
57
|
requirement: !ruby/object:Gem::Requirement
|
57
58
|
requirements:
|
58
59
|
- - ">="
|
59
60
|
- !ruby/object:Gem::Version
|
60
61
|
version: '0'
|
61
|
-
name: minitest
|
62
|
-
prerelease: false
|
63
62
|
type: :development
|
63
|
+
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - ">="
|
@@ -82,7 +82,6 @@ files:
|
|
82
82
|
- lib/nodo/dependency.rb
|
83
83
|
- lib/nodo/errors.rb
|
84
84
|
- lib/nodo/function.rb
|
85
|
-
- lib/nodo/nodo.js
|
86
85
|
- lib/nodo/railtie.rb
|
87
86
|
- lib/nodo/script.rb
|
88
87
|
- lib/nodo/version.rb
|
@@ -105,7 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
105
104
|
- !ruby/object:Gem::Version
|
106
105
|
version: '0'
|
107
106
|
requirements: []
|
108
|
-
rubygems_version: 3.
|
107
|
+
rubygems_version: 3.5.11
|
109
108
|
signing_key:
|
110
109
|
specification_version: 4
|
111
110
|
summary: Call Node.js from Ruby
|
data/lib/nodo/nodo.js
DELETED
@@ -1,143 +0,0 @@
|
|
1
|
-
module.exports = (function() {
|
2
|
-
const DEFINE_METHOD = '__nodo_define_class__';
|
3
|
-
const EVALUATE_METHOD = '__nodo_evaluate__';
|
4
|
-
const GC_METHOD = '__nodo_gc__';
|
5
|
-
const DEBUG = process.env.NODO_DEBUG;
|
6
|
-
|
7
|
-
const vm = require('vm');
|
8
|
-
const http = require('http');
|
9
|
-
const path = require('path');
|
10
|
-
const fs = require('fs');
|
11
|
-
const performance = require('perf_hooks').performance;
|
12
|
-
|
13
|
-
let server, closing;
|
14
|
-
const classes = {};
|
15
|
-
const contexts = {};
|
16
|
-
|
17
|
-
function render_error(e) {
|
18
|
-
let errInfo = {};
|
19
|
-
if (e instanceof Error) {
|
20
|
-
errInfo.name = e.name;
|
21
|
-
Object.getOwnPropertyNames(e).reduce((obj, prop) => { obj[prop] = e[prop]; return obj }, errInfo);
|
22
|
-
} else {
|
23
|
-
errInfo.name = e.toString();
|
24
|
-
}
|
25
|
-
return JSON.stringify({ error: errInfo });
|
26
|
-
}
|
27
|
-
|
28
|
-
function respond_with_error(res, code, name) {
|
29
|
-
res.statusCode = code;
|
30
|
-
const rendered = render_error(name);
|
31
|
-
debug(`Error ${code} ${rendered}`);
|
32
|
-
res.end(rendered, 'utf8');
|
33
|
-
}
|
34
|
-
|
35
|
-
function respond_with_data(res, data, start) {
|
36
|
-
let timing;
|
37
|
-
res.statusCode = 200;
|
38
|
-
res.end(JSON.stringify(data), 'utf8');
|
39
|
-
if (start) {
|
40
|
-
timing = ` in ${(performance.now() - start).toFixed(2)}ms`;
|
41
|
-
}
|
42
|
-
debug(`Completed 200 OK${timing}\n`);
|
43
|
-
}
|
44
|
-
|
45
|
-
function debug(message) {
|
46
|
-
if (DEBUG) {
|
47
|
-
// fs.appendFileSync('log/nodo.log', `${message}\n`);
|
48
|
-
console.log(`[Nodo] ${message}`);
|
49
|
-
}
|
50
|
-
}
|
51
|
-
|
52
|
-
async function import_module(specifier) {
|
53
|
-
return await import(specifier);
|
54
|
-
}
|
55
|
-
|
56
|
-
const core = {
|
57
|
-
run: (socket) => {
|
58
|
-
debug('Starting up...');
|
59
|
-
server = http.createServer((req, res) => {
|
60
|
-
const start = performance.now();
|
61
|
-
|
62
|
-
res.setHeader('Content-Type', 'application/json');
|
63
|
-
debug(`${req.method} ${req.url}`);
|
64
|
-
|
65
|
-
if (req.method !== 'POST' || !req.url.startsWith('/')) {
|
66
|
-
return respond_with_error(res, 405, 'Method Not Allowed');
|
67
|
-
}
|
68
|
-
|
69
|
-
const url = req.url.substring(1);
|
70
|
-
const [class_name, object_id, method] = url.split('/');
|
71
|
-
let klass, context;
|
72
|
-
|
73
|
-
if (classes.hasOwnProperty(class_name)) {
|
74
|
-
klass = classes[class_name];
|
75
|
-
if (EVALUATE_METHOD == method) {
|
76
|
-
if (!contexts.hasOwnProperty(object_id)) {
|
77
|
-
contexts[object_id] = vm.createContext({ require: require, ...klass });
|
78
|
-
}
|
79
|
-
context = contexts[object_id];
|
80
|
-
} else if (!klass.hasOwnProperty(method) && !GC_METHOD == method) {
|
81
|
-
return respond_with_error(res, 404, `Method ${class_name}#${method} not found`);
|
82
|
-
}
|
83
|
-
} else if (DEFINE_METHOD != method) {
|
84
|
-
return respond_with_error(res, 404, `Class ${class_name} not defined`);
|
85
|
-
}
|
86
|
-
|
87
|
-
let body = '';
|
88
|
-
|
89
|
-
req.on('data', (data) => { body += data; });
|
90
|
-
|
91
|
-
req.on('end', () => {
|
92
|
-
let input, result;
|
93
|
-
|
94
|
-
try {
|
95
|
-
input = JSON.parse(body);
|
96
|
-
} catch (e) {
|
97
|
-
return respond_with_error(res, 400, 'Bad Request');
|
98
|
-
}
|
99
|
-
|
100
|
-
try {
|
101
|
-
if (DEFINE_METHOD == method) {
|
102
|
-
classes[class_name] = vm.runInThisContext(input, class_name);
|
103
|
-
respond_with_data(res, class_name, start);
|
104
|
-
} else if (EVALUATE_METHOD == method) {
|
105
|
-
Promise.resolve(vm.runInNewContext(input, context)).then((result) => {
|
106
|
-
respond_with_data(res, result, start);
|
107
|
-
}).catch((error) => {
|
108
|
-
respond_with_error(res, 500, error);
|
109
|
-
});
|
110
|
-
} else if (GC_METHOD == method) {
|
111
|
-
delete contexts[object_id];
|
112
|
-
respond_with_data(res, true, start);
|
113
|
-
} else {
|
114
|
-
Promise.resolve(klass[method].apply(null, input)).then((result) => {
|
115
|
-
respond_with_data(res, result, start);
|
116
|
-
}).catch((error) => {
|
117
|
-
respond_with_error(res, 500, error);
|
118
|
-
});
|
119
|
-
}
|
120
|
-
} catch(error) {
|
121
|
-
return respond_with_error(res, 500, error);
|
122
|
-
}
|
123
|
-
|
124
|
-
});
|
125
|
-
});
|
126
|
-
|
127
|
-
//server.maxConnections = 64;
|
128
|
-
server.listen(socket, () => {
|
129
|
-
debug(`server ready, listening on ${socket} (max connections: ${server.maxConnections})`);
|
130
|
-
});
|
131
|
-
},
|
132
|
-
|
133
|
-
close: (finalizer) => {
|
134
|
-
debug("Shutting down");
|
135
|
-
if (!closing) {
|
136
|
-
closing = true;
|
137
|
-
server.close(finalizer);
|
138
|
-
}
|
139
|
-
}
|
140
|
-
};
|
141
|
-
|
142
|
-
return { core: core, debug: debug, import: import_module };
|
143
|
-
})();
|