Learn how to compile and execute JavaScript code and CommonJS modules with Ruby—useful for re-using code already written in JavaScript without having to port it to Ruby, or adding another layer of complex third party dependencies to your web scale application.
Install the execjs gem
Make sure you have the execjs gem installed:
gem install execjs
Alternatively add the gem to your Gemfile and install it via bundler.
Check you have a JavaScript runtime installed
ExecJS will auto-detect which JavaScript runtimes you have installed and pick the best available to use. You can check which runtime will be used with the following command:
ruby -r execjs -e 'puts ExecJS.runtime.name'
If you get an ExecJS::RuntimeUnavailable
exception then you’ll need to install a compatible JavaScript interpreter (Node.js recommended).
Executing JavaScript code
Use the ExecJS.eval
method to evaluate simple JavaScript expressions. For example:
require 'execjs'
p ExecJS.eval('"red yellow blue".split(" ")')
p ExecJS.eval('{currency: "USD", price: 9900}')
As with eval in Ruby: don’t eval code you don’t trust.
For more complex use cases you can compile code with the ExecJS.compile
method and then use the resulting context to call JavaScript functions from Ruby, like this:
require 'execjs'
context = ExecJS.compile('function double(x) { return x * 2 }')
p context.call('double', 10)
p context.call('double', 20)
p context.call('double', 30)
Note that if you try calling require
you’ll get an ExecJS::ProgramError
exception. ExecJS doesn’t support CommonJS modules/require, because those interfaces aren’t supported by each of the JavaScript runtimes that ExecJS supports.
What if you want to use modules from npm? Answer: you can use a library called Stitch to package up CommonJS modules, and then compile the resulting source with ExecJS.
Using CommonJS modules
Make sure you have the stitch-rb gem installed:
gem install stitch-rb
Make sure you have some CommonJS structured code to package up. For example you can install the left-pad module using npm with this command:
npm install left-pad
You can then compile the module and call it from Ruby like this:
require 'stitch'
require 'execjs'
modules = Stitch::Package.new(paths: ['node_modules']).compile
context = ExecJS.compile(modules)
p context.eval('this.require("left-pad")(17, 5, "0")')
One small gotcha: you’ll need to use this.require
to access the require function after the modules have been packaged, but you can easily expose this as a global if necessary.
This example uses the node_modules directory as a path because that’s where npm installs modules by default, but you can also use it to package up CommonJS structured JavaScript code defined in your application.