HOWTO execute JavaScript code with Ruby

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.