sort{ |a, b| a <=> b }.ing with Ruby

One idiom from Perl that I’ve missed with Ruby is the ability to chain comparisons together, such as:

my @a = qw{ this is a test };

$, = ", ";

print sort { substr($a, -1) cmp substr($b, -1) || length($a) <=> length($b) } @a;
print "\n";

Which results in the output:

a, is, this, test

In Ruby, it’s a little more complicated, since Perl evaluates a zero as false, but Ruby does not. However, the nonzero? method for all Ruby Numeric objects essentially performs this conversion, for use in a boolean evaluation, returning nil if it is zero, and the number otherwise. So in Ruby, the above code would be:

a = %w{ this is a test }

puts a.sort { |a, b| (a[-1] <=> b[-1]).nonzero? || a.length <=> b.length }.join(", ")

One additional note: if you’re using this in a spaceship method (“”) for the Comparable interface, remember that it must return a numeric value, so if you chain evaluations together, the final statement should be zero, since all previous evaluations were nil (meaning that they were equal). This bit me during some recent DiffJ work, and here is an example of a corrected method:

class Java::net.sourceforge.pmd.ast::Token
  include Comparable, Loggable

  # ...

  def <=> other
    (kind <=> other.kind).nonzero? ||
      (image <=> other.image).nonzero? ||
      0
  end

That’s DiffJ opening the PMD token Java class and adding the Ruby Comparable interface to it, so tokens can be sorted in Ruby collections.

On that note, DiffJ is in rough beta status now. I’m using it for my work (refactoring and cleaning up legacy Java code), and just corrected a glitch in the Token code, ironically enough, for supporting usage in Hash objects. I’d neglected to implement the eql? method, erroneously thinking that Hash uses the Comparable code.

With that fix, the JRuby implementation of DiffJ produces the same output as the Java implementation. It’s somewhat slower, so I’ve been investigating AOT compiling of it, but that doesn’t seem to have much of an effect.

I just realized that another feature from Perl that I’ve missed (and until writing that code above, hadn’t used for 10 years) is defining the array separator with the “$,” variable. Similar to that, my RIEL library modifies the to_s method of an Array to output “, ” between elements for output, since the default is to have no space between elements.

How to Write JUnit Test Sets in Rake

As DiffJ has been rewritten in JRuby, it now has numerous tests, with a group of tests in which each test corresponds to a add/delete/change type, such as methods being added to a class declaration.

I found myself wanting to run subsets of tests, such as all those for methods. The organization of the tests is that pathnames match the scope of the code being checked, so, for example, src/test/ruby/diffj/type/method/parameters/typechange/test.rb is the test for checking for a change in the type of parameters of a method, which is part of a type, meaning an interface or class. (As a parenthetical aside, the test files themselves match the test pathnames, so the two files being tested here are src/test/resources/diffj/type/method/parameters/typechange/d0/Changed.java and src/test/resources/diffj/type/method/parameters/typechange/d1/Changed.java. And yes, this begs for an Emacs shortcut for toggling among the files, which I haven’t written yet.)

Although I’m a Rake novice, I had an idea of what I wanted: a way to specify in the Rakefile a subset of tests. Yes, I probably could have written test suites, but for whatever reason, it seemed more logical to do this as part of a build. And being a Z shell user, I also knew the pattern to apply. More about that in a minute.

So here’s what I wanted: to run “rake test:method”, and have that run all the unit tests under src/test/ruby/diffj/type/method. Or in glob-speak, “src/test/ruby/diffj/type/method/**/test*.rb”. (My testcases without their own tests are named “tc.rb”, so src/test/ruby/diffj/type/method/parameter/tc.rb has code common to the testcases under src/test/ruby/diffj/type/method/parameter.)

Enjoying the wonderful experience of writing object-oriented build scripts (bitterly said as a professional maintainer of Ant code), I subclassed the Rake test task,

class DiffJRakeTestTask < Rake::TestTask
  def initialize name, filter = name
    super(('test:' + name) => :testscompile) do |t|
      t.libs << $srcmainrubydir
      t.libs << $srctestrubydir
      t.pattern = "#{$srctestrubydir}/**/#{filter}/**/test*.rb"
      t.warning = true
      t.verbose = true
    end
  end
end

DiffJRakeTestTask.new('field')
DiffJRakeTestTask.new('method')
# ... and others

Going roughly line by line, that takes a parameter such as “method”, creates a test task named “test:method”, and adds a glob for test files (“test*.rb”) under a directory matching the filter, which defaults to the name. The “lib” lines add the directories containing my Ruby source and test code.

Using the filter means we can also specify partial or full paths for a test subset (even just a single file), writing them as needed, such as when working on failing tests. My needed ones ended up being the following:

DiffJRakeTestTask.new('method/body/zeroone')
DiffJRakeTestTask.new('method/parameters/zeroone')
DiffJRakeTestTask.new('method/throws/zeroone')
DiffJRakeTestTask.new('method/parameters/reorder')
DiffJRakeTestTask.new('method/parameters/reorder/typechange')

And we also can use the “filter isn’t the same as the name” functionality with the “test:all” task:

DiffJRakeTestTask.new('all', '*')

Yes, that’s probably redundant with the default “tests” task, but I prefer the feeling of wielding more control.

It looks like I’ll have DiffJ fully implemented in JRuby by the end of the month. I’m only packaging it as Zip files, and would appreciate offers to repackage it for Ubuntu, Fedora and other distributions. I know nearly nothing about Eclipse, but have had received interest in DiffJ as an Eclipse plugin, so that is another valuable contribution someone could make.

DoctorJ 5.2.0 and DiffJ 1.2.0 Released

DoctorJ 5.2.0 is an extensive rewrite of the code, with unit tests expanded and refined. It uses the latest version of PMD as the parsing and AST code, and is the first version of DoctorJ to use the IJDK module, leading to much more elegant code. In terms of functionality this version add spell-checking for strings of a minimal length (which defaults to 2). The project now builds with Gradle.

DiffJ 1.2.0 also has significantly rewritten code, and also uses the IJDK module. This version adds (on terminals that support it) colorization of differences. It too uses Gradle for its build.

Both projects are now distributed only in zip format, and I welcome offers to repackage them for various package managers.

The distributes are available for download:

Projects Now on GitHub

I am in the process of migrating some of my projects to my space at GitHub.

At this point, the following exist:

  • DiffJ – a Java-aware command-line utility for comparing Java files. This program ignores whitespace and comments, and detects added and removed classes, methods, and fields, as well as changes in code.
  • IJDK – extensions to the JDK, primarily motivated by functionality in various Groovy and Ruby libraries.