nodo 1.6.5 → 1.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 85e8ece889660e457a3473919da402124d9688011c3dbe5f69015e9f5e258465
4
- data.tar.gz: 32ed8d2c1905c22cc68cf2734272f0d743890be066add8ce8de1a72132c6c3f4
3
+ metadata.gz: e7c1002efcf0b7ba146dde5e10af7ddcdfdfeb450824dfaf5cce56c115630303
4
+ data.tar.gz: 3dc41a17980258b0ef634bb6a05319cc98501495ebb7a4688007cd0546f32fb3
5
5
  SHA512:
6
- metadata.gz: 1c6a6af11b36730dae8bc00d31a17a96f0fbe0fe3a5ddc3da5df636efcdc648a6890e37aee63c7f0b67fc000ce354247eaa61cde12557eb9a3126b6cd0d9a291
7
- data.tar.gz: b12074e9a1312694fcb46246eb2d66d418054b3f518be6dc5b2469a908b8b7e35d55d4a30b77144005b2b898c193ed062473ee4916239022c9d32361a5e324c4
6
+ metadata.gz: 877eac53f4eb526c8fc8ef7cc7b10437232a9b50effffd894dda29c36129b33c7a2c9e9edb47964ad2b566ce98c25f38d2866ce0cd0fc52062efbe0084105f31
7
+ data.tar.gz: 5aaf9f8097c8a3b7335e1a67b23dc388a194b2dfe395b8b8e0faeca324b194615d1b946fd3a6bf117b0c0d2567c71cae622b00611d772e95f8f35e1e1f519f4a
data/README.md CHANGED
@@ -99,6 +99,23 @@ bar = Bar.new
99
99
  bar.v4 => "b305f5c4-db9a-4504-b0c3-4e097a5ec8b9"
