Skip to content

Commit

Permalink
Improve JRuby interop implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Mike Ragalie committed Sep 26, 2013
1 parent fc0a706 commit 957af11
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 42 deletions.
18 changes: 18 additions & 0 deletions language-adaptors/rxjava-jruby/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apply plugin: 'osgi'

dependencies {
compile project(':rxjava-core')
compile 'org.jruby:jruby:1.7+'
provided 'junit:junit-dep:4.10'
provided 'org.mockito:mockito-core:1.8.5'
}

jar {
manifest {
name = 'rxjava-jruby'
instruction 'Bundle-Vendor', 'Netflix'
instruction 'Bundle-DocURL', 'https://github.com/Netflix/RxJava'
instruction 'Import-Package', '!org.junit,!junit.framework,!org.mockito.*,*'
instruction 'Fragment-Host', 'com.netflix.rxjava.core'
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
package rx.lang.jruby;

import org.jruby.RubyProc;
import org.jruby.Ruby;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.javasupport.JavaUtil;

import rx.util.functions.Action;
import rx.util.functions.Action0;
import rx.util.functions.Action1;
Expand All @@ -32,30 +37,41 @@
*/
public class JRubyActionWrapper<T1, T2, T3, T4> implements Action, Action0, Action1<T1>, Action2<T1, T2>, Action3<T1, T2, T3> {

private final RubyProc<Void> proc;
private final RubyProc proc;
private final ThreadContext context;
private final Ruby runtime;

public GroovyActionWrapper(RubyProc<Void> proc) {
public JRubyActionWrapper(ThreadContext context, RubyProc proc) {
this.proc = proc;
this.context = context;
this.runtime = context.getRuntime();
}

@Override
public void call() {
proc.call();
IRubyObject[] array = new IRubyObject[0];
proc.call(context, array);
}

@Override
public void call(T1 t1) {
proc.call(t1);
IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1)};
proc.call(context, array);
}

@Override
public void call(T1 t1, T2 t2) {
proc.call(t1, t2);
IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1),
JavaUtil.convertJavaToRuby(runtime, t2)};
proc.call(context, array);
}

@Override
public void call(T1 t1, T2 t2, T3 t3) {
proc.call(t1, t2, t3);
IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1),
JavaUtil.convertJavaToRuby(runtime, t2),
JavaUtil.convertJavaToRuby(runtime, t3)};
proc.call(context, array);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
package rx.lang.jruby;

import org.jruby.RubyProc;
import org.jruby.Ruby;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.javasupport.JavaUtil;

import rx.util.functions.Func0;
import rx.util.functions.Func1;
import rx.util.functions.Func2;
Expand Down Expand Up @@ -51,64 +56,118 @@ public class JRubyFunctionWrapper<T1, T2, T3, T4, T5, T6, T7, T8, T9, R> impleme
Func9<T1, T2, T3, T4, T5, T6, T7, T8, T9, R>,
FuncN<R> {

private final RubyProc<R> proc;
private final RubyProc proc;
private final ThreadContext context;
private final Ruby runtime;

public GroovyFunctionWrapper(RubyProc<R> proc) {
public JRubyFunctionWrapper(ThreadContext context, RubyProc proc) {
this.proc = proc;
this.context = context;
this.runtime = context.getRuntime();
}

@Override
public R call() {
return (R) proc.call();
IRubyObject[] array = new IRubyObject[0];
return (R) proc.call(context, array);
}

@Override
public R call(T1 t1) {
return (R) proc.call(t1);
IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1)};
return (R) proc.call(context, array);
}

@Override
public R call(T1 t1, T2 t2) {
return (R) proc.call(t1, t2);
IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1),
JavaUtil.convertJavaToRuby(runtime, t2)};
return (R) proc.call(context, array);
}

