nodo 1.5.6 → 1.6.3

Sign up to get free protection for your applications and to get access to all the features.
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