nodo 1.6.0 → 1.6.4

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: 421dba598b565369d200c90c8b095786f7b0a98de7c97e88c6927b59b64591a0
4
- data.tar.gz: c6ba40e8873c90713edf4cc0887dcf64c3e7b9ea4629f4d97e3fc3c8c58218b9
3
+ metadata.gz: a4870340520e40bf14cf0e9261711983d5332eaf04419ff1281626ed5b4716e1
4
+ data.tar.gz: 162b5e4f6739728c91f64422acb579db9c004429f92402db5f393cfb76ffa675
5
5
  SHA512:
6
- metadata.gz: 58410d1f53c78f40314ae24ea5f6e7d2aeca99f37aeeb6495ccdf0ee8a1fe903c956ad23462cba48ad38132df5614b3f354ee6c09638f52d47fe78e1e90d3064
7
- data.tar.gz: 8374444cbbc7257b206fb17abd3470df3488885ae843e414bca3e645cf6c9f9e9d91d6de6f354e5f7fe4a1a98d780f04562df89752286851f8727d86e3c88aa9
6
+ metadata.gz: dbe6fb8dce3f39968b26800a28f50b7d2c1ac49ffc60537a50950af598bc329b6ae59fae2db8285b16167238448ef370502d7b6f736c4dc4e66563ae0050ccfd
7
+ data.tar.gz: 37dd504e71489566a9b57df0e217c2f25d09ce3d511dab2ac6daf849829b90f1293ed07dd4e320eb70ef573da7b466263c704bb99cf46cb555449a8253909089
data/README.md CHANGED
@@ -91,6 +91,23 @@ class FooBar < Nodo::Core
91
91
  end
92
92
  ```
93
93
 
94
+ ### Dynamic ESM imports
95
+
96
+ ES modules can be imported dynamically using `nodo.import()`:
97
+
98
+ ```ruby
99
+ class DynamicFoo < Nodo::Core
100
+ function :v4, <<~JS
101
+ async () => {
102
+ const uuid = await nodo.import('uuid');
103
+ return await uuid.v4()
104
+ }
105
+ JS
106
+ end
107
+ ```
108
+
109
+ Note that the availability of dynamic imports depends on your Node version.
110
+
94
111
  ### Alternate function definition syntax
95
112
 
96
113
  JS code can also be supplied using the `code:` keyword argument:
@@ -131,6 +148,23 @@ class BarFoo < Nodo::Core
131
148
  end
132
149
  ```
133
150
 
151
+ With the above syntax, the script code will be generated during class definition
152
+ time. In order to have the code generated when the first instance is created, the
153
+ code can be defined inside a block:
154
+
155
+ ```ruby
156
+ class Foo < Nodo::Core
157
+ script do
158
+ <<~JS
159
+ var definitionTime = #{Time.now.to_json};
160
+ JS
161
+ end
162
+ end
163
+ ```
164
+
165
+ Note that the script will still be executed only once, when the first instance
166
+ of class is created.
167
+
134
168
  ### Inheritance
135
169
 
136
170
  Subclasses will inherit functions, constants, dependencies and scripts from
@@ -168,10 +202,50 @@ class SyncFoo < Nodo::Core
168
202
  end
169
203
  ```
170
204
 
205
+ ### Deferred function definition
206
+
207
+ By default, the function code string literal is created when the class
208
+ is defined. Therefore any string interpolation inside the code will take
209
+ place at definition time.
210
+
211
+ In order to defer the code generation until the first object instantiation,
212
+ the function code can be given inside a block:
213
+
214
+ ```ruby
215
+ class Deferred < Nodo::Core
216
+ function :now, <<~JS
217
+ () => { return #{Time.now.to_json}; }
218
+ JS
219
+
220
+ function :later do
221
+ <<~JS
222
+ () => { return #{Time.now.to_json}; }
223
+ JS
224
+ end
225
+ end
226
+
227
+ instance = Deferred.new
228
+ sleep 5
229
+ instance.now => "2021-10-28 20:30:00 +0200"
230
+ instance.later => "2021-10-28 20:30:05 +0200"
231
+ ```
232
+
233
+ The block will be invoked when the first instance is created. As with deferred
234
+ scripts, it will only be invoked once.
235
+
171
236
  ### Limiting function execution time
172
237
 
173
- The default timeout for a single JS function call is 60 seconds due to the
174
- `Net::HTTP` default. It can be overridden on a per-function basis:
238
+ The default timeout for a single JS function call is 60 seconds and can be
239
+ set globally:
240
+
241
+ ```ruby
242
+ Nodo.timeout = 5
243
+ ```
244
+
245
+ If the execution of a single function call exceeds the timeout, `Nodo::TimeoutError`
246
+ is raised.
247
+
248
+ The timeout can also be set on a per-function basis:
175
249
 
176
250
  ```ruby
177
251
  class Foo < Nodo::Core
@@ -208,8 +282,63 @@ Nodo.debug = true
208
282
  before instantiating any worker instances. The debug mode will be active during
209
283
  the current process run.
210
284
 
285
+ To print a debug message from JS code:
286
+
287
+ ```js
288
+ nodo.debug("Debug message");
289
+ ```
290
+
291
+ ### Evaluation
292
+
293
+ While `Nodo` is mainly function-based, it is possible to evaluate JS code in the
294
+ context of the defined object.
295
+
296
+ ```ruby
297
+ foo = Foo.new.evaluate("3 + 5")
298
+ => 8
299
+ ```
300
+
301
+ Evaluated code can access functions, required dependencies and constants:
302
+
303
+ ```ruby
304
+ class Foo < Nodo::Core
305
+ const :BAR, 'bar'
306
+ require :uuid
307
+ function :hello, code: '() => "world"'
308
+ end
309
+
310
+ foo = Foo.new
311
+
312
+ foo.evaluate('BAR')
313
+ => "bar"
314
+
315
+ foo.evaluate('uuid.v4()')
316
+ => "f258bef3-0d6f-4566-ad39-d8dec973ef6b"
317
+
318
+ foo.evaluate('hello()')
319
+ => "world"
320
+ ```
321
+
322
+ Variables defined by evaluation are local to the current instance:
323
+
324
+ ```ruby
325
+ one = Foo.new
326
+ one.evaluate('a = 1')
327
+ two = Foo.new
328
+ two.evaluate('a = 2')
329
+ one.evaluate('a') => 1
330
+ two.evaluate('a') => 2
331
+ ```
332
+
333
+ ⚠️ Evaluation comes with the usual caveats:
334
+
335
+ - Avoid modifying any of your predefined identifiers. Remember that in JS,
336
+ as in Ruby, constants are not necessarily constant.
337
+ - Never evaluate any code which includes un-checked user data. The Node.js process
338
+ has full read/write access to your filesystem! 💥
339
+
211
340
 
212
- #### Clean your Rails root
341
+ ## Clean your Rails root
213
342
 
214
343
  For Rails applications, Nodo enables you to move `node_modules`, `package.json` and
215
344
  `yarn.lock` into your application's `vendor` folder by setting the `NODE_PATH` in
@@ -220,7 +349,7 @@ an initializer:
220
349
  Nodo.modules_root = Rails.root.join('vendor', 'node_modules')
221
350
  ```
222
351
 
223
- The rationale behind this is NPM modules being external vendor dependencies, which
352
+ The rationale for this is NPM modules being external vendor dependencies, which
224
353
  should not clutter the application root directory.
225
354
 
226
355
  With this new default, all `yarn` operations should be done after `cd`ing to `vendor`.
data/lib/nodo/constant.rb CHANGED
@@ -7,7 +7,7 @@ module Nodo
7
7
  end
8
8
 
9
9
  def to_js
10
- "const #{name} = #{value.to_json};\n"
10
+ "const #{name} = __nodo_klass__.#{name} = #{value.to_json};\n"
11
11
  end
12
12
  end
13
13
  end
data/lib/nodo/core.rb CHANGED
@@ -1,8 +1,11 @@
1
1
  module Nodo
2
2
  class Core
3
3
  SOCKET_NAME = 'nodo.sock'
4
- DEFINE_METHOD = '__nodo_define_class__'
5
- TIMEOUT = 5
4
+ DEFINE_METHOD = '__nodo_define_class__'.freeze
5
+ EVALUATE_METHOD = '__nodo_evaluate__'.freeze
6
+ GC_METHOD = '__nodo_gc__'.freeze
7
+ INTERNAL_METHODS = [DEFINE_METHOD, EVALUATE_METHOD, GC_METHOD].freeze
8
+ LAUNCH_TIMEOUT = 5
6
9
  ARRAY_CLASS_ATTRIBUTES = %i[dependencies constants scripts].freeze
7
10
  HASH_CLASS_ATTRIBUTES = %i[functions].freeze
8
11
  CLASS_ATTRIBUTES = (ARRAY_CLASS_ATTRIBUTES + HASH_CLASS_ATTRIBUTES).freeze
@@ -10,6 +13,7 @@ module Nodo
10
13
  @@node_pid = nil
11
14
  @@tmpdir = nil
12
15
  @@mutex = Mutex.new
16
+ @@exiting = nil
13
17
 
14
18
  class << self
15
19
  extend Forwardable
@@ -21,15 +25,11 @@ module Nodo
21
25
  subclass.send "#{attr}=", send(attr).dup
22
26
  end
23
27
  end
24
-
28
+
25
29
  def instance
26
30
  @instance ||= new
27
31
  end
28
32
 
29
- def class_function(*methods)
30
- singleton_class.def_delegators(:instance, *methods)
31
- end
32
-
33
33
  def class_defined?
34
34
  !!class_defined
35
35
  end
@@ -42,6 +42,7 @@ module Nodo
42
42
  define_method "#{attr}=" do |value|
43
43
  instance_variable_set :"@#{attr}", value
44
44
  end
45
+ protected "#{attr}="
45
46
  end
46
47
 
47
48
  ARRAY_CLASS_ATTRIBUTES.each do |attr|
@@ -55,30 +56,6 @@ module Nodo
55
56
  instance_variable_get(:"@#{attr}") || instance_variable_set(:"@#{attr}", {})
56
57
  end
57
58
  end
58
-
59
- def require(*mods)
60
- deps = mods.last.is_a?(Hash) ? mods.pop : {}
61
- mods = mods.map { |m| [m, m] }.to_h
62
- self.dependencies = dependencies + mods.merge(deps).map { |name, package| Dependency.new(name, package) }
63
- end
64
-
65
- def function(name, _code = nil, timeout: 60, code: nil)
66
- raise ArgumentError, "reserved method name #{name.inspect}" if Nodo::Core.method_defined?(name) || name.to_s == DEFINE_METHOD
67
- code = (code ||= _code).strip
68
- raise ArgumentError, 'function code is required' if '' == code
69
- loc = caller_locations(1, 1)[0]
70
- source_location = "#{loc.path}:#{loc.lineno}: in `#{name}'"
71
- self.functions = functions.merge(name => Function.new(name, _code || code, source_location, timeout))
72
- define_method(name) { |*args| call_js_method(name, args) }
73
- end
74
-
75
- def const(name, value)
76
- self.constants = constants + [Constant.new(name, value)]
77
- end
78
-
79
- def script(code)
80
- self.scripts = scripts + [Script.new(code)]
81
- end
82
59
 
83
60
  def generate_core_code
84
61
  <<~JS
@@ -106,8 +83,7 @@ module Nodo
106
83
  def generate_class_code
107
84
  <<~JS
108
85
  (() => {
109
- const __nodo_log = nodo.log;
110
- const __nodo_klass__ = {};
86
+ const __nodo_klass__ = { nodo: global.nodo };
111
87
  #{dependencies.map(&:to_js).join}
112
88
  #{constants.map(&:to_js).join}
113
89
  #{functions.values.map(&:to_js).join}
@@ -117,14 +93,55 @@ module Nodo
117
93
  JS
118
94
  end
119
95
 
96
+ protected
97
+
98
+ def finalize_context(context_id)
99
+ proc do
100
+ if not @@exiting and core = Nodo::Core.instance
101
+ core.send(:call_js_method, GC_METHOD, context_id)
102
+ end
103
+ end
104
+ end
105
+
120
106
  private
121
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) }
112
+ end
113
+
114
+ def function(name, _code = nil, timeout: Nodo.timeout, code: nil, &block)
115
+ raise ArgumentError, "reserved method name #{name.inspect}" if reserved_method_name?(name)
116
+ loc = caller_locations(1, 1)[0]
117
+ source_location = "#{loc.path}:#{loc.lineno}: in `#{name}'"
118
+ self.functions = functions.merge(name => Function.new(name, _code || code, source_location, timeout, &block))
119
+ define_method(name) { |*args| call_js_method(name, args) }
120
+ end
121
+
122
+ def class_function(*methods)
123
+ singleton_class.def_delegators(:instance, *methods)
124
+ end
125
+
126
+ def const(name, value)
127
+ self.constants = constants + [Constant.new(name, value)]
128
+ end
129
+
130
+ def script(code = nil, &block)
131
+ self.scripts = scripts + [Script.new(code, &block)]
132
+ end
133
+
122
134
  def nodo_js
123
135
  Pathname.new(__FILE__).dirname.join('nodo.js').to_s.to_json
124
136
  end
137
+
138
+ def reserved_method_name?(name)
139
+ Nodo::Core.method_defined?(name, false) || Nodo::Core.private_method_defined?(name, false) || name.to_s == DEFINE_METHOD
140
+ end
125
141
  end
126
142
 
127
143
  def initialize
144
+ raise ClassError, :new if self.class == Nodo::Core
128
145
  @@mutex.synchronize do
129
146
  ensure_process_is_spawned
130
147
  wait_for_socket
@@ -132,6 +149,13 @@ module Nodo
132
149
  end
133
150
  end
134
151
 
152
+ def evaluate(code)
153
+ ensure_context_is_defined
154
+ call_js_method(EVALUATE_METHOD, code)
155
+ end
156
+
157
+ private
158
+
135
159
  def node_pid
136
160
  @@node_pid
137
161
  end
@@ -148,9 +172,15 @@ module Nodo
148
172
  self.class.clsid
149
173
  end
150
174
 
175
+ def context_defined?
176
+ @context_defined
177
+ end
178
+
151
179
  def log_exception(e)
152
180
  return unless logger = Nodo.logger
153
- logger.error "\n#{e.class} (#{e.message}):\n\n#{e.backtrace.join("\n")}"
181
+ message = "\n#{e.class} (#{e.message})"
182
+ message << ":\n\n#{e.backtrace.join("\n")}" if e.backtrace
183
+ logger.error message
154
184
  end
155
185
 
156
186
  def ensure_process_is_spawned
@@ -164,12 +194,22 @@ module Nodo
164
194
  self.class.class_defined = true
165
195
  end
166
196
 
197
+ def ensure_context_is_defined
198
+ return if context_defined?
199
+ @@mutex.synchronize do
200
+ call_js_method(EVALUATE_METHOD, '')
201
+ ObjectSpace.define_finalizer(self, self.class.send(:finalize_context, self.object_id))
202
+ @context_defined = true
203
+ end
204
+ end
205
+
167
206
  def spawn_process
168
207
  @@tmpdir = Pathname.new(Dir.mktmpdir('nodo'))
169
208
  env = Nodo.env.merge('NODE_PATH' => Nodo.modules_root.to_s)
170
209
  env['NODO_DEBUG'] = '1' if Nodo.debug
171
210
  @@node_pid = Process.spawn(env, Nodo.binary, '-e', self.class.generate_core_code, '--', socket_path.to_s, err: :out)
172
211
  at_exit do
212
+ @@exiting = true
173
213
  Process.kill(:SIGTERM, node_pid) rescue Errno::ECHILD
174
214
  Process.wait(node_pid) rescue Errno::ECHILD
175
215
  FileUtils.remove_entry(tmpdir) if File.directory?(tmpdir)
@@ -179,7 +219,7 @@ module Nodo
179
219
  def wait_for_socket
180
220
  start = Time.now
181
221
  socket = nil
182
- while Time.now - start < TIMEOUT
222
+ while Time.now - start < LAUNCH_TIMEOUT
183
223
  begin
184
224
  break if socket = UNIXSocket.new(socket_path)
185
225
  rescue Errno::ENOENT, Errno::ECONNREFUSED, Errno::ENOTDIR
@@ -192,13 +232,19 @@ module Nodo
192
232
 
193
233
  def call_js_method(method, args)
194
234
  raise CallError, 'Node process not ready' unless node_pid
195
- raise CallError, "Class #{clsid} not defined" unless self.class.class_defined? || method == DEFINE_METHOD
235
+ raise CallError, "Class #{clsid} not defined" unless self.class.class_defined? || INTERNAL_METHODS.include?(method)
196
236
  function = self.class.functions[method]
197
- raise NameError, "undefined function `#{method}' for #{self.class}" unless function || method == DEFINE_METHOD
198
- request = Net::HTTP::Post.new("/#{clsid}/#{method}", 'Content-Type': 'application/json')
237
+ raise NameError, "undefined function `#{method}' for #{self.class}" unless function || INTERNAL_METHODS.include?(method)
238
+ context_id = case method
239
+ when DEFINE_METHOD then 0
240
+ when GC_METHOD then args.first
241
+ else
242
+ object_id
243
+ end
244
+ request = Net::HTTP::Post.new("/#{clsid}/#{context_id}/#{method}", 'Content-Type': 'application/json')
199
245
  request.body = JSON.dump(args)
200
246
  client = Client.new("unix://#{socket_path}")
201
- client.read_timeout = function.timeout if function
247
+ client.read_timeout = function&.timeout || Nodo.timeout
202
248
  response = client.request(request)
203
249
  if response.is_a?(Net::HTTPOK)
204
250
  parse_response(response)
@@ -215,7 +261,10 @@ module Nodo
215
261
  def handle_error(response, function)
216
262
  if response.body
217
263
  result = parse_response(response)
218
- error = JavaScriptError.new(result['error'], function) if result.is_a?(Hash) && result.key?('error')
264
+ error = if result.is_a?(Hash) && result['error'].is_a?(Hash)
265
+ attrs = result['error']
266
+ (attrs['nodo_dependency'] ? DependencyError : JavaScriptError).new(attrs, function)
267
+ end
219
268
  end
220
269
  error ||= CallError.new("Node returned #{response.code}")
221
270
  log_exception(error)
@@ -7,7 +7,16 @@ module Nodo
7
7
  end
8
8
 
9
9
  def to_js
10
- "const #{name} = require(#{package.to_json});\n"
10
+ <<~JS
11
+ const #{name} = __nodo_klass__.#{name} = (() => {
12
+ try {
13
+ return require(#{package.to_json});
14
+ } catch(e) {
15
+ e.nodo_dependency = #{package.to_json};
16
+ throw e;
17
+ }
18
+ })();
19
+ JS
11
20
  end
12
21
  end
13
22
  end
data/lib/nodo/errors.rb CHANGED
@@ -2,6 +2,11 @@ module Nodo
2
2
  class Error < StandardError; end
3
3
  class TimeoutError < Error; end
4
4
  class CallError < Error; end
5
+ class ClassError < Error
6
+ def initialize(method = nil)
7
+ super("Cannot call method `#{method}' on Nodo::Core, use subclass instead")
8
+ end
9
+ end
5
10
 
6
11
  class JavaScriptError < Error
7
12
  attr_reader :attributes
@@ -37,13 +42,23 @@ module Nodo
37
42
  end
38
43
 
39
44
  def generate_message
40
- message = "#{attributes['message'] || 'Unknown error'}"
41
- if loc = attributes['loc']
42
- message << loc.inject(' in') { |s, (key, value)| s << " #{key}: #{value}" }
43
- end
44
- message
45
+ message = "#{attributes['message'] || attributes['name'] || 'Unknown error'}"
46
+ message << format_location(attributes['loc'])
47
+ end
48
+
49
+ def format_location(loc)
50
+ return '' unless loc
51
+ loc.inject(' in') { |s, (key, value)| s << " #{key}: #{value}" }
45
52
  end
46
53
  end
47
54
 
48
- class DependencyError < JavaScriptError; end
55
+ class DependencyError < JavaScriptError
56
+ private
57
+
58
+ def generate_message
59
+ message = "#{attributes['message'] || attributes['name'] || 'Dependency error'}\n"
60
+ message << "The specified dependency '#{attributes['nodo_dependency']}' could not be loaded. "
61
+ message << "Run 'yarn add #{attributes['nodo_dependency']}' to install it.\n"
62
+ end
63
+ end
49
64
  end
data/lib/nodo/function.rb CHANGED
@@ -2,12 +2,17 @@ module Nodo
2
2
  class Function
3
3
  attr_reader :name, :code, :source_location, :timeout
4
4
 
5
- def initialize(name, code, source_location, timeout)
6
- @name, @code, @source_location, @timeout = name, code, source_location, timeout
5
+ def initialize(name, code, source_location, timeout, &block)
6
+ raise ArgumentError, 'cannot give code when block is given' if code && block
7
+ code = code.strip if code
8
+ raise ArgumentError, 'function code is required' if '' == code
9
+ raise ArgumentError, 'code is required' unless code || block
10
+ @name, @code, @source_location, @timeout = name, code || block, source_location, timeout
7
11
  end
8
12
 
9
13
  def to_js
10
- "const #{name} = __nodo_klass__.#{name} = (#{code});\n"
14
+ js = code.respond_to?(:call) ? code.call.strip : code
15
+ "const #{name} = __nodo_klass__.#{name} = (#{js});\n"
11
16
  end
12
17
  end
13
18
  end
data/lib/nodo/nodo.js CHANGED
@@ -1,5 +1,7 @@
1
1
  module.exports = (function() {
2
2
  const DEFINE_METHOD = '__nodo_define_class__';
3
+ const EVALUATE_METHOD = '__nodo_evaluate__';
4
+ const GC_METHOD = '__nodo_gc__';
3
5
  const DEBUG = process.env.NODO_DEBUG;
4
6
 
5
7
  const vm = require('vm');
@@ -7,9 +9,10 @@ module.exports = (function() {
7
9
  const path = require('path');
8
10
  const fs = require('fs');
9
11
  const performance = require('perf_hooks').performance;
10
-
12
+
11
13
  let server, closing;
12
14
  const classes = {};
15
+ const contexts = {};
13
16
 
14
17
  function render_error(e) {
15
18
  let errInfo = {};
@@ -45,7 +48,11 @@ module.exports = (function() {
45
48
  console.log(`[Nodo] ${message}`);
46
49
  }
47
50
  }
48
-
51
+
52
+ async function import_module(specifier) {
53
+ return await import(specifier);
54
+ }
55
+
49
56
  const core = {
50
57
  run: (socket) => {
51
58
  debug('Starting up...');
@@ -60,12 +67,17 @@ module.exports = (function() {
60
67
  }
61
68
 
62
69
  const url = req.url.substring(1);
63
- const [class_name, method] = url.split('/');
64
- let klass;
70
+ const [class_name, object_id, method] = url.split('/');
71
+ let klass, context;
65
72
 
66
73
  if (classes.hasOwnProperty(class_name)) {
67
74
  klass = classes[class_name];
68
- if (!klass.hasOwnProperty(method)) {
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) {
69
81
  return respond_with_error(res, 404, `Method ${class_name}#${method} not found`);
70
82
  }
71
83
  } else if (DEFINE_METHOD != method) {
@@ -87,13 +99,21 @@ module.exports = (function() {
87
99
 
88
100
  try {
89
101
  if (DEFINE_METHOD == method) {
90
- let new_class = vm.runInThisContext(input, class_name);
91
- classes[class_name] = new_class;
102
+ classes[class_name] = vm.runInThisContext(input, class_name);
92
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);
93
113
  } else {
94
- Promise.resolve(klass[method].apply(null, input)).then(function (result) {
114
+ Promise.resolve(klass[method].apply(null, input)).then((result) => {
95
115
  respond_with_data(res, result, start);
96
- }).catch(function(error) {
116
+ }).catch((error) => {
97
117
  respond_with_error(res, 500, error);
98
118
  });
99
119
  }
@@ -119,5 +139,5 @@ module.exports = (function() {
119
139
  }
120
140
  };
121
141
 
122
- return { core: core, debug: debug };
142
+ return { core: core, debug: debug, import: import_module };
123
143
  })();
data/lib/nodo/script.rb CHANGED
@@ -2,12 +2,14 @@ module Nodo
2
2
  class Script
3
3
  attr_reader :code
4
4
 
5
- def initialize(code)
6
- @code = code
5
+ def initialize(code = nil, &block)
6
+ raise ArgumentError, 'cannot give code when block is given' if code && block
7
+ @code = code || block
7
8
  end
8
9
 
9
10
  def to_js
10
- "#{code}\n"
11
+ js = code.respond_to?(:call) ? code.call : code
12
+ "#{js}\n"
11
13
  end
12
14
  end
13
15
  end
data/lib/nodo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Nodo
2
- VERSION = '1.6.0'
2
+ VERSION = '1.6.4'
3
3
  end
data/lib/nodo.rb CHANGED
@@ -9,13 +9,14 @@ require 'forwardable'
9
9
 
10
10
  module Nodo
11
11
  class << self
12
- attr_accessor :modules_root, :env, :binary, :logger, :debug
12
+ attr_accessor :modules_root, :env, :binary, :logger, :debug, :timeout
13
13
  end
14
14
  self.modules_root = './node_modules'
15
15
  self.env = {}
16
16
  self.binary = 'node'
17
17
  self.logger = Logger.new(STDOUT)
18
18
  self.debug = false
19
+ self.timeout = 60
19
20
  end
20
21
 
21
22
  require_relative 'nodo/version'
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.6.0
4
+ version: 1.6.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthias Grosser