Software metrics (and tools) for Rails projects (part 1)

(11.02.2009)

In this article I’d like to explore some tools that help us to pick the insights of some software metrics on our Rails projects. Even if (sometimes) software metrics are used on BIG projects, having them can come in hand to look at “health status” on our Rails projects too.

We do this with metric_fu plugin for Rails, written by Jake Scruggs.

For the sake of readability I’ve split this article in two parts, the second part will come soon.

First step: install the plugin

Obviously the first step is to create a new Rails project:

$ rails my_project

Then, add this line in config/environment.rb:

config.gem 'jscruggs-metric_fu', :version => '0.9.0', :lib => 'metric_fu', :source => 'http://gems.github.com'

At this point, hit the following commands:

$ rake gems:install
$ rake gems:unpack

Now we have the gems under vendor/gems directory (and that’s fine if we move our Rails project from one place to another).

Now add the following line to Rakefile in order to have specific tasks related to metrics:

require 'metric_fu'

and this one to (minimally) configure metric_fu plugin:

MetricFu::Configuration.run do |config|
  config.churn = { :start_date => lambda{ 3.months.ago }, :minimum_churn_count => 3 }
  config.saikuro = { "--warn_cyclo" => "3", "--error_cyclo" => "4" }
end

And finally we have all related task to measure metrics:

rake metrics:all                 # Generate coverage, cyclomatic complexity,...
rake metrics:all_with_migrate    # Generate metrics after migrating...
rake metrics:churn               # Which files change the most
rake metrics:coverage            # Generate and open coverage report
rake metrics:coverage:clean      # Delete aggregate coverage data.
rake metrics:coverage:clobber_do # Remove rcov products for do
rake metrics:coverage:do         # RCov task to generate report
rake metrics:flay                # Generate code duplication report with flay
rake metrics:flog:all            # Generate and open flog report
rake metrics:flog:clean          # Delete aggregate flog data.
rake metrics:flog:controllers    # Flog code in app/controllers
rake metrics:flog:custom         # Generate a flog report from spec...
rake metrics:flog:helpers        # Flog code in app/helpers
rake metrics:flog:lib            # Flog code in lib
rake metrics:flog:models         # Flog code in app/models
rake metrics:reek                # A code smell report using Reek
rake metrics:roodi               # A Ruby coding standards report using Roodi
rake metrics:saikuro             # A cyclomatic complexity report using Saikuro
rake metrics:stats               # A stats report

Now we can see what each rake task is for, experimenting a little with it.

Before going further letís put our project under version control with Git:

$ cd my_project
$ git init
$ git add .
$ git commit -m "Initial commit"

and add some model/view/controllers in order to have some code on which take measurements:

$ ruby script/generate scaffold Post title:string body:text
$ rake db:migrate
$ git commit -am "Added Post (w/ scaffold)"

Done, time to start now.

Churn

Churn measure which files in our project changed most. A file that changes often over time may be a bad sign: maybe it needs some refactoring, maybe a new model is necessary, and so on…

Churn inferences changes accessing Git/Svn commit logs, so feed him with some changes. Lets suppose we don’t want to access Post on POST via XML, so we delete some lines from app/controllers/posts_controller.rb:

# POST /posts
def create
  @post = Post.new(params[:post])

  respond_to do |format|
    if @post.save
      flash[:notice] = 'Post was successfully created.'
      format.html { redirect_to(@post) }
    else
      format.html { render :action => "new" }
    end
  end
end

And make a commit:

$ git commit -a -m "Prevented POSTing on Post via XML"

But now we see that also on PUT verb we have the same problem, so (always on app/controllers/posts_controller.rb):

# PUT /posts/1
def update
  @post = Post.find(params[:id])
  respond_to do |format|
    if @post.update_attributes(params[:post])
      flash[:notice] = 'Post was successfully updated.'
      format.html { redirect_to(@post) }
    else
      format.html { render :action => "edit" }
    end
  end
end

And finalize with a commit:

$ git commit -a -m "Prevented PUTting on Post via XML"

Now, hitting on the command line:

$ rake metrics:churn

opens a new Safari (or our default browser) window showing advices for the most changed files:

Flog

Flog tell us which files have the most “tortured” code. It applies an ABC metric to the code present in each project file and shows a summary report like this one:

ABC metric measures the Euclidean distance from the origin in 3-dimensional space formed by:

So, the more “linear” the code le lower is the score (and it tends to be more manageable). On the contrary if we have a code with lot of branch, subconditions, assignments to support variables…well, it’ll show an high score. And this is a bad sign…

Used with cyclomatic complexity indicator it can show us the code most prone to hold back bugs.

Flay

One of the “mantra” of the Rails community is the DRY principle: Don’t Repeat Yourself. Well, even if this should be understood as a general principle we should also avoid repetitions in single file.

Flay to the rescue! Flay analyzes “structural similarities” (branch, cycle, etc) in ruby code: if two pieces of code are similar they are good candidates for refactoring.

In our project, issuing the rake task:

$ rake metrics:flay

tells us that in posts_controller.rb the two methods index and new present similar structures:

def index
  @posts = Post.all

  respond_to do |format|
    format.html # index.html.erb
    format.xml  { render :xml => @posts }
  end
end

def new
  @post = Post.new

  respond_to do |format|
    format.html # new.html.erb
    format.xml  { render :xml => @post }
  end
end

Even if it’s not the case, Flay does a nice job telling us cases when potential refactoring exists. In fact the two above methods have exactly the same structure. Imagine the benefit of this tool in more complex situations…

Let’s continue on the second part.



Comments

(Comments)
blog comments powered by Disqus