@Override
public R call(T1 t1, T2 t2, T3 t3) {
return (R) proc.call(t1, t2, t3);
IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1),
JavaUtil.convertJavaToRuby(runtime, t2),
JavaUtil.convertJavaToRuby(runtime, t3)};
return (R) proc.call(context, array);
}

@Override
public R call(T1 t1, T2 t2, T3 t3, T4 t4) {
return (R) proc.call(t1, t2, t3, t4);
IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1),
JavaUtil.convertJavaToRuby(runtime, t2),
JavaUtil.convertJavaToRuby(runtime, t3),
JavaUtil.convertJavaToRuby(runtime, t4)};
return (R) proc.call(context, array);
}

@Override
public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) {
return (R) proc.call(t1, t2, t3, t4, t5);
IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1),
JavaUtil.convertJavaToRuby(runtime, t2),
JavaUtil.convertJavaToRuby(runtime, t3),
JavaUtil.convertJavaToRuby(runtime, t4),
JavaUtil.convertJavaToRuby(runtime, t5)};
return (R) proc.call(context, array);
}

@Override
public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) {
return (R) proc.call(t1, t2, t3, t4, t5, t6);
IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1),
JavaUtil.convertJavaToRuby(runtime, t2),
JavaUtil.convertJavaToRuby(runtime, t3),
JavaUtil.convertJavaToRuby(runtime, t4),
JavaUtil.convertJavaToRuby(runtime, t5),
JavaUtil.convertJavaToRuby(runtime, t6)};
return (R) proc.call(context, array);
}

@Override
public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7) {
return (R) proc.call(t1, t2, t3, t4, t5, t6, t7);
IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1),
JavaUtil.convertJavaToRuby(runtime, t2),
JavaUtil.convertJavaToRuby(runtime, t3),
JavaUtil.convertJavaToRuby(runtime, t4),
JavaUtil.convertJavaToRuby(runtime, t5),
JavaUtil.convertJavaToRuby(runtime, t6),
JavaUtil.convertJavaToRuby(runtime, t7)};
return (R) proc.call(context, array);
}

@Override
public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8) {
return (R) proc.call(t1, t2, t3, t4, t5, t6, t7, t8);
IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1),
JavaUtil.convertJavaToRuby(runtime, t2),
JavaUtil.convertJavaToRuby(runtime, t3),
JavaUtil.convertJavaToRuby(runtime, t4),
JavaUtil.convertJavaToRuby(runtime, t5),
JavaUtil.convertJavaToRuby(runtime, t6),
JavaUtil.convertJavaToRuby(runtime, t7),
JavaUtil.convertJavaToRuby(runtime, t8)};
return (R) proc.call(context, array);
}

@Override
public R call(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9) {
return (R) proc.call(t1, t2, t3, t4, t5, t6, t7, t8, t9);
IRubyObject[] array = {JavaUtil.convertJavaToRuby(runtime, t1),
JavaUtil.convertJavaToRuby(runtime, t2),
JavaUtil.convertJavaToRuby(runtime, t3),
JavaUtil.convertJavaToRuby(runtime, t4),
JavaUtil.convertJavaToRuby(runtime, t5),
JavaUtil.convertJavaToRuby(runtime, t6),
JavaUtil.convertJavaToRuby(runtime, t7),
JavaUtil.convertJavaToRuby(runtime, t8),
JavaUtil.convertJavaToRuby(runtime, t9)};
return (R) proc.call(context, array);
}

