nodo 1.5.6 → 1.6.3

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: 650d220075d68a34268a03f2e68587b1caab13f47707c8b44f7cb7d05f5a5588
4
- data.tar.gz: 0b73f0cd827a1ab364975d3559f64f2d9c49afe265759715f92d23f133bab10c
3
+ metadata.gz: 42cd6c0e3e07674ceb2f10dd670c490b7f23ed6534f22f978a40e0b91d529570
4
+ data.tar.gz: b560df0c25e2b72842e6d6d5be610503252069bfd1741fb2cd9228066d28f91e
5
5
  SHA512:
6
- metadata.gz: 0c1049cd1e574c0a7ec503ef01ae735d7b3ca33d21695a1a76c4c8f5c92f0914db58b59136a7ea0bcf17b83f9c6776817b8b4890061e6c442e414c5476d80037
7
- data.tar.gz: d5fd39f74f9ebfabaec0463dbf7234e6da34a73b3167f2580fb6ac276f52f6aef433156ef0917b4a5c9c344f1dfc6d0f28e7c1af1bfe329829c5c87068d932dd
6
+ metadata.gz: 012ed2197c0ea3e519884b3295a62986158326027adb08c484fbf87903874763594fe72236bebcc01f47a919a4893ef92cdc7a14b1f8ad02da7ea527ae5eab38
7
+ data.tar.gz: 6a953be3a789671c966248dcc385740c5964ad0bbb370411d93cdc44ee7ea129c14537d73c08e7a44eb4c19ca1bc2b5465873bec65fd4d56884ba9ed339981d6
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
- [![Gem Version](https://badge.fury.io/rb/nodo.svg)](http://badge.fury.io/rb/nodo) [![build](https://github.com/mtgrosser/nodo/actions/workflows/build.yml/badge.svg)](https://github.com/mtgrosser/nodo/actions/workflows/build.yml)
1
+ [![Gem Version](https://badge.fury.io/rb/nodo.svg)](http://badge.fury.io/rb/nodo)
2
+ [![build](https://github.com/mtgrosser/nodo/actions/workflows/build.yml/badge.svg)](https://github.com/mtgrosser/nodo/actions/workflows/build.yml)
2
3
 
3
4
  # Nōdo – call Node.js from Ruby
4
5
 
@@ -107,13 +108,7 @@ To set a custom path:
107
108
  Nodo.modules_root = 'path/to/node_modules'
108
109
  ```
109
110
 
110
- For Rails applications, it will be set to `vendor/node_modules`.
111
- To use the Rails 6 default of putting `node_modules` to `RAILS_ROOT`:
112
-
113
- ```ruby
114
- # config/initializers/nodo.rb
115
- Nodo.modules_root = Rails.root.join('node_modules')
116
- ```
111
+ Also see: [Clean your Rails root](#Clean-your-Rails-root)
117
112
 
118
113
  ### Defining JS constants
119
114
 
@@ -136,6 +131,23 @@ class BarFoo < Nodo::Core
136
131
  end
137
132
  ```
138
133
 
134
+ With the above syntax, the script code will be generated during class definition
135
+ time. In order to have the code generated when the first instance is created, the
136
+ code can be defined inside a block:
137
+
138
+ ```ruby
139
+ class Foo < Nodo::Core
140
+ script do
141
+ <<~JS
142
+ var definitionTime = #{Time.now.to_json};
143
+ JS
144
+ end
145
+ end
146
+ ```
147
+
148
+ Note that the script will still be executed only once, when the first instance
149
+ of class is created.
150
+
139
151
  ### Inheritance
140
152
 
141
153
  Subclasses will inherit functions, constants, dependencies and scripts from
@@ -173,10 +185,50 @@ class SyncFoo < Nodo::Core
173
185
  end
174
186
  ```
175
187
 
188
+ ### Deferred function definition
189
+
190
+ By default, the function code string literal is created when the class
191
+ is defined. Therefore any string interpolation inside the code will take
192
+ place at definition time.
193
+
194
+ In order to defer the code generation until the first object instantiation,
195
+ the function code can be given inside a block:
196
+
197
+ ```ruby
198
+ class Deferred < Nodo::Core
199
+ function :now, <<~JS
200
+ () => { return #{Time.now.to_json}; }
201
+ JS
202
+
203
+ function :later do
204
+ <<~JS
205
+ () => { return #{Time.now.to_json}; }
206
+ JS
207
+ end
208
+ end
209
+
210
+ instance = Deferred.new
211
+ sleep 5
212
+ instance.now => "2021-10-28 20:30:00 +0200"
213
+ instance.later => "2021-10-28 20:30:05 +0200"
214
+ ```
215
+
216
+ The block will be invoked when the first instance is created. As with deferred
217
+ scripts, it will only be invoked once.
218
+
176
219
  ### Limiting function execution time
177
220
 
178
- The default timeout for a single JS function call is 60 seconds due to the
179
- `Net::HTTP` default. It can be overridden on a per-function basis:
221
+ The default timeout for a single JS function call is 60 seconds and can be
222
+ set globally:
223
+
224
+ ```ruby
225
+ Nodo.timeout = 5
226
+ ```
227
+
228
+ If the execution of a single function call exceeds the timeout, `Nodo::TimeoutError`
229
+ is raised.
230
+
231
+ The timeout can also be set on a per-function basis:
180
232
 
181
233
  ```ruby
182
234
  class Foo < Nodo::Core
@@ -201,6 +253,7 @@ Nodo.logger = Logger.new('nodo.log')
201
253
 
202
254
  In Rails applications, `Rails.logger` will automatically be set.
203
255
 
256
+
204
257
  ### Debugging
205
258
 
206
259
  To get verbose debug output, set
@@ -211,3 +264,78 @@ Nodo.debug = true
211
264
 
212
265
  before instantiating any worker instances. The debug mode will be active during
213
266
  the current process run.
267
+
268
+ To print a debug message from JS code:
269
+
270
+ ```js
271
+ nodo.debug("Debug message");
272
+ ```
273
+
274
+ ### Evaluation
275
+
276
+ While `Nodo` is mainly function-based, it is possible to evaluate JS code in the
277
+ context of the defined object.
278
+
279
+ ```ruby
280
+ foo = Foo.new.evaluate("3 + 5")
281
+ => 8
282
+ ```
283
+
284
+ Evaluated code can access functions, required dependencies and constants:
285
+
286
+ ```ruby
287
+ class Foo < Nodo::Core
288
+ const :BAR, 'bar'
289
+ require :uuid
290
+ function :hello, code: '() => "world"'
291
+ end
292
+
293
+ foo = Foo.new
294
+
295
+ foo.evaluate('BAR')
296
+ => "bar"
297
+
298
+ foo.evaluate('uuid.v4()')
299
+ => "f258bef3-0d6f-4566-ad39-d8dec973ef6b"
300
+
301
+ foo.evaluate('hello()')
302
+ => "world"
303
+ ```
304
+
305
+ Variables defined by evaluation are local to the current instance:
306
+
307
+ ```ruby
308
+ one = Foo.new
309
+ one.evaluate('a = 1')
310
+ two = Foo.new
311
+ two.evaluate('a = 2')
312
+ one.evaluate('a') => 1
313
+ two.evaluate('a') => 2
314
+ ```
315
+
316
+ ⚠️ Evaluation comes with the usual caveats:
317
+
318
+ - Avoid modifying any of your predefined identifiers. Remember that in JS,
319
+ as in Ruby, constants are not necessarily constant.
320
+ - Never evaluate any code which includes un-checked user data. The Node.js process
321
+ has full read/write access to your filesystem! 💥
322
+
323
+
324
+ ## Clean your Rails root
325
+
326
+ For Rails applications, Nodo enables you to move `node_modules`, `package.json` and
327
+ `yarn.lock` into your application's `vendor` folder by setting the `NODE_PATH` in
328
+ an initializer:
329
+
330
+ ```ruby
331
+ # config/initializers/nodo.rb
332
+ Nodo.modules_root = Rails.root.join('vendor', 'node_modules')
333
+ ```
334
+
335
+ The rationale for this is NPM modules being external vendor dependencies, which
336
+ should not clutter the application root directory.
337
+
338
+ With this new default, all `yarn` operations should be done after `cd`ing to `vendor`.
339
+
340
+ This repo provides an [adapted version](https://github.com/mtgrosser/nodo/blob/master/install/yarn.rake)
341
+ of the `yarn:install` rake task which will automatically take care of the vendored module location.
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');
@@ -10,6 +12,7 @@ module.exports = (function() {
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 = {};
@@ -60,12 +63,17 @@ module.exports = (function() {
60
63
  }
61
64
 
62
65
  const url = req.url.substring(1);
63
- const [class_name, method] = url.split('/');
64
- let klass;
66
+ const [class_name, object_id, method] = url.split('/');
67
+ let klass, context;
65
68
 
66
69
  if (classes.hasOwnProperty(class_name)) {
67
70
  klass = classes[class_name];
68
- if (!klass.hasOwnProperty(method)) {
71
+ if (EVALUATE_METHOD == method) {
72
+ if (!contexts.hasOwnProperty(object_id)) {
73
+ contexts[object_id] = vm.createContext({ require: require, ...klass });
74
+ }
75
+ context = contexts[object_id];
76
+ } else if (!klass.hasOwnProperty(method) && !GC_METHOD == method) {
69
77
  return respond_with_error(res, 404, `Method ${class_name}#${method} not found`);
70
78
  }
71
79
  } else if (DEFINE_METHOD != method) {
@@ -87,13 +95,21 @@ module.exports = (function() {
87
95
 
88
96
  try {
89
97
  if (DEFINE_METHOD == method) {
90
- let new_class = vm.runInThisContext(input, class_name);
91
- classes[class_name] = new_class;
98
+ classes[class_name] = vm.runInThisContext(input, class_name);
92
99
  respond_with_data(res, class_name, start);
100
+ } else if (EVALUATE_METHOD == method) {
101
+ Promise.resolve(vm.runInNewContext(input, context)).then((result) => {
102
+ respond_with_data(res, result, start);
103
+ }).catch((error) => {
104
+ respond_with_error(res, 500, error);
105
+ });
106
+ } else if (GC_METHOD == method) {
107
+ delete contexts[object_id];
108
+ respond_with_data(res, true, start);
93
109
  } else {
94
- Promise.resolve(klass[method].apply(null, input)).then(function (result) {
110
+ Promise.resolve(klass[method].apply(null, input)).then((result) => {
95
111
  respond_with_data(res, result, start);
96
- }).catch(function(error) {
112
+ }).catch((error) => {
97
113
  respond_with_error(res, 500, error);
98
114
  });
99
115
  }
data/lib/nodo/railtie.rb CHANGED
@@ -3,7 +3,6 @@ require 'active_support'
3
3
 
4
4
  class Nodo::Railtie < Rails::Railtie
5
5
  initializer 'nodo' do |app|
6
- Nodo.modules_root = Rails.root.join('vendor', 'node_modules')
7
6
  Nodo.env['NODE_ENV'] = Rails.env.to_s
8
7
  Nodo.logger = Rails.logger
9
8
  end
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.5.6'
2
+ VERSION = '1.6.3'
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.5.6
4
+ version: 1.6.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthias Grosser