JRubyArt internals and jruby tricks
The Guts:
JRubyArt uses regular Ruby for generating sketches, 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 values. However, a handful of useful methods (like load_library
, and map1d
etc) are defined in JRuby. JRubyArt also provides a more ruby-like alternative to processings PVector
class see examples (some code such as map1d
and Vec3D
are coded in java as JRuby Extensions which are loaded into the jruby runtime). The rpextras.jar see below contains the jruby extensions and some other code that make it possible to use some of processing java reflection interfaces, see javadoc here.
Inside the lib
folder of the JRubyArt source, you’ll find:
a ruby folder (that contains the vendored jruby-complete.jar
), processing jars are all accessed by classpath, from an installed vanilla processing (which is why we need .juby_art/config.yml
to point to processing root).
the jruby_art folder is the belly of the beast. It contains the Processing::App
itself, the runners, and the creators.
app.rb provides the Processing::App
that you inherit from when you write a JRubyArt sketch. It takes care to try and map the many Processing 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 JRubyArt sketch in a window, or in a full-screen context.
runner.rb is the class that executes all of the k9
commands. All calls to k9
begin by going through the Processing::Runner
in a vanilla Ruby process, before being replaced by the Java process that will run one of the scripts in the lib/runners
folder via JRuby. In that folder, you can find runners for the run
, live
, and watch
commands.
JRuby’s Guts
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 JRubyArt 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, JRubyArt 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 9.0.3.0+ is virtually 100% compatible with the Ruby language (version-2.2). Any libraries that are pure-Ruby and stay away from POSIX-only functions or platform-specific features are generally compatible with JRuby.
- Configuring jruby Should you require anything more complicated read about configuring jruby here.
JRuby Tricks & Tips
These tricks are taken from the JRuby wiki page Calling Java from JRuby, and more are available there.
JRubyArt 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
The majority of 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.' }