@Override
public R call(Object... args) {
return (R) proc.call(args);
IRubyObject[] array = new IRubyObject[args.length];
for (int i = 0; i < args.length; i++) {
array[i] = JavaUtil.convertJavaToRuby(runtime, args[i]);
}
return (R) proc.call(context, array);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
package rx.lang.jruby;

import org.jruby.RubyProc;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.javasupport.JavaUtil;

import rx.Observable.OnSubscribeFunc;
import rx.Observer;
import rx.Subscription;
Expand All @@ -27,15 +31,18 @@
*/
public class JRubyOnSubscribeFuncWrapper<T> implements OnSubscribeFunc<T> {

private final RubyProc<Subscription> proc;
private final RubyProc proc;
private final ThreadContext context;

public GroovyOnSubscribeFuncWrapper(RubyProc<Subscription> proc) {
public JRubyOnSubscribeFuncWrapper(ThreadContext context, RubyProc proc) {
this.proc = proc;
this.context = context;
}

@Override
public Subscription onSubscribe(Observer<? super T> observer) {
return proc.call(observer);
IRubyObject[] array = {JavaUtil.convertJavaToRuby(context.getRuntime(), observer)};
return (Subscription) proc.call(context, array);
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
require 'pp'

klasses = [Java::Rx::Observable, Java::RxObservables::BlockingObservable]
function = Java::RxUtilFunctions::Function.java_class

WRAPPERS = {
Java::RxUtilFunctions::Action.java_class => Java::RxLangJruby::JRubyActionWrapper,
Java::RxObservable::OnSubscribeFunc.java_class => Java::RxLangJruby::JRubyOnSubscribeFuncWrapper
Java::RxUtilFunctions::Action => Java::RxLangJruby::JRubyActionWrapper,
Java::Rx::Observable::OnSubscribeFunc => Java::RxLangJruby::JRubyOnSubscribeFuncWrapper
}

WRAPPERS.default = Java::RxLangJruby::JRubyFunctionWrapper
Expand All @@ -13,30 +15,49 @@
method.public? && method.parameter_types.any? {|type| function.assignable_from?(type)}
end

parameter_types = function_methods.group_by(&:name).each_with_object({}) do |(method_name, methods), memo|
types = methods.map(&:parameter_types).select {|type| function.assignable_from?(type)}.flatten.uniq
raise ArgumentError, "More than one function type for #{method_name}" if types.length > 1
parameter_types = {}
function_methods.each do |method|
parameter_types[method.name] ||= []

method.parameter_types.each_with_index do |type, idx|
next unless function.assignable_from?(type)

constructor = WRAPPERS.find do |java_class, wrapper|
type.ruby_class.ancestors.include?(java_class)
end

constructor = (constructor && constructor.last) || WRAPPERS.default

memo[method_name] = WRAPPERS[types.first]
parameter_types[method.name][idx] ||= []
parameter_types[method.name][idx] << constructor
end
end

function_methods.map(&:name).uniq.each do |method_name|
klass.class_eval <<EOS
def #{method_name}(*args, &block)
args.map! do |arg|
if arg.is_a?(Proc)
#{parameter_types[method_name]}.new(arg)
else
arg
end
end
parameter_types.each_pair do |method_name, types|
types.map! do |type|
next type.first if type && type.uniq.length == 1
nil
end
end

parameter_types.each_pair do |method_name, types|
next if types.all?(&:nil?)

if block_given?
block = #{parameter_types[method_name]}.new(block)
klass.send(:alias_method, "#{method_name}_without_wrapping", method_name)
klass.send(:define_method, method_name) do |*args, &block|
args = args.each_with_index.map do |arg, idx|
if arg.is_a?(Proc) && types[idx]
types[idx].new(JRuby.runtime.get_current_context, arg)
else
arg
end
end

super(*args, &block)
if block && types[args.length]
block = types[args.length].new(JRuby.runtime.get_current_context, block)
end
EOS

send("#{method_name}_without_wrapping", *(args + [block].compact))
end
end
end
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ rootProject.name='rxjava'
include 'rxjava-core', \
'language-adaptors:rxjava-groovy', \
'language-adaptors:rxjava-clojure', \
'language-adaptors:rxjava-jruby', \
'language-adaptors:rxjava-scala', \
'language-adaptors:rxjava-scala-java', \
'rxjava-contrib:rxjava-swing', \
Expand Down

0 comments on commit 957af11

Please sign in to comment.