Rcov for JRuby Gem Available

31 07 2008

It’s been a long time coming, but, thanks to Jay, who’s really kicked some ass to make this happen, the long suffering Rcov port to JRuby finally is bearing fruit. The first gem is now available.

There are a few known issues. Performance isn’t that great. And there is some slight variation between what Rcov reports in JRuby vs. MRI. But Jay’s been using it in his build environment, and so should you. So go ahead and grab the gem and help us make it great!

Advertisements




More “Monkey Patch”-ed Java Classes

28 02 2007

Last time, I demonstrated how to re-open Java objects from JRuby and I used a completely worthless example. This time, the example is only mostly worthless. By that I mean, we’re going to add some Ruby-like aesthetics to a very useful Java library called XOM. The perceived value of this code is directly proportional to how much you believe you should be able to use each, any?, all?, include?, or grep on all collection-like objects from JRuby code.

XOM is a well designed and very opinionated Java library. It is also my preferred library for general purpose XML wrangling. However, because of the principles under which XOM was developed (not implementing collections API, I’m looking at you), certain Ruby conveniences don’t get applied to XOM objects in JRuby. In particular, XOM’s collections don’t implement the Java collections framework. Because of this, XOM collections don’t get decorated with Ruby-like iterators and such. We’re going to change that.

Before we get started, make sure you can reach XOM from JRuby:

1) Download XOM, of course.
2) Set or export your CLASSPATH so that it includes the XOM jar.
3) Fire up jirb.

The classes we’re looking at are Nodes and Elements. I would like for both of these classes to behave more like Ruby array objects. For the Nodes class, which is a modifiable collection, I want [] to be an alias for get, I want << to be an alias for append, and I want to mix in the Ruby Enumerable module. For Elements, which is a read-only collection, I want the [] alias and Enumerable.

So first things first:

require 'java'

XomNodes = Java::nu.xom.Nodes

XomElement = Java::nu.xom.Element

XomElements = Java::nu.xom.Elements

This should look familiar, but you’ll notice that we used the Java module to reference the XOM class objects. This is because the magic that makes JRuby understand that java.lang.String is a Java class, doesn’t work for nu.xom.Nodes, because nu is not a common Java package starter. So we use Java::nu.xom.Nodes, and everything works great.

Now, if we want our collections to behave more like Ruby arrays, then we need to mix Enumerable into our Java objects. Enumerable will not work unless the object has an each method that iterates over the collection, passing each iterated item to yield, in turn.

module XomCollectionExtensions
  include Enumerable

  # Implements each method as described in the Enumerable contract
  def each
    (0...size).each { |i| yield get(i) }
  end
end

What I’ve done here is created my own mixin module. I’ve included Enumerable in my module, and I’ve created the each method. My each implementation just uses an end exclusive Range to iterate over the Nodes in the collection.

Now I can either re-open the Java class and include my module (as well as aliasing the append and get method)…

# modifications to the nu.xom.Nodes to make it behave more
# like a Ruby Array
class XomNodes
  include XomCollectionExtensions

  alias_method :[], :get
  alias_method :<<, :append
end

… or I can send the include and alias_method messages to the class.

XomElements.send :include, XomCollectionExtensions
XomElements.send :alias_method, :[], :get

If you’ve been following along, you should now be able to check out your handiwork:

nodes = XomNodes.new
nodes << XomElement.new('first')
nodes << XomElement.new('second')

nodes.each { |n| p n.to_xml }

Also, if you’re interested in what else can be done with this sort of black magic (besides hacking up your Java objects at runtime), errtheblog has a some nice examples of using alias_method_chain. A welcome bit of warmth that was on this cold and snowy evening, I can tell you.





“Monkey Patch” Java Objects from JRuby

24 02 2007

Sometimes, when you’re programming Java objects in JRuby, you just wish the object acted more… Rubily. JRuby does a pretty good job creating a more Ruby-like interface for Java objects (by aliasing method names and whatnot), but sometimes you want more. Sometimes you want to Monkey Patch those Java objects!

To follow along, install or build JRuby and then fire-up jirb. And, before we go on, please note:

1) I’m using JRuby trunk (3072 I believe). These examples should work in 0.9.2 and 0.9.1, but I haven’t tested them in those environments. Just be aware.

2) “Monkey Patched” Java methods are only available in Ruby code.

3) Aspects of this code are likely to change in the next release.

With that in mind, let’s look at a completely contrived example. Let’s patch java.lang.String. There’s no good reason to do this sort of patching because JRuby marshals strings between Java and Ruby pretty well. But I picked String because everyone should be down with what a String is and how it works, so I can focus on the technique without having to explain the API.

First things first:

require 'java'

This is one of those aspects of the code that will change. In the next release, Java features will be handled in a module, so they will be mixed in, instead of required. But just use require ‘java’ for now. Trust me πŸ™‚

JString = java.lang.String

Here we assign the String class to a constant (JString). We will refer to Java Strings through this constant from now on. Because Ruby objects are just Class objects assigned to constants, this allows us to treat the Java object just like a Ruby object.

So, what do we want to fix about Java Strings? How about this: Ruby strings have size and length methods. Java Strings only have length. If we want JString to behave more like a Ruby String, we’ll have to fix that.

class JString
  def size
    length
  end
end

That’s it. Now take it for a test drive:

s = JString.new('monkey')
p "#{s} | #{s.size}"

You should see something like this:

monkey | 6

Now, just to prove that we did change the behavior of java.lang.String…

s = java.lang.String.new('patch')
p "#{s} | #{s.size}"

Gives you:

patch | 5

That’s how you re-open a Java class in JRuby.

For this particular change, where we just created an aliased name for an existing method, we did things the hard way. Ruby objects accept a message named alias_method that will, as the name suggests, create a second alias for an existing method.

We could re-open the class and alias the method:

class JString
  alias_method :size, :length
end

or, we could just send the alias_method message to the class object.

JString.send :alias_method, :size, :length

This looks like a good place to stop for now, but never fear. I have enough sample code left over for another post. The next one will demonstrate a more practical use of these techniques.





Ruby DSL Meets Java JTree

31 12 2006

For work, as a proof-of-concept, I started porting a proprietary macro language to Ruby as a DSL. Tonight, on a lark, I tried using JRuby to hook a JTree view up to the output. Here’s a screen shot:

jruby jtree

Pretty cool!





Netbeans: Look Ma, No Jagged Fonts

12 12 2006

My first observation regarding Java 6: Netbeans 5.5 running on Java 6 looks really, really nice.





Weak is Good

3 12 2006

I’ve been giving Ruby and JRuby all the love lately, but tonight is Java’s night:

A Java program, if reasonably well designed, can be a joy to maintain.

I spent the last couple of days hunting down a memory leak in our server app. Finding the memory leak really sucked, but here’s where it gets good. The leak was in a single object, used for caching. Fixing it involved replacing the standard collections with WeakReference based collections. Since the program used the Collections interfaces, I only had to change the instantiations. That’s it. Game over.

Now, since the program is reasonably well designed, there are unit tests available, so I can verify that my fix didn’t break anything else. Also, now that the garbage collector is able to get its job done, the application seems to be performing better (and, of course, not crashing).

Weak is Good.





RDT Issues: Syntax Highlighting and TracTickets Edition

18 10 2006

Syntax Highlighting is a snob – Ruby syntax highlighting seems to have some hard coded constraints on what file types it will actually highlight. I have some custom scripts that don’t use file extensions and some not so custom scripts (*.gemspec) that I have associated with the Ruby editor, but syntax highlighting continues to ignore those files. It would be nice if syntax highlighting would just work on whatever file was currently in the Ruby editor, regardless of file extension.

TracTickets – The reason I’m griping to lazyweb instead of quietly reporting a bug is because I can’t figure out how to report this issue to the RDT devs. I’ve logged into the TracWiki, but I see no option for creating a new ticket, and any attempt to navigate to the newticket URL is roundly rebuked. There are tickets in the system, so someone’s entering them. If one of those magically gifted folk could drop by and tell me how I screwed up, I’d appreciate it.

Oh, and so this post doesn’t come off as too negative, RDT is a fine product. Keep up the good work and all that…