Saikuro

Saikuro is a Ruby cyclomatic complexity analyzer. When given Ruby source code Saikuro will generate a report listing the cyclomatic complexity of each method found. In addition, Saikuro counts the number of lines per method and can generate a listing of the number of tokens on each line of code.

License:

Saikuro uses the BSD license. See attached LICENSE file.

Installation:

Install atoulme-Saikuro using gem install and have at it.

Usage:

Saikuro is a command line program.

Running “saikuro -h” will output a usage statement describing all the various arguments you can pass to it.

saikuro -c -p tests/samples.rb

The above command is a simple example that generates a cyclomatic complexity report on the samples.rb file, using the default filter, warning and error settings. The report is saved in the current directory.

A more detailed example is

saikuro -c -t -i tests -y 0 -w 11 -e 16 -o out/

This will analyze all Ruby files found in the “tests/” directory. Saikuro will generate a token count report and a cyclomatic complexity report in the “out” directory . The “-y 0” command will turn off filtering and thus show the complexity of all methods. The “-w 11” will mark all methods with a complexity of 11 or higher with a warning. Finally, “-e 16” will flag all methods with a complexity of 16 or higher with an error.

Saikuro is also a Ruby library.

You can call it from your application to run metrics. It’s perfectly suited for a Rake task, right in the middle of your build. An example from Buildr:

gem 'atoulme-Saikuro'
require 'saikuro'
output_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", "_reports", "saikuro"))
base_dir = Pathname.new(File.expand_path(File.join(File.dirname(__FILE__), "..")))
rb_files = ["lib", "addon"].collect { |folder| 
  FileList[File.expand_path(File.join(File.dirname(__FILE__), "..", folder, "**", "*.rb"))]
}.flatten.collect {|path| 
  Pathname.new(path).relative_path_from(base_dir).to_s
}
SaikuroRunner.new.run(rb_files, output_dir)

About Cyclomatic Complexity:

The following document provides a very good and detailed description by the author of cyclomatic complexity.

NIST Special Publication 500-235 Structured Testing: A Testing Methodology Using the Cyclomatic Complexity Metric

By Arthur H. Watson and Thomas J. McCabe HTML hissa.nist.gov/HHRFdata/Artifacts/ITLdoc/235/title.htm PDF www.mccabe.com/iq_research_nist.htm

How and what Saikuro counts to calculate the cyclomatic complexity:

Saikuro uses the Simplified Complexity Calculation, which is just adding up the number of branch points in a method.

Each method starts with a complexity of 1, because there is at least one path through the code. Then each conditional or looping operator (if, unless, while, until, for, elsif, when) adds one point to the complexity. Each “when” in a case statement adds one point. Also each “rescue” statement adds one.

Saikuro also regards blocks as an addition to a method’s complexity because in many cases a block does add a path that may be traversed. For example, invoking the “each” method of an array with a block would only traverse the give block if the array is not empty. Thus if you want to find the basis set to get 100% coverage of your code then a block should add one point to the method’s complexity. It is not yet for sure however to what level the accuracy is decreased through this measurement, as normal Ruby code uses blocks quite heavily and new paths are not necessarily introduced by every block.

In addition, the short-circuiting “and” operators (&& and “and”) currently do not contribute to a method’s complexity, although McCabe’s paper listed above suggests doing so.

#Example for "and" operator handling:

# Starting values for case 1 and 2
x = false
y = 15
r, q = nil

# case 1
puts "W" if ((r = x) && (q = y))
puts r # => false
puts q # => nil

# case 2
puts "W" if ((q = y) && (r = x))
puts r # => false
puts q # => 15

Case 1 illustrates why “and” operators should add to a method’s complexity, because the result of ( r = x ) is false the if statement stops and returns false without evaluating the ( q = y ) branch. Thus if a total coverage of source code is desired, one point should be added to the method’s complexity.

So why is it not added? Mainly, because we have not gotten around to it. We are wondering if this would increase the noise more than it should.

Tests:

In the test directory is a sample file that has examples of the various possible cases that we examined and documented the expected cyclomatic complexity result. If you find mistakes or missing tests please report them.

Contact:

Saikuro is written by Zev Blut (zb at ubit dot com) Morton Jonuschat Antoine Toulme (antoine at lunar-ocean.com)

Acknowledgments:

Thanks to Elbert Corpuz for writing the CSS for the HTML output!

Other metric tools for Ruby:

Ryan Davis has an abc metric program as an example in his ParseTree product: www.zenspider.com/ZSS/Products/ParseTree/

The PMD project has a tool called CPD that can scan Ruby source code looking for source duplication: pmd.sourceforge.net/