100
100
  ```
101
101
 
102
+ `import` is also supported for loading ESM packages:
103
+
104
+ ```ruby
105
+ class Bar < Nodo::Core
106
+ import :uuid
107
+
108
+ function :v4, <<~JS
109
+ () => {
110
+ return uuid.v4();
111
+ }
112
+ JS
113
+ end
114
+
115
+ bar = Bar.new
116
+ bar.v4 => "b305f5c4-db9a-4504-b0c3-4e097a5ec8b9"
117
+ ```
118
+
102
119
  ### Aliasing requires
103
120
 
104
121
  If the library name cannot be used as name of the constant, the `const` name
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
  };
@@ -82,7 +82,7 @@ module Nodo
82
82
 
83
83
  def generate_class_code
84
84
  <<~JS
85
- (() => {
85
+ (async () => {
86
86
  const __nodo_klass__ = { nodo: global.nodo };
87
87
  #{dependencies.map(&:to_js).join}
88
88
  #{constants.map(&:to_js).join}
@@ -92,23 +92,27 @@ 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
- if not @@exiting and core = Nodo::Core.instance
100
+ if not @@exiting and core = @instance
101
+ puts "finalize_context #{context_id}"
101
102
  core.send(:call_js_method, GC_METHOD, context_id)
102
103
  end
103
104
  end
104
105
  end
105
-
106
+
106
107
  private
107
-
108
- def require(*mods)
109
- deps = mods.last.is_a?(Hash) ? mods.pop : {}
110
- mods = mods.map { |m| [m, m] }.to_h
111
- self.dependencies = dependencies + mods.merge(deps).map { |name, package| Dependency.new(name, package) }
108
+
109
+ { require: :cjs, import: :esm }.each do |method, type|
110
+ define_method method do |*mods|
111
+ deps = mods.last.is_a?(Hash) ? mods.pop : {}
112
+ mods = mods.map { |m| [m, m] }.to_h
113
+ self.dependencies = dependencies + mods.merge(deps).map { |name, package| Dependency.new(name, package, type: type) }
114
+ end
115
+ private method
112
116
  end
113
117
 
114
118
  def function(name, _code = nil, timeout: Nodo.timeout, code: nil, &block)
@@ -118,7 +122,7 @@ module Nodo
118
122
  self.functions = functions.merge(name => Function.new(name, _code || code, source_location, timeout, &block))
119
123
  define_method(name) { |*args| call_js_method(name, args) }
120
124
  end
121
-
125
+
122
126
  def class_function(*methods)
123
127
  singleton_class.def_delegators(:instance, *methods)
124
128
  end
@@ -126,20 +130,20 @@ module Nodo
126
130
  def const(name, value)
127
131
  self.constants = constants + [Constant.new(name, value)]
128
132
  end
129
-
133
+
130
134
  def script(code = nil, &block)
131
135
  self.scripts = scripts + [Script.new(code, &block)]
132
136
  end
133
-
137
+
134
138
  def nodo_js
135
- Pathname.new(__FILE__).dirname.join('nodo.js').to_s.to_json
139
+ Pathname.new(__FILE__).dirname.join('nodo.cjs').to_s.to_json
136
140
  end
137
-
141
+
138
142
  def reserved_method_name?(name)
139
143
  Nodo::Core.method_defined?(name, false) || Nodo::Core.private_method_defined?(name, false) || name.to_s == DEFINE_METHOD
140
144
  end
141
145
  end
142
-
146
+
143
147
  def initialize
144
148
  raise ClassError, :new if self.class == Nodo::Core
145
149
  @@mutex.synchronize do
@@ -148,18 +152,18 @@ module Nodo
148
152
  ensure_class_is_defined
149
153
  end
150
154
  end
151
-
155
+
152
156
  def evaluate(code)
153
157
  ensure_context_is_defined
154
158
  call_js_method(EVALUATE_METHOD, code)
155
159
  end
156
-
160
+
157
161
  private
158
-
162
+
159
163
  def node_pid
160
164
  @@node_pid
161
165
  end
162
-
166
+
163
167
  def tmpdir
164
168
  @@tmpdir
165
169
  end
@@ -167,33 +171,33 @@ module Nodo
167
171
  def socket_path
168
172
  tmpdir && tmpdir.join(SOCKET_NAME)
169
173
  end
170
-
174
+
171
175
  def clsid
172
176
  self.class.clsid
173
177
  end
174
-
178
+
175
179
  def context_defined?
176
180
  @context_defined
177
181
  end
178
-
182
+
179
183
  def log_exception(e)
180
184
  return unless logger = Nodo.logger
181
185
  message = "\n#{e.class} (#{e.message})"
182
186
  message << ":\n\n#{e.backtrace.join("\n")}" if e.backtrace
183
187
  logger.error message
184
188
  end
185
-
189
+
186
190
  def ensure_process_is_spawned
187
191
  return if node_pid
188
192
  spawn_process
189
193
  end
190
-
194
+
191
195
  def ensure_class_is_defined
192
196
  return if self.class.class_defined?
193
197
  call_js_method(DEFINE_METHOD, self.class.generate_class_code)
194
198
  self.class.class_defined = true
195
199
  end
196
-
200
+
197
201
  def ensure_context_is_defined
198
202
  return if context_defined?
199
203
  @@mutex.synchronize do
@@ -202,7 +206,7 @@ module Nodo
202
206
  @context_defined = true
203
207
  end
204
208
  end
205
-
209
+
206
210
  def spawn_process
207
211
  @@tmpdir = Pathname.new(Dir.mktmpdir('nodo'))
208
212
  env = Nodo.env.merge('NODE_PATH' => Nodo.modules_root.to_s)
@@ -215,7 +219,7 @@ module Nodo
215
219
  FileUtils.remove_entry(tmpdir) if File.directory?(tmpdir)
216
220
  end
217
221
  end
218
-
222
+
219
223
  def wait_for_socket
220
224
  start = Time.now
221
225
  socket = nil
@@ -237,7 +241,7 @@ module Nodo
237
241
  raise NameError, "undefined function `#{method}' for #{self.class}" unless function || INTERNAL_METHODS.include?(method)
238
242
  context_id = case method
239
243
  when DEFINE_METHOD then 0
240
- when GC_METHOD then args.first
244
+ when GC_METHOD then args
241
245
  else
242
246
  object_id
243
247
  end
