lib/metric_fu/metrics/flog/flog.rb
require 'pathname'
require 'optparse'
require 'rbconfig'
class RubyParser
alias_method :original_process, :process
def process(s,f,t)
original_process(s,f,t)
rescue => e
if e.message =~ /wrong number of arguments/
original_process(s,f)
else
raise
end
end
end
module MetricFu
class Flog < Generator
def emit
files = []
MetricFu.flog[:dirs_to_flog].each do |directory|
directory = "." if directory=='./'
dir_files = Dir.glob("#{directory}/**/*.rb")
dir_files = remove_excluded_files(dir_files)
files += dir_files
end
options = ::Flog.parse_options ["--all", "--details"]
# TODO determine if flogging should continue despite errors
# options = ::Flog.parse_options ["--all", "--details", "--continue"]
@flogger = ::Flog.new options
@flogger.flog files
rescue LoadError => e
message = "#{e.class}\t#{e.message}\n\t#{e.backtrace.join('\n\t')}"
if MetricFu.configuration.mri?
puts "Flog Error: #{message}"
else
puts "Flog tasks only available in MRI: #{message}"
end
end
def analyze
@method_containers = {}
@flogger.calls.each do |full_method_name, operators|
container_name = full_method_name.split('#').first
path = @flogger.method_locations[full_method_name]
if @method_containers[container_name]
@method_containers[container_name].add_method(full_method_name, operators, @flogger.totals[full_method_name], path)
@method_containers[container_name].add_path(path)
else
mc = MethodContainer.new(container_name, path)
mc.add_method(full_method_name, operators, @flogger.totals[full_method_name], path)
@method_containers[container_name] = mc
end
end
end
def to_h
sorted_containers = @method_containers.values.sort_by {|c| c.highest_score}.reverse
{:flog => { :total => @flogger.total,
:average => @flogger.average,
:method_containers => sorted_containers.map {|method_container| method_container.to_h}}}
end
def per_file_info(out)
@method_containers.each_pair do |klass, container|
container.methods.each_pair do |method_name, data|
next if data[:path].nil?
file, line = data[:path].split(':')
out[file] ||= {}
out[file][line] ||= []
out[file][line] << {:type => :flog, :description => "Score of %.2f" % data[:score]}
end
end
end
end
class MethodContainer
attr_reader :methods
def initialize(name, path)
@name = name
add_path path
@methods = {}
end
def add_path path
return unless path
@path ||= path.split(':').first
end
def add_method(full_method_name, operators, score, path)
@methods[full_method_name] = {:operators => operators, :score => score, :path => path}
end
def to_h
{ :name => @name,
:path => @path || '',
:total_score => total_score,
:highest_score => highest_score,
:average_score => average_score,
:methods => @methods}
end
def highest_score
method_scores.max
end
private
def method_scores
@method_scores ||= @methods.values.map {|v| v[:score] }
end
def total_score
@total_score ||= method_scores.inject(0) {|sum, score| sum += score}
end
def average_score
total_score / method_scores.size.to_f
end
end
end