Nōdo – call Node.js from Ruby
Nodo provides a Ruby environment to interact with JavaScript running inside a Node process.
ノード means "node" in Japanese.
Why Nodo?
Nodo will dispatch all JS function calls to a single long-running Node process.
JavaScript code is run in a namespaced environment, where you can access your initialized JS objects during sequential function calls without having to re-initialize them.
IPC is done via unix sockets, greatly improving performance over classic process/eval solutions.
Installation
In your Gemfile:
gem 'nodo'
Node.js
Nodo requires a working installation of Node.js.
If the executable is located in your PATH, no configuration is required. Otherwise, the path to to binary can be set using:
Nodo.binary = '/usr/local/bin/node'
Usage
In Nodo, you define JS functions as you would define Ruby methods:
class Foo < Nodo::Core
function :say_hi, <<~JS
(name) => {
return `Hello ${name}!`;
}
JS
end
foo = Foo.new
foo.say_hi('Nodo')
=> "Hello Nodo!"
Using npm modules
Install your modules to node_modules:
$ yarn add uuid
requireing your dependencies will make the library available as a const with the same name:
class Bar < Nodo::Core
require :uuid
function :v4, <<~JS
() => {
return uuid.v4();
}
JS
end
= Bar.new
.v4 => "b305f5c4-db9a-4504-b0c3-4e097a5ec8b9"
Aliasing requires
If the library name cannot be used as name of the constant, the const name
can be given using hash syntax:
class FooBar < Nodo::Core
require commonjs: '@rollup/plugin-commonjs'
end
Setting NODE_PATH
By default, ./node_modules is used as the NODE_PATH.
To set a custom path:
Nodo.modules_root = 'path/to/node_modules'
For Rails applications, it will be set to vendor/node_modules.
To use the Rails 6 default of putting node_modules to RAILS_ROOT:
# config/initializers/nodo.rb
Nodo.modules_root = Rails.root.join('node_modules')
Defining JS constants
class BarFoo < Nodo::Core
const :HELLO, "World"
end
Execute some custom JS during initialization
class BarFoo < Nodo::Core
script <<~JS
// custom JS to be executed during initialization
// things defined here can later be used inside functions
const bigThing = someLib.init();
JS
end
Inheritance
Subclasses will inherit functions, constants, dependencies and scripts from their superclasses, while only functions can be overwritten.
class Foo < Nodo::Core
function :foo, "() => 'superclass'"
end
class SubFoo < Foo
function :bar, "() => { return 'calling' + foo() }"
end
class SubSubFoo < SubFoo
function :foo, "() => 'subsubclass'"
end
Foo.new.foo => "superclass"
SubFoo.new. => "callingsuperclass"
SubSubFoo.new. => "callingsubsubclass"
Async functions
Nodo supports calling async functions from Ruby.
The Ruby call will happen synchronously, i.e. it will block until the JS function resolves:
class SyncFoo < Nodo::Core
function :do_something, <<~JS
async () => { return await asyncFunc(); }
JS
end