From 33ef76f85e5341a856d426f019c7c4d2b8abbf5d Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Tue, 6 Oct 2009 08:52:45 -0500 Subject: [PATCH] add native object wrapper for easy access to js objects from ruby --- lib/rhino.rb | 1 + lib/rhino/context.rb | 37 +++++++++++++++---- lib/rhino/java.rb | 9 +++++ lib/rhino/native_object.rb | 19 ++++++++++ spec/rhino/context_spec.rb | 61 +++++++++++++++++++++++++++----- spec/rhino/native_object_spec.rb | 31 ++++++++++++++++ 6 files changed, 143 insertions(+), 15 deletions(-) create mode 100644 lib/rhino/native_object.rb create mode 100644 spec/rhino/native_object_spec.rb diff --git a/lib/rhino.rb b/lib/rhino.rb index ac610ef..3be4781 100644 --- a/lib/rhino.rb +++ b/lib/rhino.rb @@ -6,4 +6,5 @@ module Rhino VERSION = '1.72.0' require 'rhino/java' require 'rhino/context' + require 'rhino/native_object' end \ No newline at end of file diff --git a/lib/rhino/context.rb b/lib/rhino/context.rb index 5727e92..a8bb9fc 100644 --- a/lib/rhino/context.rb +++ b/lib/rhino/context.rb @@ -7,22 +7,45 @@ module Rhino class Context class << self + def open + J::ContextFactory.new.call do |native| + yield new(native) + end + end + + def to_scriptable(object) + case object + when NativeObject then object.j + when J::Scriptable then object + else + #wrap ruby object into ScriptableRubyObject + end + end + + def to_ruby(object) + object.class <= J::Scriptable ? NativeObject.new(object) : object + end + private :new end def initialize(native) #:nodoc: @native = native end - - def self.open - J::ContextFactory.new.call do |native| - yield new(native) + + def init_standard_objects(options = {}) + NativeObject.new(@native.initStandardObjects(nil, options[:sealed] == true)).tap do |objects| + unless options[:java] + for package in ["Packages", "java", "org", "com"] + objects.j.delete(package) + end + end end end def evaljs(str, scope = @native.initStandardObjects()) begin - @native.evaluateString(scope, str, "", 1, nil) + Context.to_ruby(@native.evaluateString(Context.to_scriptable(scope), str, "", 1, nil)) rescue J::RhinoException => e raise Rhino::RhinoError, e end @@ -33,7 +56,7 @@ module Rhino end end - + class Function < J::BaseFunction def initialize(&block) @block = block @@ -55,7 +78,7 @@ module Rhino end def javascript_backtrace - @native.script_stack_trace + @native.getScriptStackTrace() end end end \ No newline at end of file diff --git a/lib/rhino/java.rb b/lib/rhino/java.rb index 2d86427..741ba7d 100644 --- a/lib/rhino/java.rb +++ b/lib/rhino/java.rb @@ -5,4 +5,13 @@ module Rhino module J import "org.mozilla.javascript" end +end + +unless Object.method_defined?(:tap) + class Object + def tap + yield self + self + end + end end \ No newline at end of file diff --git a/lib/rhino/native_object.rb b/lib/rhino/native_object.rb new file mode 100644 index 0000000..bad04ce --- /dev/null +++ b/lib/rhino/native_object.rb @@ -0,0 +1,19 @@ + +module Rhino + class NativeObject + attr_reader :j + def initialize(j) + @j = j + end + + def [](k) + if v = @j.get(k.to_s,@j) + v == J::Scriptable::NOT_FOUND ? nil : Context.to_ruby(v) + end + end + + def []=(k,v) + @j.put(k.to_s,@j,v) + end + end +end \ No newline at end of file diff --git a/spec/rhino/context_spec.rb b/spec/rhino/context_spec.rb index 641d1e8..52860f5 100644 --- a/spec/rhino/context_spec.rb +++ b/spec/rhino/context_spec.rb @@ -1,25 +1,71 @@ require File.dirname(__FILE__) + '/../spec_helper' +include Rhino + describe Rhino::Context do - include Rhino it "can evaluate some javascript" do - Rhino::Context.open do |cxt| + Context.open do |cxt| cxt.evaljs("5 + 3").should == 8 end end it "can embed ruby object into javascript" do - Rhino::Context.open do |cxt| - cxt.standard do |scope| - scope.put("foo", scope, "Hello World") + Context.open do |cxt| + cxt.init_standard_objects.tap do |scope| + scope["foo"] = "Hello World" cxt.evaljs("foo", scope).should == "Hello World" end end + end + + describe "Initalizing Standard Javascript Objects" do + it "provides the standard objects without java integration by default" do + Context.open do |cxt| + cxt.init_standard_objects.tap do |scope| + scope["Object"].should_not be_nil + scope["Math"].should_not be_nil + scope["String"].should_not be_nil + scope["Function"].should_not be_nil + scope["Packages"].should be_nil + scope["java"].should be_nil + scope["org"].should be_nil + scope["com"].should be_nil + end + end + end + + it "provides unsealed standard object by default" do + Context.open do |cxt| + cxt.init_standard_objects.tap do |scope| + cxt.evaljs("Object.foop = 'blort'", scope) + scope["Object"]['foop'].should == 'blort' + end + end + end + + it "allows you to seal the standard objects so that they cannot be modified" do + Context.open do |cxt| + cxt.init_standard_objects(:sealed => true).tap do |scope| + lambda { + cxt.evaljs("Object.foop = 'blort'", scope) + }.should raise_error(Rhino::RhinoError) + end + end + end + + it "allows java integration to be turned on when initializing standard objects" do + Context.open do |cxt| + cxt.init_standard_objects(:java => true).tap do |scope| + scope["Packages"].should_not be_nil + end + end + end end + it "can call ruby functions from javascript" do - Rhino::Context.open do |cxt| + Context.open do |cxt| cxt.standard do |scope| scope.put("say", scope, function {|word, times| word * times}) cxt.evaljs("say('Hello',2)", scope).should == "HelloHello" @@ -29,8 +75,7 @@ describe Rhino::Context do it "has a private constructor" do lambda { - Rhino::Context.new(nil) + Context.new(nil) }.should raise_error end - end \ No newline at end of file diff --git a/spec/rhino/native_object_spec.rb b/spec/rhino/native_object_spec.rb new file mode 100644 index 0000000..8cac9f0 --- /dev/null +++ b/spec/rhino/native_object_spec.rb @@ -0,0 +1,31 @@ + +require File.dirname(__FILE__) + '/../spec_helper' + +include Rhino + +describe Rhino::NativeObject do + + before(:each) do + @j = J::NativeObject.new + @o = NativeObject.new(@j) + end + + it "wraps a native javascript object" do + @o["foo"] = 'bar' + @j.get("foo", @j).should == "bar" + @j.put("blue",@j, "blam") + @o["blue"].should == "blam" + end + + it "doesn't matter if you use a symbol or a string to set a value" do + @o[:foo] = "bar" + @o['foo'].should == "bar" + @o['baz'] = "bang" + @o[:baz].should == "bang" + end + + it "returns nil when the value is null, null, or not defined" do + @o[:foo].should be_nil + end + +end