-
Notifications
You must be signed in to change notification settings - Fork 94
Ruby Processing Internals and JRuby Tricks
Ruby-Processing uses regular Ruby for generating sketches, 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. Ruby-processing also provides a more ruby-like alternative to processings PVector class (to use this you will need to load the ‘vecmath’ library) see examples.
Inside the lib
folder of the Ruby-Processing source, you’ll find:
- a ruby folder (that contains the vendored
jruby-complete.jar
), since processing-2.4 processing jars are all accessed by classpath, from an installed vanilla processing. -
ruby-processing
is the belly of the beast. It contains theProcessing::App
itself, the runners, and the exporters.-
app.rb
provides theProcessing::App
that you inherit from when you write a Ruby-Processing sketch. It takes care to try and map the many Processing methods, static methods and fields to accessible constructs in idiomatic Ruby. It provides methods for loading Processing libraries and Ruby libraries into your sketch. It knows how to display a Ruby-Processing sketch in a window, in a full-screen context. -
runner.rb
is the class that executes all of therp5
commands. All calls torp5
begin by going through theProcessing::Runner
in a vanilla Ruby process, before being replaced by the Java process that will run one of the scripts in thelib/runners
folder via JRuby. In that folder, you can find runners for therun
,live
, andwatch
commands. - The
exporters
folder contains all the classes needed to export Ruby-Processing sketches.
-
For a fuller description of JRuby internals, take a look at JRuby Wiki, but here’s a brief overview:
- JRuby is a 100% Java implementation of the Ruby programming language. It is Ruby for the JVM.
- 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.7.22 is virtually 100% compatible with the Ruby language (version-1.9.3). Any libraries that are pure-Ruby and stay away from POSIX-only functions or platform-specific features are generally compatible with JRuby. JRuby-9.0.0.0 is mainly 2.2 compatible, but is not quite as fast as which is why we don’t install jruby-complete-9.0.0.0 yet (however see JRubyArt where we use JRuby-9.0.0.0 from the start because we have the faith).
- Configuring jruby
Should you require anything more complicated read about configuring jruby here.
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 name-spaced 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 java_import
them:
java_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 java_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." }