However, how difficult is it to interface between two non-Java JVM languages? It turns out that it isn't really that hard at all! For this post, we'll talk about how to interface between JRuby and Scala, it's really rather simple.
So here's our scenario. We are writing our main app in Scala, because Scala is faster than Ruby. However there are some situations where we don't want to have to recompile the whole thing to make changes, and we want the code to be nice and easy to understand for people who don't know Scala (admittedly, Scala is not a good first language as it is rather complex). We're going to embed some Ruby scripts within our app.
Here's a basic Scala outline in ScalaTest.scala:
import javax.script._ // import Java's scripting APIAnd test.rb:
import java.io.FileReader
object ScalaTest extends Application {
var engine = (new ScriptEngineManager).getEngineByName("jruby")
engine.eval(new FileReader("test.rb"))
}
puts "Hello from Ruby!"Before we attempt to run this though, we'll need to get both JRuby, and the JRuby engine. You can grab JRuby from their website (as of this writing the latest version is 1.3.1), just extract that and grab jruby.jar from the lib folder. As for the JRuby Engine, you'll have to grab the big engine tarball from the Java site and extract jruby-engine.jar from the jruby/build folder. Once you have those put them in the same folder as your code, then compile and run:
scalac ScalaTest.scalaWhat you should see is "Hello from Ruby!" pop up on the screen.
scala -cp .:jruby-engine.jar:jruby.jar ScalaTest
So this is pretty cool, but how do we get information between Scala and Ruby? That part can be a little bit tricky since the type systems in Scala and Ruby are very different. However for our example here, we will stick to simple things. Here is our new Scala code:
import javax.script._And the Ruby code:
import java.io.FileReader
object ScalaTest extends Application {
val engine = (new ScriptEngineManager).getEngineByName("jruby")
val name = Console.readLine("What is your name?\n")
// assign a variable in the engine
engine.put("name", name)
// cast the result of the execution into a string
val colour = engine.eval(new FileReader("test.rb")).asInstanceOf[String]
Console.print(name + ", your favourite colour is ")
// this part is unnecessary, but let's have some fun with colours :)
Console.print(
if (colour.toLowerCase == "blue")
Console.BLUE
else if (colour.toLowerCase == "red")
Console.RED
else if (colour.toLowerCase == "green")
Console.GREEN
else
""
)
Console.println(colour + Console.RESET + ".")
}
# the name variable is passed in as $name, which is a StringWhile this is a trivial example, we could use this to embed all sorts of functionality within our application. In fact, this isn't even limited to JRuby. By changing the getEngineByName call to say, "Javascript", we can execute Javascript code instead of Ruby code. Or any other JVM language included in that big engine tarball, including Scheme, Jython, and Groovy to name a few. You can even use Java as a scripting language, although you have to jump through a few hoops since Java doesn't support global variables.
puts $name + ", what is your favourite colour?"
gets.strip