I often get side tracked seeing JRuby as just a cure for Rails deployment issues. Although it is huge to think of a fast Ruby container, and having access to JVM internals for management and such, JRuby also has a fantastic set of Java integration features.
This means that while you are in Java-land, you can actually hide Ruby implementations of features behind nice Java interfaces. Rob Harrop showed off some of this at his JRuby DSL talk at JavaOne last Friday.
Here is a simple example of having Java code doing some kind of calculation, and behind the scenes the implementation is in Ruby.
The pieces are:
- Java client code that does the calculation
- Factory to return the object that can do the calculation
- Calculation interface
- The Ruby object that does the work
- The Ruby file that makes the object implement the interface
Java Client Code
Calculator c = CalculatorFactory.getCalculator();
String calculation = "1 + 3";
System.out.println("Calculate(" + calculation + ") = " + c.calculate(calculation));
Calculation interface
public interface Calculator {
public String calculate(String calculation);
}
Calculator Factory
The factory loads up the Ruby code, and then munges the Ruby object to return it back to Java-land as the Calculator interface:
public static Calculator getCalculator() {
Ruby runtime = Ruby.getDefaultInstance();
try {
runtime.evalScript(getContents("rcalculator.rb"));
runtime.evalScript(getContents("makejcalculator.rb"));
} catch (Exception e) {
System.err.println(e.toString());
e.printStackTrace();
}
Object c = runtime.evalScript("RCalculator.new");
c = JavaEmbedUtils.rubyToJava(runtime, (IRubyObject) c, Calculator.class);
return (Calculator) c;
}
The magic is done in JavaEmbedUtils.rubyToJava(runtime, (IRubyObject) c, Calculator.class);
.
Ruby implementation of Calculator
The Ruby implementation is simple:
class RCalculator
def calculate(s)
return eval(s).to_s
end
end
To make it implement Calculator we monkey patch it by also loading the makejcalculator.rb file:
require 'java'
class RCalculator
include Java::Calculator
end
You could obviously do this all at once, but this pattern can allow you to share the Ruby code, and you may have other Ruby that you can’t just hack on.
This pattern will be common, so having a java_implement RCalculator, Java::Calculator
simple piece of meta-code is useful.
All in all, JRuby gives you everything you need to do as much of your business logic in Ruby, and never expose it. This gives me thoughts of working on a Java team and telling my team mates about the interfaces they can use, and writing the entire app in Ruby behind the scenes ;)