Roodi is now a few weeks old, and ready for it's first official release. It statically parses your Ruby code and warns you about design issues that you might have. to install Roodi, run this command:
gem install roodi
You can then run the roodi command and give it a list of patterns to find files to check. For example, you could:
Check all ruby files in a rails app:
roodi "rails_app/**/*.rb"
...or check one controller and all model files in a rails app:
roodi app/controller/sample_controller.rb "app/models/*.rb"
Here's an example of what Roodi output looks like when it is run against the Hpricot source code:
pudbookpro:Data marty$ roodi "hpricot/**/*.rb" hpricot/extras/mingw-rbconfig.rb:155 - Block cyclomatic complexity is 5. It should be 4 or less. hpricot/lib/hpricot/builder.rb:75 - Block cyclomatic complexity is 5. It should be 4 or less. hpricot/lib/hpricot/parse.rb:52 - Block cyclomatic complexity is 40. It should be 4 or less. hpricot/lib/hpricot/traverse.rb:50 - Block cyclomatic complexity is 5. It should be 4 or less. hpricot/lib/hpricot/traverse.rb:315 - Block cyclomatic complexity is 5. It should be 4 or less. hpricot/lib/hpricot/traverse.rb:555 - Block cyclomatic complexity is 11. It should be 4 or less. hpricot/setup.rb:215 - Block cyclomatic complexity is 6. It should be 4 or less. hpricot/setup.rb:578 - Block cyclomatic complexity is 5. It should be 4 or less. hpricot/lib/hpricot/builder.rb:63 - Method name "tag!" has a cyclomatic complexity is 15. It should be 8 or less. hpricot/lib/hpricot/traverse.rb:247 - Method name "search" has a cyclomatic complexity is 27. It should be 8 or less. hpricot/lib/hpricot/traverse.rb:553 - Method name "each_hyperlink_attribute" has a cyclomatic complexity is 11. It should be 8 or less. hpricot/lib/hpricot/traverse.rb:774 - Method name "author" has a cyclomatic complexity is 12. It should be 8 or less. hpricot/setup.rb:147 - Method name "standard_entries" has a cyclomatic complexity is 13. It should be 8 or less. hpricot/setup.rb:858 - Method name "parsearg_global" has a cyclomatic complexity is 10. It should be 8 or less. hpricot/setup.rb:1265 - Method name "update_shebang_line" has a cyclomatic complexity is 9. It should be 8 or less. hpricot/lib/hpricot/traverse.rb:781 - Rescue block should not be empty. hpricot/lib/hpricot/traverse.rb:791 - Rescue block should not be empty. hpricot/lib/hpricot/traverse.rb:800 - Rescue block should not be empty. hpricot/lib/hpricot/traverse.rb:807 - Rescue block should not be empty. hpricot/lib/hpricot.rb:17 - Rescue block should not be empty. hpricot/setup.rb:592 - Rescue block should not be empty. hpricot/setup.rb:601 - Rescue block should not be empty. hpricot/setup.rb:613 - Rescue block should not be empty. hpricot/lib/hpricot/builder.rb:63 - Method name "tag!" has 31 lines. It should have 20 or less. hpricot/lib/hpricot/traverse.rb:247 - Method name "search" has 80 lines. It should have 20 or less. hpricot/setup.rb:147 - Method name "standard_entries" has 51 lines. It should have 20 or less. hpricot/setup.rb:948 - Method name "print_usage" has 31 lines. It should have 20 or less. hpricot/test/test_parser.rb:69 - Method name "scan_basic" has 50 lines. It should have 20 or less. hpricot/lib/hpricot/parse.rb:3 - Method name "Hpricot" should match pattern /^[_a-z<>=\[\]|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/
The default parameters for checks can be changed, and checks can be turned on and off. Lots of additional checks are planned, as is support for rake tasks and rails plugins.
Please drop me a line if you use Roodi and find it useful. I'd love to get some feedback on how people are using it. Happy checking!
Comments
This looks great. Can't wait to try it out.
Posted by: Alistair Holt | September 11, 2008 09:19 AM
Looks cool, but what does Roodi offer over other analyzers, such as Saikuro and Flogg and Heckle?
Or put another way, what does it offer me that would complement or replace functionality in existing packages?
Posted by: Sammy Larbi | September 11, 2008 09:33 AM
Sorry, "Flogg" above should have been "Flog" ... typing error. See http://ruby.sadi.st/Flog.html for more.
Posted by: Sammy Larbi | September 11, 2008 09:34 AM
Just tested here, nice.
Need to hook this up with autotest =D
Posted by: nofxx | September 11, 2008 09:48 AM
Roodi is totally different to Heckle. I don't think they'll ever cross over in terms of functionality. Heckle changes your code and runs tests. Roodi is totally static analysis.
Flog definitely has overlap. Roodi is basically a framework for static code analysis with a collection of checks that plug into it. There is no check yet in Roodi to calculate the ABC metric for code, but there could be. Flog is basically a separate tool that implements that single check and adds some nice reporting on top of it.
Posted by: Marty Andrews | September 11, 2008 03:23 PM
This looks as though it will be very useful particularly in driving refactoring. I've tried to write static analysers for Ruby in the past and there are lots of edge cases that come up, so I appreciate the effort.
A few things struck me looking at the output and the rdocs. Could the output be DRY'ed up a bit? Where the error occurs in successive places the suggested remedy need not be repeated: it would mean less for the user to check for small differences in output. It might be useful to allow one to optionally sort the output by error type.
The numbers of acceptable lines, complexity are simply asserted. I could not see in the rdocs where these came from. Wikipedia (first thing Google gave me) suggests there are a number of ways to calculate cyclomatic complexity, so more docs on that might be useful.
Given the continued complaints from outsiders about the state of documentation in Ruby, it might be useful to count lines per (method|class|module) that would be picked up by rdoc. There is a case made by many people that too much documentation is a problem, and shows an attitude of "We'll fix that in the documentation". Conversely, no comments at all means one has to "prise the lid off" to use a library. I'm not sure where the boundaries should lie, or if concensus is possible.
As I have nobody to pair program with at the moment, I will be looking into this further. Thank you.
[OT: On using preview the text came up really small, and the security code feature seemed to have vanished. This is in Opera 9.52]
Posted by: hgs | September 11, 2008 10:28 PM
The output can be cleaned up a bit. I have some design changes to make that will probably end up meaning that all the errors for a single file are clustered together. I'm not sure yet what other sorting options make sense.
Yes - the thresholds for checks are asserted for now. They're defaulted in the constructors of the checks, and can be changed via code, but I haven't yet created a way to easily configure that. It's one of the things I need to add soon.
The description of how I implement Cyclomatic complexity is on http://roodi.rubyforge.org/classes/Roodi/Checks/CyclomaticComplexityCheck.html I do need to add more docs to the checks as a whole though.
Thanks for the ideas on rdoc checks. I'll add it to the wish list.
Posted by: Marty Andrews | September 12, 2008 10:24 AM
Doesn't look like I will be using this.
"Don't use 'for' loops. Use Enumerable.each instead." well in .. for .. is just Enumerable.each in a different form.
"Found = in conditional. It should probably be an ==" no not really, it really should be an =. I remember coz I put it there on purpose!
Posted by: joost | September 25, 2008 07:46 AM
How very literal of you joost. I think you missed the point a bit.
Both of these things are about being consistent with your coding style. I think using Enumerable.each is more idiomatic of Ruby style, and I whilst you happen to know that you meant to put an '=' instead of an '==', the other people reading your code might not be so sure. I like to avoid those mistakes.
If you don't like those checks, you can always turn them off in your config file...
Posted by: Marty Andrews | September 25, 2008 02:43 PM