diff --git a/lib/execjs.rb b/lib/execjs.rb index 6793c7b..db7c48f 100644 --- a/lib/execjs.rb +++ b/lib/execjs.rb @@ -1,3 +1,7 @@ module ExecJS + class Error < ::StandardError; end + class RuntimeError < Error; end + class ProgramError < Error; end + autoload :Runtimes, "execjs/runtimes" end diff --git a/lib/execjs/runtimes/node.js b/lib/execjs/runtimes/node.js new file mode 100644 index 0000000..c2e3df6 --- /dev/null +++ b/lib/execjs/runtimes/node.js @@ -0,0 +1,20 @@ +(function(program, execJS) { execJS(program) })(function() { #{source} +}, function(program) { + var output, print = function(string) { + process.stdout.write('' + string); + }; + try { + result = program(); + if (typeof result == 'undefined' && result !== null) { + print('["ok"]'); + } else { + try { + print(JSON.stringify(['ok', result])); + } catch (err) { + print('["err"]'); + } + } + } catch (err) { + print(JSON.stringify(['err', '' + err])); + } +}); diff --git a/lib/execjs/runtimes/node.rb b/lib/execjs/runtimes/node.rb index ae3e6be..6b67bc3 100644 --- a/lib/execjs/runtimes/node.rb +++ b/lib/execjs/runtimes/node.rb @@ -1,26 +1,58 @@ +require "json" +require "tempfile" + module ExecJS module Runtimes - class Node + module Node + extend self + def exec(source) + compile_to_tempfile(source) do |file| + extract_result(exec_runtime("node #{file.path}")) + end + end + + def eval(source) + if /\S/ =~ source + exec("return eval(#{"(#{source})".to_json})") + end + end + + protected + def compile_to_tempfile(source) + tempfile = Tempfile.open("execjs") + tempfile.write compile(source) + tempfile.close + yield tempfile + ensure + tempfile.close! + end + + def compile(source) + wrapper.sub('#{source}', source) + end + + def wrapper + @wrapper ||= IO.read(File.expand_path('../node.js', __FILE__)) + end + + def exec_runtime(command) + output = `#{command} 2>&1` + if $?.success? + output + else + raise RuntimeError, output + end + end + + def extract_result(output) + status, value = output.empty? ? [] : JSON.parse(output) + if status == "ok" + value + else + raise ProgramError, value + end + end end end end - -__END__ -(function(program, execJS) { execJS(program) })(function() { #{source} -}, function(program) { - var result, output; - try { - result = program(); - try { - output = JSON.stringify(result); - sys.print('ok '); - sys.print(output); - } catch (err) { - sys.print('err'); - } - } catch (err) { - sys.print('err '); - sys.print(err); - } -}); diff --git a/readme.md b/readme.md index 109d37d..066d367 100644 --- a/readme.md +++ b/readme.md @@ -19,5 +19,5 @@ ExecJS supports these runtimes: A short example: require "execjs" - ExecJS.exec "'red yellow blue'.split('')" + ExecJS.eval "'red yellow blue'.split('')" # => ["red", "yellow", "blue"] diff --git a/test/test_node_runtime.rb b/test/test_node_runtime.rb index e69de29..cb39a7b 100644 --- a/test/test_node_runtime.rb +++ b/test/test_node_runtime.rb @@ -0,0 +1,44 @@ +require "execjs" +require "test/unit" + +class TestNodeRuntime < Test::Unit::TestCase + def setup + @runtime = ExecJS::Runtimes::Node + end + + def test_exec + assert_nil @runtime.exec("1") + assert_nil @runtime.exec("return") + assert_nil @runtime.exec("return null") + assert_nil @runtime.exec("return function() {}") + assert_equal 0, @runtime.exec("return 0") + assert_equal true, @runtime.exec("return true") + assert_equal [1, 2], @runtime.exec("return [1, 2]") + assert_equal "hello", @runtime.exec("return 'hello'") + assert_equal({"a"=>1,"b"=>2}, @runtime.exec("return {a:1,b:2}")) + end + + def test_eval + assert_nil @runtime.eval("") + assert_nil @runtime.eval(" ") + assert_nil @runtime.eval("null") + assert_nil @runtime.eval("function() {}") + assert_equal 0, @runtime.eval("0") + assert_equal true, @runtime.eval("true") + assert_equal [1, 2], @runtime.eval("[1, 2]") + assert_equal "hello", @runtime.eval("'hello'") + assert_equal({"a"=>1,"b"=>2}, @runtime.eval("{a:1,b:2}")) + end + + def test_syntax_error + assert_raise ExecJS::RuntimeError do + @runtime.exec(")") + end + end + + def test_thrown_exception + assert_raise ExecJS::ProgramError do + @runtime.exec("throw 'hello'") + end + end +end