nodo 1.6.5 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nodo might be problematic. Click here for more details.
- 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
|
-
})();
|