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, " (name) => {\n return `Hello ${name}!`;\n }\n JS\n\nend\n\nfoo = Foo.new\nfoo.say_hi('Nodo')\n=> \"Hello Nodo!\"\n"
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, " () => {\n return uuid.v4();\n }\n JS\nend\n\nbar = Bar.new\nbar.v4 => \"b305f5c4-db9a-4504-b0c3-4e097a5ec8b9\"\n"
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 " // custom JS to be executed during initialization\n // things defined here can later be used inside functions\n const bigThing = someLib.init();\n JS\nend\n"
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, " async () => { return await asyncFunc(); }\n JS\nend\n"