JRuby is my favorite Java tool. It lets me wield the power of Java libraries while building tools and abstractions to speed development.
On a current project, I’m using JAXB2 to bind a large set of deeply-nested XML Schemas into Java classes. I have a Rake task to create the classes from the xsd files and bundle them into a jar. From there, I import the classes into JRuby where the software uses them to interact with other systems via XML marshaling.
Ruby on its own doesn’t have this power. Integrating with existing J2EE systems, marshaling complex XML files based on multi-layered schema definitions: this is the definition of “heavy-lifting.” So I let Java do that for me. Then I can orchestrate from the podium of JRuby. Now I have tools for language-building, leading-edge testing libraries, and breezy syntax to get work done easier. The power comes from being able to build on the foundation of existing Java tools in JRuby.
For instance, accessing useful bits of info from these JAXB2-generated classes means using complex chains of object properties. In my case, I get over 350 classes that are inter-composed and contain only simple getters and setters. I need to wrangle them easily for my RSpec specifications and my Cucumber features, as well as to make my actual code as simple and expressive as possible.
A common use case for these classes involves reading some property using a deeply-nested getter. In Java, this would look like this:
|
String name = customerResponse.getTransaction().getPerson().getName().getFirstName();
|
in JRuby, it could look like this:
|
name = customer_response.transaction.person.name.first_name |
This is already a small improvement, but what about when I need to set that value? Since much of my XML structure is optional, the intermediate objects are frequently not present. With Java, this is where things get messy:
1 2 3 4 5 6 7 8 |
Transaction transaction = new Transaction(); Person person = new Person(); Name name = new Name(); String firstName = "Marshal"; name.setFirstName(firstName); person.setName(name); transaction.setPerson(person); customerResponse.setTransaction(transaction); |
Ugh. I feel less alive for having typed all that. Luckily JRuby offers some help out of the box:
1 2 3 4 |
customer_response.transaction = Transaction.new customer_response.transaction.person = Person.new customer_response.transaction.person.name = Name.new customer_response.transaction.person.name.first_name = 'Marshal' |
Still not very DRY. So here’s where I can put on my toolsmith hat. I wrote a utility that makes this sort of chained access easier. Here’s what the above code looks like now:
|
customer_response.set_chain [:transaction, :person, :name, :first_name], [Transaction.new, Person.new, Name.new, 'Marshal'] |
Here’s the utility that makes that possible: set_chain
That’s got some dynamic dispatch, a mix-in, and Object-level enhancement without inheritance. Very Ruby. (Not that you can’t do this sort of thing in other languages. This utility inspired Scott to create DeepSet in C# to deal with the same problem context.)
Arguably, the java snippet can be improved:
customerResponse.setTransaction(new Transaction(){{
setPerson(new Person(){{
setName(new Name(){{
setFirstName(“Marshal”);
}});
}});
}});
Only slightly repetitive and strongly typed.
Thanks grze, and you’re right, there’s always room for improvement. The example I gave is from my app’s code where we were subsequently using each of the intermediate objects, which I hope explains the verbosity. Your example is definitely an improvement if you don’t need the objects immediately. To be fair to the pure-Java side, I should also mention that there is a “fluent” API available for JAXB2: https://jaxb2-commons.dev.java.net/fluent-api/
That plugin even has some nice support for handling List and Collection properties. Of course, you can use the Fluent API from JRuby too!
“”Ruby on its own doesn’t have this power.”
Neither does Java.