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.