I have been looking for a Ruby/Rails equivalent of checkstyle since I started working with it, but have only found a single orphaned project. Until yesterday that is. Yesterday’s Double Shot had a link to the Roodi project. Roodi stands for ‘Ruby Object Oriented Design Inferometer’, but essentially it is checkstyle for Ruby.
The Java community has a rich set of guidelines to build static checks from, but the Ruby community seems to lack that cohesion. That is going to make for an interesting collection of checks over time and no doubt some discussion over what should be and checked and how. Here are the checks it currently does:
- AssignmentInConditionalCheck – Check for an assignment inside a conditional. It‘s probably a mistaken equality comparison.
- CaseMissingElseCheck – Check that case statements have an else statement so that all cases are covered.
- ClassLineCountCheck – Check that the number of lines in a class is below the threshold.
- ClassNameCheck – Check that class names match convention.
- CyclomaticComplexityBlockCheck – Check that the cyclomatic complexity of all blocks is below the threshold.
- CyclomaticComplexityMethodCheck – Check that the cyclomatic complexity of all methods is below the threshold.
- EmptyRescueBodyCheck – Check that there are no empty rescue blocks.
- ForLoopCheck – Check that for loops aren‘t used (Use Enumerable.each instead)
- MethodLineCountCheck – Check that the number of lines in a method is below the threshold.
- MethodNameCheck – Check that method names match convention.
- ModuleLineCountCheck – Check that the number of lines in a module is below the threshold.
- ModuleNameCheck – Check that module names match convention.
- ParameterNumberCheck – Check that the number of parameters on a method is below the threshold.
Thats all well and good, but a tool that is not run is just a clever piece of code. And relying on people to remember to run a tool is not a plan I would bet the house on. But that is why we have things like CruiseControl.
Here is a pretty heavily edited version of our Rakefile.
desc "Cruise Control"
namespace :cruise do
output_dir = ENV["CC_BUILD_ARTIFACTS"]
task :build do
...
CruiseControl::invoke_rake_task 'cruise:roodi'
end
desc "Run the roodi tests"
task :roodi do
`roodi -config="#{RAILS_ROOT}/config/roodi.yml" "#{RAILS_ROOT}/app/**/*.rb" > #{output_dir}/roodi.txt`
end
end
There is not (yet) a roodi_on_rails plugin, though I expect it is only a matter of time, so I just run the tool on the commandline and put the output in the place CruiseControl needs it to display it on the report page. The output is, to say the least, un-pretty but if the community backs this tool I can see it growing nice rspec like reports with links to the actual offending line.
Oh, and here is config/roodi.yml, which is just the default configs for all the rules, but explicit is better than implicit.
AssignmentInConditionalCheck: { }
CaseMissingElseCheck: { }
ClassLineCountCheck: { line_count: 300 }
ClassNameCheck: { pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/ }
CyclomaticComplexityBlockCheck: { complexity: 4 }
CyclomaticComplexityMethodCheck: { complexity: 8 }
EmptyRescueBodyCheck: { }
ForLoopCheck: { }
MethodLineCountCheck: { line_count: 20 }
MethodNameCheck: { pattern: !ruby/regexp /^[_a-z<>=\[\]|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/ }
ModuleLineCountCheck: { line_count: 300 }
ModuleNameCheck: { pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/ }
ParameterNumberCheck: { parameter_count: 5 }