@@ -257,7 +261,7 @@ module Nodo
257
261
  # TODO: restart or something? If this happens the process is completely broken
258
262
  raise Error, 'Node process failed'
259
263
  end
260
-
264
+
261
265
  def handle_error(response, function)
262
266
  if response.body
263
267
  result = parse_response(response)
@@ -270,12 +274,12 @@ module Nodo
270
274
  log_exception(error)
271
275
  raise error
272
276
  end
273
-
277
+
274
278
  def parse_response(response)
275
279
  data = response.body.force_encoding('UTF-8')
276
280
  JSON.parse(data) unless data == ''
277
281
  end
278
-
282
+
279
283
  def with_tempfile(name)
280
284
  ext = File.extname(name)
281
285
  result = nil
@@ -284,6 +288,6 @@ module Nodo
284
288
  end
285
289
  result
286
290
  end
287
-
291
+
288
292
  end
289
293
  end
@@ -1,12 +1,22 @@
1
1
  module Nodo
2
2
  class Dependency
3
- attr_reader :name, :package
4
-
5
- def initialize(name, package)
6
- @name, @package = name, package
3
+ attr_reader :name, :package, :type
4
+
5
+ def initialize(name, package, type:)
6
+ @name, @package, @type = name, package, type
7
7
  end
8
-
8
+
9
9
  def to_js
10
+ case type
11
+ when :cjs then to_cjs
12
+ when :esm then to_esm
13
+ else raise "Unknown dependency type: #{type}"
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def to_cjs
10
20
  <<~JS
11
21
  const #{name} = __nodo_klass__.#{name} = (() => {
12
22
  try {
@@ -18,5 +28,18 @@ module Nodo
18
28
  })();
19
29
  JS
20
30
  end
31
+
32
+ def to_esm
33
+ <<~JS
34
+ const #{name} = __nodo_klass__.#{name} = await (async () => {
35
+ try {
36
+ return await nodo.import(#{package.to_json});
37
+ } catch(e) {
38
+ e.nodo_dependency = #{package.to_json};
39
+ throw e;
40
+ }
41
+ })();
42
+ JS
43
+ end
21
44
  end
22
45
  end
