Sometime I want to use a ruby library and implement something in ruby code so I could use the cool ruby features. JRuby provide the capability to run ruby code in Java. Therefore I could implement Java interface in Ruby.
1. Create the Java Interface
public interface Animal
{
public String speak();
public String move();
}
2. Implement the interface in Ruby
class Animal
def move
"Move..."
end
def speak
"Speak..."
end
end
3. Create a factory to create the concrete class of the interface
import org.jruby.Ruby;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.javasupport.JavaEmbedUtils;
import java.lang.reflect.Method;
import java.io.InputStream;
import java.io.File;
public class RubyFactory
{
public static final String RUBY_src="ruby";
public static <T> T getBean(Class<T> type)
{
Ruby runtime = Ruby.getDefaultInstance();
try
{
runtime.evalScript(runtime.evalScript("File.open('" + RUBY_SRC + File.separator + type.getSimpleName().toLowerCase() + ".rb').read").toString());
runtime.evalScript(extendRubyScript(type));
}
catch (Exception e)
{
System.err.println(e.toString());
}
Object c = runtime.evalScript(type.getSimpleName() + ".new");
c = JavaEmbedUtils.rubyToJava(runtime, (IRubyObject) c, type);
return (T) c;
}
private static String extendRubyScript(Class type)
{
String rubyScript = "require 'java'\n" +
"class " + type.getSimpleName() + "\n" +
" include Java::" + type.getName().replace(".", "::") + "\n";
Method[] methods = type.getMethods();
for (Method method : methods)
{
String name = method.getName();
rubyScript += " eval(\"alias " + name + " #{'" + name + "'.gsub(/([A-Z]+)([A-Z][a-z])/,'\\1_\\2').gsub(/([a-z\\d])([A-Z])/,'\\1_\\2').tr('-', '_').downcase}\")\n";
}
rubyScript += "end";
return rubyScript;
} public static void main(String[] args)
{
Animal animal = RubyFactory.getBean(Animal.class);
System.out.println(animal.move());
System.out.println(animal.speak());
}
}
All the ruby files should be put into the ruby directory. I follow the naming convertion as following:
interface Greeting -> greeting.rb with class Greeting
method sayGoodbye in java -> method say_goodbye in ruby
4. use the factory above, we could easily to implement another java interface.
public interface Greeting
{
public String sayGoodbye();
public String sayHello();
}
5. The implementation of interface greeting.
class Greeting
def say_hello()
"Hello"
end
def say_goodbye()
"Goodbye"
end
end
6. Test Greeting.
public class GreetingTest extends TestCase
{
private Greeting greeting;
@Before
protected void setUp() throws Exception
{
greeting = RubyFactory.getBean(Greeting.class);
}
@Test
public void testSayHello()
{
assertEquals("Hello", greeting.sayHello());
}
@Test
public void testSayGoodbye()
{
assertEquals("Goodbye", greeting.sayGoodbye());
}
}
In order to run the application, you need to include asm-2.2.3.jar, asm-commons-2.2.3.jar, backport-util-concurrent.jar and jruby-complete-1.0.1.jar
download source code