diff --git a/lib/execjs/external_runtime.rb b/lib/execjs/external_runtime.rb index cd2c784..61b2aae 100644 --- a/lib/execjs/external_runtime.rb +++ b/lib/execjs/external_runtime.rb @@ -9,6 +9,12 @@ module ExecJS @script = "" end + def <<(script) + @script << script + @script << "\n" + self + end + def eval(source, options = {}) if /\S/ =~ source exec("return eval(#{"(#{source})".to_json})") @@ -16,9 +22,10 @@ module ExecJS end def exec(source, options = {}) - if !options[:pure] - @script << source - @script << "\n" + if options[:pure] + source = @script + source + else + self << source source = @script end @@ -27,6 +34,10 @@ module ExecJS end end + def call(properties, *args) + eval "#{properties}.apply(this, #{args.to_json})" + end + protected def compile_to_tempfile(source) tempfile = Tempfile.open("execjs") diff --git a/lib/execjs/ruby_racer_runtime.rb b/lib/execjs/ruby_racer_runtime.rb index c16d5ac..3cd4317 100644 --- a/lib/execjs/ruby_racer_runtime.rb +++ b/lib/execjs/ruby_racer_runtime.rb @@ -23,6 +23,16 @@ module ExecJS end end + def call(properties, *args) + unbox @v8_context.eval(properties).call(*args) + rescue ::V8::JSError => e + if e.value["name"] == "SyntaxError" + raise RuntimeError, e + else + raise ProgramError, e + end + end + def unbox(value) case value when ::V8::Function diff --git a/lib/execjs/ruby_rhino_runtime.rb b/lib/execjs/ruby_rhino_runtime.rb index a436fc5..b0647ed 100644 --- a/lib/execjs/ruby_rhino_runtime.rb +++ b/lib/execjs/ruby_rhino_runtime.rb @@ -23,6 +23,16 @@ module ExecJS end end + def call(properties, *args) + unbox @rhino_context.eval(properties).call(*args) + rescue ::Rhino::JavascriptError => e + if e.message == "syntax error" + raise RuntimeError, e + else + raise ProgramError, e + end + end + def unbox(value) case value when ::Rhino::NativeFunction diff --git a/test/test_execjs.rb b/test/test_execjs.rb index 512b865..1c307bc 100644 --- a/test/test_execjs.rb +++ b/test/test_execjs.rb @@ -28,4 +28,21 @@ class TestExecJS < Test::Unit::TestCase context = ExecJS.compile("foo = function() { return \"bar\"; }") assert_equal "bar", context.eval("foo()", :pure => true) end + + def test_context_call + context = ExecJS.compile("id = function(v) { return v; }") + assert_equal "bar", context.call("id", "bar") + end + + def test_nested_context_call + context = ExecJS.compile("a = {}; a.b = {}; a.b.id = function(v) { return v; }") + assert_equal "bar", context.call("a.b.id", "bar") + end + + def test_context_call_missing_function + context = ExecJS.compile("") + assert_raises ExecJS::ProgramError do + context.call("missing") + end + end end diff --git a/test/test_runtimes.rb b/test/test_runtimes.rb index d27a196..1b0603a 100644 --- a/test/test_runtimes.rb +++ b/test/test_runtimes.rb @@ -33,6 +33,7 @@ module TestRuntime context = @runtime.compile("foo = function() { return \"bar\"; }") assert_equal "bar", context.exec("return foo()") assert_equal "bar", context.eval("foo()") + assert_equal "bar", context.call("foo") end def test_this_is_global_scope