Throughout history, man has repeatedly attempted to join two or more simple elements to create a new and more astonishing item from the combination. For example, consider metal alloys, mules and hinnies, Reeses peanut butter cups, and the act of putting the lime in the coconut.
Today I practice my own brand of amalgamation by introducing Spring to Ruby. What follows are the results of a 10-minute experiment that I performed during lunch today...
Let's say that you have a simple POJO named
Lime
. Lime
is defined as follows:
package com.habuma.jruby; public class Lime { public Lime() {} public void drink() { System.out.println("Call the doctor, wake him up!"); } }
And let's also say that you've configured Lime
in a Spring
context like so:
<beans> <bean id="lime" class="com.habuma.jruby.Lime"/> </beans>
So far, so good. Now, let's suppose that for some reason (be it practical or sick and
twisted) you need to use Lime
in your Ruby code. Using JRuby I could simply
import Lime
into my Ruby script and run with it:
require 'java' include_class 'com.habuma.jruby.Lime' class Coconut def initialize(l) puts "You put the lime in the coconut" @lime = l end def drinkThemBothUp puts "Drink them both up" @lime.drink end end lime = Lime.new coconut = Coconut.new lime coconut.drinkThemBothUp
The first two lines are key as they indicate that I'll be using Java objects and specifically that I'll be using the Lime
class. With Lime
imported, I can now use it as if it were a Ruby class. This will work and the only real gotcha is that I must run it through
JRuby instead of the standard Ruby interpreter.
That's really nice, but I'm still instantiating Lime
directly. It's not too much of a mental stretch to realize that if Lime
were much more complex that I may want to configure it in a Spring context and use dependency injection to wire it up with other objects. So how can I (for reasons either practical or perverse) retrieve my Lime
object from Spring?
It stands to reason that if I can use Lime
in my Ruby
script that I can also use Spring's
FileSystemXmlApplicationContext
(or any of Spring's other
container implementations) in much the same way. And if I can
access a Spring application context in Ruby, then I can also access
beans configured in that application context and use them as if they
were Ruby objects:
require 'java' include_class( 'org.springframework.context.support.FileSystemXmlApplicationContext' ) appContext = FileSystemXmlApplicationContext.new "lime.xml" class Coconut def initialize(l) puts "You put the lime in the coconut" @lime = l end def drinkThemBothUp puts "Drink them both up" @lime.drink end end lime = appContext.getBean "lime" coconut = Coconut.new lime coconut.drinkThemBothUp
Now the lime
object in my Ruby script is retrieved from a
Spring application context instead of being instantiated
directly. And Ruby's duck-typing kept me from having to cast the bean
to a Lime
before I can use it.
I'm certain that I'm going to get comments from both the Ruby camp and the Spring camp saying that this is an abomination and questioning the real worth of this technique. Honestly, this was just a quick lunch-time experiment where I was only asking a question of "Can it be done?" I think I've answered that question with a resounding "Yes". But I really haven't explored the question of "What practical purpose does this have?"
However, before you completely dismiss this article, consider this: I often hear arguments against Ruby and Rails that say that Ruby applications can't make use of resources and APIs that are readily available to Java applications. For instance, what if a Rails application needs access to a legacy EJB?
I'm not yet sure if it's even possible to run Rails within JRuby...that's an experiment for another lunch hour. But if it can be done, then a Rails application can use Spring's mechanism for wiring EJBs to easily gain access to an EJB.
Furthermore, any Ruby can take advantage of the wealth of Java
libraries that are available to do their bidding. And when configured
as <bean>
s in a Spring context, Spring's dependency
injection and AOP support can be applied as needed.
What do you think? Can you think of a practical use of this technique? If so, leave me a comment...I'd love to hear about it.