@@ -13,7 +13,7 @@ module.exports = (function() {
13
13
  let server, closing;
14
14
  const classes = {};
15
15
  const contexts = {};
16
-
16
+
17
17
  function render_error(e) {
18
18
  let errInfo = {};
19
19
  if (e instanceof Error) {
@@ -31,7 +31,7 @@ module.exports = (function() {
31
31
  debug(`Error ${code} ${rendered}`);
32
32
  res.end(rendered, 'utf8');
33
33
  }
34
-
34
+
35
35
  function respond_with_data(res, data, start) {
36
36
  let timing;
37
37
  res.statusCode = 200;
@@ -58,18 +58,18 @@ module.exports = (function() {
58
58
  debug('Starting up...');
59
59
  server = http.createServer((req, res) => {
60
60
  const start = performance.now();
61
-
61
+
62
62
  res.setHeader('Content-Type', 'application/json');
63
63
  debug(`${req.method} ${req.url}`);
64
64
 
65
65
  if (req.method !== 'POST' || !req.url.startsWith('/')) {
66
66
  return respond_with_error(res, 405, 'Method Not Allowed');
67
67
  }
68
-
68
+
69
69
  const url = req.url.substring(1);
70
70
  const [class_name, object_id, method] = url.split('/');
71
71
  let klass, context;
72
-
72
+
73
73
  if (classes.hasOwnProperty(class_name)) {
74
74
  klass = classes[class_name];
75
75
  if (EVALUATE_METHOD == method) {
@@ -83,9 +83,9 @@ module.exports = (function() {
83
83
  } else if (DEFINE_METHOD != method) {
84
84
  return respond_with_error(res, 404, `Class ${class_name} not defined`);
85
85
  }
86
-
86
+
87
87
  let body = '';
88
-
88
+
89
89
  req.on('data', (data) => { body += data; });
90
90
 
91
91
  req.on('end', () => {
@@ -99,8 +99,12 @@ module.exports = (function() {
99
99
 
100
100
  try {
101
101
  if (DEFINE_METHOD == method) {
102
- classes[class_name] = vm.runInThisContext(input, class_name);
103
- respond_with_data(res, class_name, start);
102
+ Promise.resolve(vm.runInThisContext(input, class_name)).then((result) => {
103
+ classes[class_name] = result;
104
+ respond_with_data(res, class_name, start);
105
+ }).catch((error) => {
106
+ respond_with_error(res, 500, error);
107
+ })
104
108
  } else if (EVALUATE_METHOD == method) {
105
109
  Promise.resolve(vm.runInNewContext(input, context)).then((result) => {
106
110
  respond_with_data(res, result, start);
@@ -120,7 +124,7 @@ module.exports = (function() {
120
124
  } catch(error) {
121
125
  return respond_with_error(res, 500, error);
122
126
  }
123
-
127
+
124
128
  });
125
129
  });
126
130
 
@@ -129,7 +133,7 @@ module.exports = (function() {
129
133
  debug(`server ready, listening on ${socket} (max connections: ${server.maxConnections})`);
130
134
  });
131
135
  },
132
-
136
+
133
137
  close: (finalizer) => {
134
138
  debug("Shutting down");
135
139
  if (!closing) {
@@ -138,6 +142,6 @@ module.exports = (function() {
138
142
  }
139
143
  }
140
144
  };
141
-
145
+
142
146
  return { core: core, debug: debug, import: import_module };
143
147
  })();
data/lib/nodo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Nodo
2
- VERSION = '1.6.5'
2
+ VERSION = '1.8.0'
3
3
  end
metadata CHANGED
@@ -1,66 +1,23 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nodo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.5
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthias Grosser
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2021-10-12 00:00:00.000000000 Z
10
+ date: 2025-02-05 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
13
+ name: logger
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
18
  version: '0'
19
- name: bundler
19
+ type: :runtime
20
20
  prerelease: false
21
- type: :development
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
- - !ruby/object:Gem::Dependency
28
- requirement: !ruby/object:Gem::Requirement
29
- requirements:
30
- - - ">="
31
- - !ruby/object:Gem::Version
32
- version: '0'
33
- name: rake
34
- prerelease: false
35
- type: :development
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- requirement: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: '0'
47
- name: byebug
48
- prerelease: false
49
- type: :development
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- - !ruby/object:Gem::Dependency
56
- requirement: !ruby/object:Gem::Requirement
57
- requirements:
58
- - - ">="
59
- - !ruby/object:Gem::Version
60
- version: '0'
61
- name: minitest
62
- prerelease: false
63
- type: :development
64
21
  version_requirements: !ruby/object:Gem::Requirement
65
22
  requirements:
66
23
  - - ">="
@@ -82,7 +39,7 @@ files:
82
39
  - lib/nodo/dependency.rb
83
40
  - lib/nodo/errors.rb
84
41
  - lib/nodo/function.rb
85
- - lib/nodo/nodo.js
42
+ - lib/nodo/nodo.cjs
86
43
  - lib/nodo/railtie.rb
87
44
  - lib/nodo/script.rb
88
45
  - lib/nodo/version.rb
@@ -90,7 +47,6 @@ homepage: https://github.com/mtgrosser/nodo
90
47
  licenses:
91
48
  - MIT
92
49
  metadata: {}
93
- post_install_message:
94
50
  rdoc_options: []
95
51
  require_paths:
96
52
  - lib
@@ -98,15 +54,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
98
54
  requirements:
99
55
  - - ">="
100
56
  - !ruby/object:Gem::Version
101
- version: 2.3.0
57
+ version: 3.0.0
102
58
  required_rubygems_version: !ruby/object:Gem::Requirement
103
59
  requirements:
104
60
  - - ">="
105
61
  - !ruby/object:Gem::Version
106
62
  version: '0'
107
63
  requirements: []
108
- rubygems_version: 3.3.25
109
- signing_key:
64
+ rubygems_version: 3.6.2
110
65
  specification_version: 4
111
66
  summary: Call Node.js from Ruby
112
67
  test_files: []