Jul 23 2007

Using JRuby to rapidly prototype changes in a Java codebase

Published by Laurie Tagged as:, ,

Today, as an exercise, I coded up a simple piece of java and ruby integration, via JRuby.

The task I set myself was simple. Start with a java only application, and then use JRuby to rapidly prototype adding a new feature to it. I used my favourite sample application: A library with three entities, books, borrowers and loans (the first application design I was ever taught – way back in A-levels). The design is pretty simple; a borrower is associated to a number of loans, each of which is associated to a book. There is also a link from each book to all the loans it’s refereced in.

I intentionally made a limiting design decision however. Each book’s author is saved as a string, and the meaning is lost when someone tries to enter several authors to a book

public class Book {
<snip>
private String author;
<snip>
}

Book jrubyBook = new Book(”JRuby for beginners”,
“Ruby Guy, Java Guy”, //second argument is author
“12365437890″,
LoanType.ONE_MONTH);

The feature I wanted to add was the ability to look at a book, and see what books influenced its authors. The logic used is:

  1. use the book.author property to find out the author(s) of the book
  2. look at the loans associated with the author(s)
  3. get the books for those loans

Not a very complex piece of logic, but good enough to demonstrate the point. Ideally the author field of book should be refactored to be a collection of people which would be created as a new super-class of borrowers. But before I go and do some refactoring, (which in a real project would mean quite a bit of changing the persistence mapping too) I want to get the functionality working to see if it really is useful. JRuby’s ability to load up some java classes and quickly change them allowed me to simulate doing these changes.

require ‘java’

include_class ‘com.wildfalcon.library.model.Book
include_class ‘com.wildfalcon.library.model.Borrower’
include_class ‘com.wildfalcon.library.persistance.Books’
include_class ‘com.wildfalcon.library.persistance.Borrowers

class Book
def authors
author_names=[]
getAuthor.split(”, “).each { |author| author_names << Borrowers.findByName(author)}
author_names
end

def inspirations
list = []
authors.each {|author| author.books.each{|book| list << book}}
list
end
end

I can then call the inspirations method:

book = Books.findByName(”JRuby for beginners”)
book.inspirations.each {|a| puts a.title}

all of which I put in a prototype.rb file an execute as follows:

ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName(”jruby”);
InputStream is = new FileInputStream(new File(”prototype.rb”));
try {
Reader reader = new InputStreamReader(is);
engine.eval(reader);
} catch (ScriptException ex) {
ex.printStackTrace();
}

Indeed when I execute it I find out what books have been read by the two different authors of “JRuby for Beginners”:

  • Java for beginners
  • Ruby for beginners
  • How to tie a tie
  • History of fairy tales
  • Cooking Italian Food for Large Groups

This quickly allows me to realise that looking at the books read by the author isn’t good enough, before I can deliver the feature I need to find some way to tell which books are relevant and which are not. But I found that out with a very small amount of coding, and without having to make any changes to the existing java application. I can happily go back to the drawing board without worrying that I broke something in the meantime.

Trackback URI | Comments RSS

Leave a Reply

For spam filtering purposes, please copy the number 6284 to the field below: