I have a rails application that needs to run a node script. i imagine that using the ExecJS gem is the cleanest way to run js from a rails app. However, so far, ExecJS has proved to be very frustrating to use.
here is the script I need to run:
// Generated by CoffeeScript 1.7.1
(function() {
var PDFDocument, doc, fs;
fs = require("fs");
PDFDocument = require('pdfkit');
doc = new PDFDocument;
doc.pipe(fs.createWriteStream('output.pdf'));
doc.addPage().fontSize(25).text('Here is some vector graphics...', 100, 100);
doc.save().moveTo(100, 150).lineTo(100, 250).lineTo(200, 250).fill("#FF3300");
doc.scale(0.6).translate(470, -380).path('M 250,75 L 323,301 131,161 369,161 177,301 z').fill('red', 'even-odd').restore();
doc.addPage().fillColor("blue").text('Here is a link!', 100, 100).underline(100, 100, 160, 27, {
color: "#0000FF"
}).link(100, 100, 160, 27, 'http://google.com/');
doc.end();
}).call(this)
From my rails console, I try this:
[2] pry(main)> file = File.open('test.js').read
[3] pry(main)> ExecJS.eval(file)
ExecJS::ProgramError: TypeError: undefined is not a function
from /Users/matt/.rvm/gems/ruby-2.1.0/gems/execjs-2.0.2/lib/execjs/external_runtime.rb:68:in `extract_result'
Note that I can run this script successfully using 'node test.js' and I am also able to run run the script using the backtick syntax ruby offers:
`node test.js`
But that feels like a hack...
Any takers?
It's erroring out because require() is not supported by EvalJS. 'require' is undefined, and undefined is not a function. ;)
I'm not sure of the answer but maybe you need to precise the exec_js_runtime environment variable to be node.
Something like ENV['EXECJS_RUNTIME'] = 'Node'
You can try to put it in the config/boot.rb or just to define the EXECJS_RUNTIME in your environment, something like export EXECJS_RUNTIME=Node
Hope it helps
ExecJS people say use commonjs.rb
https://github.com/cowboyd/commonjs.rb
Why can't I use CommonJS require() inside ExecJS?
ExecJS provides a lowest common denominator interface to any
JavaScript runtime. Use ExecJS when it doesn't matter which JavaScript
interpreter your code runs in. If you want to access the Node API, you
should check another library like commonjs.rb designed to provide a
consistent interface.
But this doesn't work basically. The require
acts completely erratically - I had to execute npm -g install pdfkit fs
between env =
and env.require
in
require 'v8'
require 'commonjs'
env = CommonJS::Environment.new(V8::Context.new, path: ::Rails.root )
env.require 'script'
for the module lookup to work O.o and if I tried pointing path
to the node_modules
folder then it would be impossible for the gem to find script
(not to mention that the #new
and require
are basically the only documented methods - only methods afaik - and #new
is misdocumented :P)
Your options as far as I can tell:
system(node ...)
- you can use Cocaine to escape some gotcha's (piping output, error handling, performance tweaks, ...) and run a cleaner syntax - this is not as bad as it looks - this is how paperclip does image postprocessing (imagemagick
system package + cocaine
) so I guess it's very stable and very doable
- expose to web api and run a separate worker on a free heroku dyno for example to do this and similar stuff you want to do with node libs
- use
prawn
:)