Skip to content

Ruby Processing Internals and JRuby Tricks

jashkenas edited this page Sep 12, 2010 · 33 revisions

The Guts:

Ruby-Processing uses regular Ruby for generating sketches, and exporting applications and applets, and uses Java via JRuby for running Processing. The main class, Processing::App, inherits from Processing’s PApplet, and has access to all of the PApplet’s methods and fields. When you call methods inside of your sketch, they may end up being called within JRuby-space, or out in Java-space, depending on whether the method is defined in the Processing::App or the processing.core.PApplet. For most methods defined in the Processing API, it entails calling into Java, with JRuby taking care of translating all the arguments and the return value. However, a handful of useful methods (like load_library, render_mode, and many of the math functions) are defined in JRuby.

For a fuller description of JRuby internals, take a look at JRuby Internal Design, but here’s a brief overview:

  • JRuby uses a Java port of Ruby’s parser.
  • Internally, Java can handle many different JRuby runtimes running in separate threads. All objects have a reference to the runtime that they belong to, and cannot be transported across runtimes without being serialized.
  • JRuby threads map directly to Java threads, so you can use the full power of your multi-core or multi-processor machine to drive computationally intensive Ruby-Processing sketches, within a single process.
  • Ruby can call methods defined in Java space via bytecode-generated adapter classes that directly invoke the methods. This avoids Java’s reflection capabilities, and provides better performance. There is still significant overhead when calling into Java, however, and as the JRuby team works on reducing this overhead, Ruby-Processing continues to speed up. Core Ruby classes have even further table-based method optimizations.
  • JRuby runs with a JIT mode that will compile interpreted calls after 20 runs through the interpreter.
  • JRuby 1.0 is very, very close to 100% compatible with the Ruby language. Any libraries that are pure-Ruby and stay away from POSIX-only functions or platform-specific features are generally compatible with JRuby.

JRuby Tricks & Tips

These tricks are taken from the JRuby wiki page Calling Java from JRuby, and more are available there.

Ruby-Processing already does a require 'java' for you, which gives you access to many of the core Java libraries from Ruby-space. For example, it allows you to refer to namespaced java classes by their full paths. The following code pops open a Swing window:

win = javax.swing.JFrame.new('A Window')
win.set_visible(true)

To gain access to java classes stored in jar files, simply require the jar. To load java classes into your namespace, you can import them:

  import "com.giganticorp.audio.AudioAnalyzer"
  analyzer = AudioAnalyzer.new

All Java camel-cased method names on imported classes will become available to you as their Ruby underscored counterparts, so clapHandsIfHappy => clap_hands_if_happy. All Java objects gain a handful of additional methods: java_class returns the Java class of a given object, and java_kind_of? lets you know if your object is an instance of a given Java class.

JRuby’s open classes allow you to add methods to existing Java classes, but your additions will only be available from the JRuby side. Watch out for class name collisions between core Ruby classes and imported Java ones. If you import "java.lang.Thread" and then write MyThread < Thread, MyThread will actually inherit from core Ruby’s Thread and not the java one that you just imported.

JRuby classes can mix in Java interfaces as modules in order to implement them.

  class SomeFlexibleClass
    include java.lang.Runnable
    include java.lang.Comparable
  end

JRuby does a wonderful thing with Ruby’s blocks, where a block can be converted to the appropriate Java interface. It works by converting the block to a Proc, and then decorating it with a proxy object that runs the block for any method called on the interface. Runnable is the prime example here. For example:

  button = javax.swing.JButton.new "Press me!"
  button.add_action_listener {|event| event.source.text = "I done been pressed." }