mirror of
https://github.com/rubyjs/therubyrhino
synced 2023-03-27 23:21:34 -04:00
RubyObject/RubyFunction review + specs for better integration when some ruby needs to enter Rhino's JS world ...
This commit is contained in:
parent
0941f932ed
commit
11f4df4be9
9 changed files with 275 additions and 69 deletions
|
@ -7,8 +7,22 @@ module Rhino
|
|||
@callable = callable
|
||||
end
|
||||
|
||||
def call(cxt, scope, this, args)
|
||||
To.javascript @callable.call(*Array(args).map {|a| To.ruby(a)})
|
||||
def unwrap
|
||||
@callable
|
||||
end
|
||||
|
||||
# override Object BaseFunction#call(Context context, Scriptable scope,
|
||||
# Scriptable thisObj, Object[] args)
|
||||
def call(context, scope, this, args)
|
||||
rb_args = To.args_to_ruby(args.to_a)
|
||||
begin
|
||||
result = @callable.call(*rb_args)
|
||||
rescue => e
|
||||
# ... correct wrapping thus it's try { } catch (e) works in JS :
|
||||
raise JS::WrappedException.new(org.jruby.exceptions.RaiseException.new(e))
|
||||
end
|
||||
To.to_javascript(result, scope)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -8,64 +8,89 @@ module Rhino
|
|||
@ruby = object
|
||||
end
|
||||
|
||||
# abstract Object Wrapper#unwrap();
|
||||
def unwrap
|
||||
@ruby
|
||||
end
|
||||
|
||||
def getClassName()
|
||||
# abstract String Scriptable#getClassName();
|
||||
def getClassName
|
||||
@ruby.class.name
|
||||
end
|
||||
|
||||
def getPrototype()
|
||||
Prototype::Generic
|
||||
def toString
|
||||
"[ruby #{getClassName}]" # [object User]
|
||||
end
|
||||
|
||||
def put(key, start, value)
|
||||
if @ruby.respond_to?("#{key}=")
|
||||
@ruby.send("#{key}=", To.ruby(value))
|
||||
value
|
||||
else
|
||||
# override Object Scriptable#get(String name, Scriptable start);
|
||||
# override Object Scriptable#get(int index, Scriptable start);
|
||||
def get(name, start)
|
||||
if name.is_a?(String)
|
||||
# NOTE: preferrably when using a ruby object in JS methods should
|
||||
# be used but instance variables will work as well but if there's
|
||||
# a attr reader it is given a preference e.g. :
|
||||
#
|
||||
# class Foo
|
||||
# attr_reader :bar2
|
||||
# def initialize
|
||||
# @bar1 = 'bar1'
|
||||
# @bar2 = 'bar2'
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# fooObj.bar1; // 'bar1'
|
||||
# fooObj.bar2; // function
|
||||
# fooObj.bar2(); // 'bar2'
|
||||
#
|
||||
if @ruby.respond_to?(name)
|
||||
return RubyFunction.new(@ruby.method(name))
|
||||
elsif @ruby.instance_variables.include?(var_name = "@#{name}")
|
||||
var_value = @ruby.instance_variable_get(var_name)
|
||||
return Rhino::To.to_javascript(var_value, self)
|
||||
end
|
||||
end
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def getIds()
|
||||
@ruby.public_methods(false).map {|m| m.gsub(/(.)_(.)/) {java.lang.String.new("#{$1}#{$2.upcase}")}}.to_java
|
||||
end
|
||||
|
||||
def to_s
|
||||
"[Native #{@ruby.class.name}]"
|
||||
end
|
||||
|
||||
alias_method :prototype, :getPrototype
|
||||
|
||||
|
||||
class Prototype < JS::ScriptableObject
|
||||
|
||||
def get(name, start)
|
||||
robject = To.ruby(start)
|
||||
if name == "toString"
|
||||
return RubyFunction.new(lambda { "[Ruby #{robject.class.name}]"})
|
||||
end
|
||||
rb_name = name.gsub(/([a-z])([A-Z])/) {"#{$1}_#{$2.downcase}"}.to_sym
|
||||
if (robject.public_methods(false).collect(&:to_sym).include?(rb_name))
|
||||
method = robject.method(rb_name)
|
||||
if method.arity == 0
|
||||
To.javascript(method.call)
|
||||
else
|
||||
RubyFunction.new(method)
|
||||
end
|
||||
else
|
||||
super(name, start)
|
||||
end
|
||||
end
|
||||
|
||||
# override boolean Scriptable#has(String name, Scriptable start);
|
||||
# override boolean Scriptable#has(int index, Scriptable start);
|
||||
def has(name, start)
|
||||
rb_name = name.gsub(/([a-z])([A-Z])/) {"#{$1}_#{$2.downcase}"}.to_sym
|
||||
To.ruby(start).public_methods(false).collect(&:to_sym).include?(rb_name) ? true : super(name,start)
|
||||
if name.is_a?(String)
|
||||
if @ruby.respond_to?(name) ||
|
||||
@ruby.instance_variables.include?("@#{name}")
|
||||
return true
|
||||
end
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
Generic = new
|
||||
# override void Scriptable#put(String name, Scriptable start, Object value);
|
||||
# override void Scriptable#put(int index, Scriptable start, Object value);
|
||||
def put(name, start, value)
|
||||
if name.is_a?(String)
|
||||
if @ruby.respond_to?(set_name = "#{name}=")
|
||||
return @ruby.send(set_name, Rhino::To.to_ruby(value))
|
||||
end
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
# override boolean Scriptable#hasInstance(Scriptable instance);
|
||||
def hasInstance(instance)
|
||||
super
|
||||
end
|
||||
|
||||
# override Object[] Scriptable#getIds();
|
||||
def getIds
|
||||
ids = @ruby.instance_variables.map { |ivar| ivar[1..-1].to_java }
|
||||
@ruby.public_methods(false).each do |name|
|
||||
name = name[0...-1] if name[-1, 1] == '=' # 'foo=' ... 'foo'
|
||||
name = name.to_java
|
||||
ids << name unless ids.include?(name)
|
||||
end
|
||||
super.each { |id| ids.unshift(id) }
|
||||
ids.to_java
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,6 +29,10 @@ module Rhino
|
|||
end
|
||||
def javascript(object, scope = nil); to_javascript(object, scope); end # alias
|
||||
|
||||
def args_to_ruby(args)
|
||||
args.map { |arg| to_ruby(arg) }
|
||||
end
|
||||
|
||||
def array_to_ruby(js_array)
|
||||
js_array.length.times.map { |i| to_ruby( js_array.get(i, js_array) ) }
|
||||
end
|
||||
|
|
|
@ -25,7 +25,7 @@ describe Rhino::Context do
|
|||
|
||||
it "allows you to scope the context to an object" do
|
||||
class MyScope
|
||||
def foo; proc { 'bar' }; end
|
||||
def foo; 'bar'; end
|
||||
end
|
||||
Rhino::Context.open(:with => MyScope.new) do |ctx|
|
||||
ctx.eval("foo()").should == 'bar'
|
||||
|
|
40
spec/rhino/ruby_function_spec.rb
Normal file
40
spec/rhino/ruby_function_spec.rb
Normal file
|
@ -0,0 +1,40 @@
|
|||
require File.expand_path('../spec_helper', File.dirname(__FILE__))
|
||||
|
||||
describe Rhino::RubyFunction do
|
||||
|
||||
it "create and unwrap ruby function" do
|
||||
rb_function = Rhino::RubyFunction.new method = Object.new.method(:to_s)
|
||||
rb_function.unwrap.should be(method)
|
||||
end
|
||||
|
||||
it "call a ruby function" do
|
||||
rb_function = Rhino::RubyFunction.new method = 'foo'.method(:to_s)
|
||||
context = nil; scope = nil; this = nil; args = nil
|
||||
rb_function.call(context, scope, this, args).should == 'foo'
|
||||
end
|
||||
|
||||
it "args get converted before delegating a ruby function call" do
|
||||
klass = Class.new(Object) do
|
||||
def foo(array)
|
||||
array.all? { |elem| elem.is_a?(String) }
|
||||
end
|
||||
end
|
||||
rb_function = Rhino::RubyFunction.new method = klass.new.method(:foo)
|
||||
context = nil; scope = nil; this = nil
|
||||
args = [ '1'.to_java, java.lang.String.new('2') ].to_java
|
||||
args = [ Rhino::JS::NativeArray.new(args) ].to_java
|
||||
rb_function.call(context, scope, this, args).should be(true)
|
||||
end
|
||||
|
||||
it "returned value gets converted to javascript" do
|
||||
klass = Class.new(Object) do
|
||||
def foo
|
||||
[ 42 ]
|
||||
end
|
||||
end
|
||||
rb_function = Rhino::RubyFunction.new method = klass.new.method(:foo)
|
||||
context = nil; scope = nil; this = nil; args = [].to_java
|
||||
rb_function.call(context, scope, this, args).should be_a(Rhino::JS::NativeArray)
|
||||
end
|
||||
|
||||
end
|
123
spec/rhino/ruby_object_spec.rb
Normal file
123
spec/rhino/ruby_object_spec.rb
Normal file
|
@ -0,0 +1,123 @@
|
|||
require File.expand_path('../spec_helper', File.dirname(__FILE__))
|
||||
|
||||
describe Rhino::RubyObject do
|
||||
|
||||
it "unwraps a ruby object" do
|
||||
rb_object = Rhino::RubyObject.new object = Object.new
|
||||
rb_object.unwrap.should be(object)
|
||||
end
|
||||
|
||||
class UII < Object
|
||||
end
|
||||
|
||||
it "returns the ruby class name" do
|
||||
rb_object = Rhino::RubyObject.new UII.new
|
||||
rb_object.getClassName.should == UII.name
|
||||
end
|
||||
|
||||
it "reports being a ruby object on toString" do
|
||||
rb_object = Rhino::RubyObject.new UII.new
|
||||
rb_object.toString.should == '[ruby UII]'
|
||||
end
|
||||
|
||||
class UII
|
||||
|
||||
attr_reader :anAttr0
|
||||
attr_accessor :the_attr_1
|
||||
|
||||
def initialize
|
||||
@anAttr0 = nil
|
||||
@the_attr_1 = 'attr_1'
|
||||
@an_attr_2 = 'an_attr_2'
|
||||
end
|
||||
|
||||
def theMethod0; nil; end
|
||||
|
||||
def a_method1; 1; end
|
||||
|
||||
def the_method_2; '2'; end
|
||||
|
||||
end
|
||||
|
||||
it "gets methods and instance variables" do
|
||||
rb_object = Rhino::RubyObject.new UII.new
|
||||
|
||||
rb_object.get('anAttr0', nil).should be_a(Rhino::RubyFunction)
|
||||
rb_object.get('the_attr_1', nil).should be_a(Rhino::RubyFunction)
|
||||
rb_object.get('an_attr_2', nil).should == 'an_attr_2'
|
||||
|
||||
[ 'theMethod0', 'a_method1', 'the_method_2' ].each do |name|
|
||||
rb_object.get(name, nil).should be_a(Rhino::RubyFunction)
|
||||
end
|
||||
|
||||
rb_object.get('non-existent-method', nil).should be(Rhino::JS::Scriptable::NOT_FOUND)
|
||||
end
|
||||
|
||||
it "has methods and instance variables" do
|
||||
rb_object = Rhino::RubyObject.new UII.new
|
||||
|
||||
rb_object.has('anAttr0', nil).should be_true
|
||||
rb_object.has('the_attr_1', nil).should be_true
|
||||
rb_object.has('an_attr_2', nil).should be_true
|
||||
|
||||
[ 'theMethod0', 'a_method1', 'the_method_2' ].each do |name|
|
||||
rb_object.has(name, nil).should be_true
|
||||
end
|
||||
|
||||
rb_object.has('non-existent-method', nil).should be_false
|
||||
end
|
||||
|
||||
it "puts using attr writer" do
|
||||
start = mock('start')
|
||||
start.expects(:put).never
|
||||
rb_object = Rhino::RubyObject.new UII.new
|
||||
|
||||
rb_object.put('the_attr_1', start, 42)
|
||||
rb_object.the_attr_1.should == 42
|
||||
end
|
||||
|
||||
it "puts a non-existent attr (delegates to start)" do
|
||||
start = mock('start')
|
||||
start.expects(:put).once
|
||||
rb_object = Rhino::RubyObject.new UII.new
|
||||
|
||||
rb_object.put('nonExistingAttr', start, 42)
|
||||
end
|
||||
|
||||
it "getIds include ruby class methods" do
|
||||
rb_object = Rhino::RubyObject.new UII.new
|
||||
|
||||
[ 'anAttr0', 'the_attr_1', 'an_attr_2' ].each do |attr|
|
||||
rb_object.getIds.to_a.should include(attr)
|
||||
end
|
||||
[ 'theMethod0', 'a_method1', 'the_method_2' ].each do |method|
|
||||
rb_object.getIds.to_a.should include(method)
|
||||
end
|
||||
end
|
||||
|
||||
it "getIds include ruby instance methods" do
|
||||
rb_object = Rhino::RubyObject.new object = UII.new
|
||||
object.instance_eval do
|
||||
def foo; 'foo'; end
|
||||
end
|
||||
|
||||
rb_object.getIds.to_a.should include('foo')
|
||||
end
|
||||
|
||||
it "getIds include writers as attr names" do
|
||||
rb_object = Rhino::RubyObject.new object = UII.new
|
||||
|
||||
rb_object.getIds.to_a.should include('the_attr_1')
|
||||
rb_object.getIds.to_a.should_not include('the_attr_1=')
|
||||
|
||||
object.instance_eval do
|
||||
def foo=(foo)
|
||||
'foo'
|
||||
end
|
||||
end
|
||||
|
||||
rb_object.getIds.to_a.should include('foo')
|
||||
rb_object.getIds.to_a.should_not include('foo=')
|
||||
end
|
||||
|
||||
end
|
|
@ -141,24 +141,22 @@ describe Rhino::To do
|
|||
end
|
||||
end
|
||||
|
||||
it "creates a prototype for the object based on its class" do
|
||||
Class.new.tap do |klass|
|
||||
klass.class_eval do
|
||||
def foo(one, two)
|
||||
"1: #{one}, 2: #{two}"
|
||||
end
|
||||
end
|
||||
|
||||
Rhino::To.javascript(klass.new).tap do |o|
|
||||
o.should be_kind_of(Rhino::RubyObject)
|
||||
o.prototype.tap do |p|
|
||||
p.should_not be_nil
|
||||
p.get("foo", p).should_not be_nil
|
||||
p.get("toString", p).should_not be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
# it "creates a prototype for the object based on its class" do
|
||||
# klass = Class.new do
|
||||
# def foo(one, two)
|
||||
# "1: #{one}, 2: #{two}"
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Rhino::To.javascript(klass.new).tap do |o|
|
||||
# o.should be_kind_of(Rhino::RubyObject)
|
||||
# o.prototype.tap do |p|
|
||||
# p.should_not be_nil
|
||||
# p.get("foo", p).should_not be_nil
|
||||
# p.get("toString", p).should_not be_nil
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
|
||||
require 'mocha'
|
||||
require 'rhino'
|
||||
|
|
|
@ -17,5 +17,6 @@ Gem::Specification.new do |s|
|
|||
|
||||
s.add_development_dependency "rake"
|
||||
s.add_development_dependency "rspec"
|
||||
s.add_development_dependency "mocha"
|
||||
s.add_development_dependency "jruby-openssl"
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue