Matt Van Horn

Things I Like

(and some that I don't)

A rake task for ruby code analysis

July 30th 2012

I threw this together out of some tasks I found on the web, and some code I had used on http://www.livingbjj.com # lib/tasks/analysis.rake begin require ‘rubygems’ require ‘term/ansicolor’ require ‘reek/rake/task’

  Reek::Rake::Task.new do |t|
    t.source_files = "app"
    t.verbose = false
    t.fail_on_error = false
  end

  namespace :analyzer do
    desc "run all code analyzing tools (reek, flog, flay, rails_best_practices)"
    task :all => ['reek', 'flog:total', 'flay', 'rails_best_practices'] do
      message(:info, 'have been running all code analyzing tools')
    end

    desc "run reek and find code smells"
    task :reek do
      message(:info, 'Running reek and find code smells')
      Rake::Task['reek'].invoke
    end

    desc "run rails_best_practices and inform about found issues"
    task :rails_best_practices do
      require 'rails_best_practices'
      message(:info, 'Running rails_best_practices and inform about found issues')
      app_root = Rake.application.original_dir
      output_file = File.join(app_root, 'analyzer', 'rails_best_practices.html')
      analyzer = RailsBestPractices::Analyzer.new(app_root, {
        'format' => 'html',
        'with-textmate' => true,
        'output-file' => output_file
      })
      analyzer.analyze
      analyzer.output
      `open #{output_file}`
      fail "found bad practices" if analyzer.runner.errors.size > 0
    end

    namespace :flog do
      require 'flog'
      desc "Analyze total code complexity with flog"
      task :total do
        message(:info, 'Running flog total complexity')
        threshold = 1000
        flog = Flog.new
        flog.flog %w(app lib)
        flog.report
        fail "OMG What a confusing pile of crap! (#{flog.total} > #{threshold})" if flog.total > threshold
      end

      desc "Analyze for average code complexity"
      task :average do
        message(:info, 'Running flog average complexity')
        threshold = 25
        flog = Flog.new
        flog.flog %w(app lib)
        fail "Flog total too high! #{flog.average} > #{threshold}" if flog.average > threshold
      end

      desc "Analyze for individual code complexity"
      task :each do
        message(:info, 'Running flog individual complexity')
        threshold = 40
        flog = Flog.new
        flog.flog %w(app lib)

        bad_methods = flog.totals.select do |name, score|
          score > threshold
        end
        bad_methods.sort { |a,b| a[1] <=> b[1] }.each do |name, score|
          puts "%8.1f: %s" % [score, name]
        end
        fail "#{bad_methods.size} methods have a flog complexity >#{threshold}" unless bad_methods.empty?
      end
    end

    desc "run flay and analyze code for structural similarities"
    task :flay do
      require 'flay'
      message :info, 'Running flay and and analyze code for structural similarities'
      output = `flay #{FileList["lib/**/*.rb", "app/**/*.rb"].join(' ')}`
      fail "Error #{$?}: #{output}" unless $? == 0
      message :info, output
    end
  end

  def message(type, message)
    set_color(type)
    puts message
    reset_color
  end

  def set_color(type)
    term = Term::ANSIColor
    colors = {info: term.green, error: term.red}
    puts colors[type]
  end

  def reset_color
    puts Term::ANSIColor.reset
  end

rescue LoadError => err
  # WE DONT CARE ABOUT THIS ON HEROKU, BUT OUTPUT IS NICE IN DEV
  warn err.message
end
blog comments powered by Disqus