Marty Andrews

artful code

Thursday, September 4, 2008

Roodi - Checkstyle for Ruby

I've been working on a tool for the last couple of weeks that I'm calling Roodi (Ruby Object Oriented Design Inferometer). It's very much a tool for Ruby like checkstyle is for Java. The framework was surprisingly quick to get up and going, and I've written seven checks (class name, method name, method cyclomatic complexity, block cyclomatic complexity, empty rescue body, for loop, method line count) for it so far. It's working pretty well for all of them.

Here's a sample of what it looks like (I've trimmed some of the output for brevity):

pudbookpro:Data marty$ ./roodi/bin/roodi "rspec/**/*.rb"rspec/lib/spec/rake/spectask.rb:152 - Block cyclomatic complexity is 11.  It should be 4 or less.rspec/lib/spec/rake/verify_rcov.rb:37 - Block cyclomatic complexity is 6.  It should be 4 or less.rspec/lib/spec/matchers/be.rb:57 - Method name "match_or_compare" has a cyclomatic complexity is 12.  It should be 8 or less.rspec/lib/spec/matchers/change.rb:12 - Method name "matches?" has a cyclomatic complexity is 9.  It should be 8 or less.rspec/lib/spec/matchers/have.rb:28 - Method name "matches?" has a cyclomatic complexity is 11.  It should be 8 or less.rspec/lib/spec/expectations/errors.rb:6 - Rescue block should not be empty.rspec/lib/spec/rake/spectask.rb:186 - Rescue block should not be empty.rspec/lib/spec/expectations/differs/default.rb:20 - Method name "diff_as_string" has 21 lines.  It should have 20 or less.rspec/lib/spec/matchers/change.rb:35 - Method name "failure_message" has 24 lines.  It should have 20 or less.rspec/lib/spec/matchers/include.rb:31 - Method name "_message" should match pattern (?-mix:^[a-z]+[a-z0-9_]*[!\?]?$).rspec/lib/spec/matchers/include.rb:35 - Method name "_pretty_print" should match pattern (?-mix:^[a-z]+[a-z0-9_]*[!\?]?$).

You can get Roodi from github for now. I'm still in the process of getting it packaged and ready as a gem.

One of the interesting features (and issues) with Roodi is that it is dependent on JRuby. That's because it uses the JRuby AST libraries to parse Ruby source code. There's no good way (IMHO) yet to build an AST of Ruby source code in Ruby. ParseTree is close, but gives you an AST of the executable code, not the source. What does that mean? Well, for example, I can't get any representation of whitespace characters from ParseTree. Nor can I get line numbers from it. JRuby isn't perfect either, but it gives me line numbers and newlines at least.

The use of JRuby libraries makes the Roodi design interesting. Roodi is completely written in Ruby, but the calls out to libraries written in Java means it needs to run under JRuby. So I end up running JRuby to execute a Ruby app, which then calls out to JRuby to parse Ruby code.

I'd love some feedback if anyone has a play. Writing checks is really easy too. I'll happily roll them in if someone wants to write some.

No comments:

Post a Comment