Ruby Builder First Draft: Intriguing Failure
Sat Jun 09 09:56:00 EDT 2012
For a while now, I've been fiddling with trying to make an Eclipse builder that smoothly translates Ruby files into Java classes and adds them to the project to be compiled, the idea being that, rather than only using Ruby inline in XPages or via "script libraries", you'd be able to write all of your supporting Java classes in it as well.
I'd been giving it about an hour or so of frustration every couple of weeks, but yesterday I decided to hunker down and make it work. After patching the standard "jrubyc" code to work without a real filesystem, wrestling with Ant builder class paths and runtime environments, and with a great deal of assistance from the handy XPages Log File Reader template, I got it working! I now have it so that you can write Ruby files in a ruby-src folder and build the project and the builder reads those files and plants "compiled" Java versions into a folder on the build path, which then causes Eclipse to build them into proper classes, usable on an XPage. Whoo!
However, they're not really usable yet. The way the "compiler" works is that it generates a class that extends RubyObject
and, in a static
block, loads up the global Ruby runtime and pre-compiles a giant string of Ruby. Then, each method you exposed to Java in the Ruby code calls the equivalent method in the Ruby runtime. It makes sense, but leads to some specific problems. For one, the global Ruby runtime's classloader doesn't know about the classes available in the XPages environment, such as the javax.faces.*
classes and any of your own NSF-hosted ones. Moreover, because the object extends RubyObject
and Java doesn't do multiple inheritance, it can't extend any OTHER Java classes. The internal Ruby class can extend whatever it wants and works well, but that doesn't help when you want to use the generated classes in Java code. It can implement Interfaces, but then you have to actually have Java-facing versions of every method, which can get hairy.
I have some ideas, though. The "jrubyc" compiler is handy, but it doesn't work any special magic - it just reads through the Ruby source for key elements like java_implements
and java_signature
and uses those to build a wrapper class that just executes a big string of Ruby code. There's nothing there I couldn't do myself, hooking into the existing parser where necessary and otherwise writing the rest myself. That way, I could generate a standard Java class on the outside but make it create a proxy object internally that's the actual RubyObject
that handles all of the method calls. It could be a bit more difficult than I'm thinking, but it'd probably be possible, and it'd let me use the runtime classloader from FacesContext
and extend classes properly at will.