Ruby Code Quality Tools
Update: Devver now offers a hosted metrics service for Ruby developers which can give you useful feedback about your code. Check out Caliper, to get started with metrics for your project.
This is the third post in my series of Ruby tools articles. This time I look at Ruby code quality tools. Rubyists like Ruby because the code can look so nice, simple, and sometimes beautiful. Unfortunately not all code is so great, in fact often the code I write doesn’t look good. Fortunately while a great language can help you to write great code, great tools can help as well. As code grows it is easy for code bloat, dead code, or confusing complexities to slip in. The tools I review below can help with all of these problems. I recommend finding the one or two code quality tools you like best and starting to integrate them more into your development process.
Roodi gives you a bunch of interesting warnings about your Ruby code. We are about to release some code, so I took the opportunity to fix up anything Roodi complained about. It helped identify refactoring opportunities, both with long methods, and overly complex methods. The code and tests became cleaner and more granular after breaking some of the methods down. I even found and fixed one silly performance issue that was easy to see after refactoring, which improved the speed of our code. Spending some time with Roodi looks like it could easily improve the quality and readability of most Ruby projects with very little effort. I didn’t solve every problem because in one case I just didn’t think the method could be simplified anymore, but the majority of the suggestions were right on. Below is an example session with Roodi
dmayer$ sudo gem install roodi dmayer$ roodi lib/client/syncer.rb lib/client/syncer.rb:136 - Block cyclomatic complexity is 5. It should be 4 or less. lib/client/syncer.rb:61 - Method name "excluded" has a cyclomatic complexity is 10. It should be 8 or less. lib/client/syncer.rb:101 - Method name "should_be_excluded?" has a cyclomatic complexity is 9. It should be 8 or less. lib/client/syncer.rb:132 - Method name "find_changed_files" has a cyclomatic complexity is 10. It should be 8 or less. lib/client/syncer.rb:68 - Rescue block should not be empty. lib/client/syncer.rb:61 - Method name "excluded" has 25 lines. It should have 20 or less. lib/client/syncer.rb:132 - Method name "find_changed_files" has 27 lines. It should have 20 or less. Found 7 errors.
~/projects/gridtest/trunk dmayer$ roodi lib/client/syncer.rb lib/client/syncer.rb:148 - Block cyclomatic complexity is 5. It should be 4 or less. lib/client/syncer.rb:82 - Rescue block should not be empty. Found 2 errors.
I did have one problem with Roodi – the errors about rescue blocks just seemed to be incorrect. For code like the little example below it kept throwing the error even though I obviously am doing some work in the rescue code.
Roodi output: lib/client/syncer.rb:68 - Rescue block should not be empty. begin socket = TCPSocket.new(server_ip,server_port) socket.close return true rescue Errno::ECONNREFUSED return false end
Dust detects unused code like unused variables,branches, and blocks. I look forward to see how the project progresses. Right now there doesn’t seem to be much out there on the web, and the README is pretty bare bones. Once you can pass it some files to scan, I think this will be something really useful. For now I didn’t think there wasn’t much I could actually do besides check it out. Kevin, who also helped create the very cool Heckle, does claim that code scanning is coming soon, so I look forward to doing a more detailed write up eventually.
Flog gives feedback about the quality of your code by scoring code using the ABC metric. Using Flog to help guide refactoring, code cleanup, and testing efforts can be highly effective. It is a little easier to understand the reports after reading how Flog scores your code, and what is a good Flog score. Once you get used to working with Flog you will likely want to run it often against your whole project after making any significant changes. There are two easy ways to do this a handy Flog Rake task or MetricFu which works with both Flog and Saikuro.
Running Flog against any subset of a project is easy, here I am running it against our client libraries
find ./lib/client/ -name \*.rb | xargs flog -n -m > flog.log
Here some example Flog output when run against our client code.
Total score = 1364.52395469781 Client#send_tests: (64.3) 14.3: assignment 13.9: puts 10.7: branch 10.5: send 4.7: send_quit 3.4: message 3.4: now 2.0: create_queue_test_msg 1.9: create_run_msg 1.9: test_files 1.8: dump 1.7: each 1.7: report_start 1.7: length 1.7: get_tests 1.7: - 1.7: open 1.7: load_file 1.6: empty? 1.6: nil? 1.6: use_cache 1.6: exists? ModClient#send_file: (32.0) 12.4: branch 5.4: + 4.3: assignment 3.9: send 3.1: puts 2.9: == 2.9: exists? 2.9: directory? 1.9: strftime 1.8: to_s 1.5: read 1.5: create_file_msg 1.4: info Syncer#sync: (30.8) 13.2: assignment 8.6: branch 3.6: inspect 3.2: info 3.0: puts 2.8: + 2.6: empty? 1.7: map 1.5: now 1.5: length 1.4: send_files 1.3: max 1.3: > 1.3: find_changed_files 1.3: write_sync_time Syncer#find_changed_files: (26.2) 15.6: assignment 8.7: branch 3.5: << 1.8: to_s 1.7: get_relative_path 1.7: > 1.7: mtime 1.6: exists? 1.6: == 1.5: prune 1.4: should_be_excluded? 1.3: get_removed_files 1.3: find ... and so on ...
Saikuro is another code complexity tool. It seems to give a little less information than some of the others. It does generate nice HTML reports. Like other code complexity tools it can be helpful to discover the most complex parts of your projects for refactoring and to help focus your testing. I liked the way Flog broke things down for me into a bit more detail, but either is a useful tool and I am sure it is a matter of preference depending on what you are looking for.