metric_fu 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/HISTORY +13 -5
- data/README +2 -2
- data/Rakefile +1 -1
- data/TODO +3 -1
- data/lib/base/base_template.rb +16 -16
- data/lib/base/churn_analyzer.rb +3 -3
- data/lib/base/code_issue.rb +8 -8
- data/lib/base/configuration.rb +24 -22
- data/lib/base/flay_analyzer.rb +2 -2
- data/lib/base/flog_analyzer.rb +4 -4
- data/lib/base/generator.rb +21 -21
- data/lib/base/graph.rb +15 -10
- data/lib/base/line_numbers.rb +56 -55
- data/lib/base/location.rb +68 -66
- data/lib/base/metric_analyzer.rb +21 -21
- data/lib/base/ranking.rb +23 -22
- data/lib/base/rcov_analyzer.rb +3 -3
- data/lib/base/reek_analyzer.rb +12 -10
- data/lib/base/report.rb +9 -9
- data/lib/base/roodi_analyzer.rb +2 -2
- data/lib/base/saikuro_analyzer.rb +4 -4
- data/lib/base/scoring_strategies.rb +5 -5
- data/lib/base/stats_analyzer.rb +2 -2
- data/lib/base/table.rb +4 -4
- data/lib/generators/churn.rb +1 -1
- data/lib/generators/flay.rb +1 -1
- data/lib/generators/hotspots.rb +4 -4
- data/lib/generators/rails_best_practices.rb +1 -1
- data/lib/generators/rcov.rb +17 -17
- data/lib/generators/reek.rb +2 -2
- data/lib/generators/saikuro.rb +42 -42
- data/lib/generators/stats.rb +6 -6
- data/lib/graphs/engines/bluff.rb +1 -1
- data/lib/graphs/engines/gchart.rb +7 -7
- data/lib/graphs/flog_grapher.rb +3 -3
- data/lib/graphs/grapher.rb +1 -1
- data/lib/metric_fu.rb +2 -2
- data/lib/templates/awesome/churn.html.erb +1 -1
- data/lib/templates/awesome/css/integrity.css +0 -1
- data/lib/templates/awesome/flay.html.erb +2 -2
- data/lib/templates/awesome/flog.html.erb +2 -2
- data/lib/templates/awesome/hotspots.html.erb +4 -4
- data/lib/templates/awesome/index.html.erb +2 -2
- data/lib/templates/awesome/rails_best_practices.html.erb +1 -1
- data/lib/templates/awesome/rcov.html.erb +1 -1
- data/lib/templates/awesome/roodi.html.erb +1 -1
- data/lib/templates/awesome/saikuro.html.erb +3 -3
- data/lib/templates/awesome/stats.html.erb +1 -1
- data/lib/templates/standard/churn.html.erb +2 -2
- data/lib/templates/standard/default.css +4 -4
- data/lib/templates/standard/flay.html.erb +4 -4
- data/lib/templates/standard/flog.html.erb +1 -1
- data/lib/templates/standard/hotspots.html.erb +4 -4
- data/lib/templates/standard/index.html.erb +2 -2
- data/lib/templates/standard/rails_best_practices.html.erb +2 -2
- data/lib/templates/standard/rcov.html.erb +2 -2
- data/lib/templates/standard/reek.html.erb +1 -1
- data/lib/templates/standard/roodi.html.erb +2 -2
- data/lib/templates/standard/saikuro.html.erb +4 -4
- data/lib/templates/standard/stats.html.erb +2 -2
- data/spec/base/base_template_spec.rb +1 -1
- data/spec/base/configuration_spec.rb +36 -36
- data/spec/base/generator_spec.rb +10 -10
- data/spec/base/graph_spec.rb +41 -4
- data/spec/base/line_numbers_spec.rb +22 -22
- data/spec/base/report_spec.rb +9 -9
- data/spec/generators/churn_spec.rb +4 -4
- data/spec/generators/flay_spec.rb +31 -31
- data/spec/generators/flog_spec.rb +18 -18
- data/spec/generators/rails_best_practices_spec.rb +6 -6
- data/spec/generators/rcov_spec.rb +18 -18
- data/spec/generators/reek_spec.rb +10 -10
- data/spec/generators/roodi_spec.rb +2 -2
- data/spec/generators/saikuro_spec.rb +7 -7
- data/spec/generators/stats_spec.rb +6 -6
- data/spec/graphs/engines/gchart_spec.rb +8 -8
- data/spec/graphs/flog_grapher_spec.rb +8 -8
- data/spec/resources/line_numbers/foo.rb +7 -7
- data/spec/resources/line_numbers/module.rb +2 -2
- data/spec/resources/line_numbers/module_surrounds_class.rb +6 -6
- data/spec/resources/saikuro/index_cyclo.html +2 -2
- data/spec/resources/yml/20090630.yml +349 -349
- data/spec/resources/yml/metric_missing.yml +1 -1
- data/tasks/metric_fu.rake +4 -4
- metadata +4 -4
data/lib/base/generator.rb
CHANGED
@@ -2,18 +2,18 @@ module MetricFu
|
|
2
2
|
|
3
3
|
# = Generator
|
4
4
|
#
|
5
|
-
# The Generator class is an abstract class that provides the
|
5
|
+
# The Generator class is an abstract class that provides the
|
6
6
|
# skeleton for producing different types of metrics.
|
7
7
|
#
|
8
|
-
# It drives the production of the metrics through a template
|
8
|
+
# It drives the production of the metrics through a template
|
9
9
|
# method - #generate_report(options={}). This method calls
|
10
10
|
# #emit, #analyze and #to_h in order to produce the metrics.
|
11
11
|
#
|
12
12
|
# To implement a concrete class to generate a metric, therefore,
|
13
|
-
# the class must implement those three methods.
|
13
|
+
# the class must implement those three methods.
|
14
14
|
#
|
15
|
-
# * #emit should take care of running the metric tool and
|
16
|
-
# gathering its output.
|
15
|
+
# * #emit should take care of running the metric tool and
|
16
|
+
# gathering its output.
|
17
17
|
# * #analyze should take care of manipulating the output from
|
18
18
|
# #emit and making it possible to store it in a programmatic way.
|
19
19
|
# * #to_h should provide a hash representation of the output from
|
@@ -37,9 +37,9 @@ module MetricFu
|
|
37
37
|
create_output_dir_if_missing
|
38
38
|
create_data_dir_if_missing
|
39
39
|
end
|
40
|
-
|
41
|
-
# Creates a new generator and returns the output of the
|
42
|
-
# #generate_report method. This is the typical way to
|
40
|
+
|
41
|
+
# Creates a new generator and returns the output of the
|
42
|
+
# #generate_report method. This is the typical way to
|
43
43
|
# generate a new MetricFu report. For more information see
|
44
44
|
# the #generate_report instance method.
|
45
45
|
#
|
@@ -66,27 +66,27 @@ module MetricFu
|
|
66
66
|
def self.class_name
|
67
67
|
self.to_s.split('::').last.downcase
|
68
68
|
end
|
69
|
-
|
69
|
+
|
70
70
|
# Returns the directory where the Generator will write any output
|
71
71
|
def self.metric_directory
|
72
|
-
File.join(MetricFu.scratch_directory, class_name)
|
72
|
+
File.join(MetricFu.scratch_directory, class_name)
|
73
73
|
end
|
74
74
|
|
75
75
|
def create_metric_dir_if_missing #:nodoc:
|
76
76
|
unless File.directory?(metric_directory)
|
77
|
-
FileUtils.mkdir_p(metric_directory, :verbose => false)
|
77
|
+
FileUtils.mkdir_p(metric_directory, :verbose => false)
|
78
78
|
end
|
79
79
|
end
|
80
|
-
|
80
|
+
|
81
81
|
def create_output_dir_if_missing #:nodoc:
|
82
82
|
unless File.directory?(MetricFu.output_directory)
|
83
|
-
FileUtils.mkdir_p(MetricFu.output_directory, :verbose => false)
|
83
|
+
FileUtils.mkdir_p(MetricFu.output_directory, :verbose => false)
|
84
84
|
end
|
85
85
|
end
|
86
|
-
|
86
|
+
|
87
87
|
def create_data_dir_if_missing #:nodoc:
|
88
88
|
unless File.directory?(MetricFu.data_directory)
|
89
|
-
FileUtils.mkdir_p(MetricFu.data_directory, :verbose => false)
|
89
|
+
FileUtils.mkdir_p(MetricFu.data_directory, :verbose => false)
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
@@ -103,7 +103,7 @@ module MetricFu
|
|
103
103
|
end
|
104
104
|
paths - files_to_remove
|
105
105
|
end
|
106
|
-
|
106
|
+
|
107
107
|
# Defines some hook methods for the concrete classes to hook into.
|
108
108
|
%w[emit analyze].each do |meth|
|
109
109
|
define_method("before_#{meth}".to_sym) {}
|
@@ -132,13 +132,13 @@ module MetricFu
|
|
132
132
|
|
133
133
|
def round_to_tenths(decimal)
|
134
134
|
decimal = 0.0 if decimal.to_s.eql?('NaN')
|
135
|
-
(decimal * 10).round / 10.0
|
135
|
+
(decimal * 10).round / 10.0
|
136
136
|
end
|
137
137
|
|
138
138
|
def emit #:nodoc:
|
139
139
|
raise <<-EOF
|
140
140
|
This method must be implemented by a concrete class descending
|
141
|
-
from Generator. See generator class documentation for more
|
141
|
+
from Generator. See generator class documentation for more
|
142
142
|
information.
|
143
143
|
EOF
|
144
144
|
end
|
@@ -146,15 +146,15 @@ module MetricFu
|
|
146
146
|
def analyze #:nodoc:
|
147
147
|
raise <<-EOF
|
148
148
|
This method must be implemented by a concrete class descending
|
149
|
-
from Generator. See generator class documentation for more
|
149
|
+
from Generator. See generator class documentation for more
|
150
150
|
information.
|
151
151
|
EOF
|
152
152
|
end
|
153
|
-
|
153
|
+
|
154
154
|
def to_graph #:nodoc:
|
155
155
|
raise <<-EOF
|
156
156
|
This method must be implemented by a concrete class descending
|
157
|
-
from Generator. See generator class documentation for more
|
157
|
+
from Generator. See generator class documentation for more
|
158
158
|
information.
|
159
159
|
EOF
|
160
160
|
end
|
data/lib/base/graph.rb
CHANGED
@@ -5,35 +5,40 @@ module MetricFu
|
|
5
5
|
end
|
6
6
|
|
7
7
|
class Graph
|
8
|
-
|
8
|
+
|
9
9
|
attr_accessor :clazz
|
10
|
-
|
10
|
+
|
11
11
|
def initialize
|
12
12
|
self.clazz = []
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
def add(graph_type, graph_engine)
|
16
16
|
grapher_name = graph_type.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase } + graph_engine.to_s.capitalize + "Grapher"
|
17
17
|
self.clazz.push MetricFu.const_get(grapher_name).new
|
18
18
|
end
|
19
|
-
|
20
|
-
|
19
|
+
|
20
|
+
|
21
21
|
def generate
|
22
22
|
return if self.clazz.empty?
|
23
23
|
puts "Generating graphs"
|
24
24
|
Dir[File.join(MetricFu.data_directory, '*.yml')].sort.each do |metric_file|
|
25
25
|
puts "Generating graphs for #{metric_file}"
|
26
|
-
|
27
|
-
y, m, d = date[0..3].to_i, date[4..5].to_i, date[6..7].to_i
|
26
|
+
date_parts = year_month_day_from_filename(metric_file)
|
28
27
|
metrics = YAML::load(File.open(metric_file))
|
29
|
-
|
28
|
+
|
30
29
|
self.clazz.each do |grapher|
|
31
|
-
grapher.get_metrics(metrics, "#{m}/#{d}")
|
30
|
+
grapher.get_metrics(metrics, "#{date_parts[:m]}/#{date_parts[:d]}")
|
32
31
|
end
|
33
32
|
end
|
34
33
|
self.clazz.each do |grapher|
|
35
34
|
grapher.graph!
|
36
35
|
end
|
37
36
|
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def year_month_day_from_filename(path_to_file_with_date)
|
40
|
+
date = path_to_file_with_date.match(/\/(\d+).yml$/)[1]
|
41
|
+
{:y => date[0..3].to_i, :m => date[4..5].to_i, :d => date[6..7].to_i}
|
42
|
+
end
|
38
43
|
end
|
39
|
-
end
|
44
|
+
end
|
data/lib/base/line_numbers.rb
CHANGED
@@ -1,65 +1,66 @@
|
|
1
1
|
require 'ruby_parser'
|
2
|
+
module MetricFu
|
3
|
+
class LineNumbers
|
2
4
|
|
3
|
-
|
5
|
+
def initialize(contents)
|
6
|
+
rp = RubyParser.new
|
7
|
+
file_sexp = rp.parse(contents)
|
8
|
+
@locations = {}
|
9
|
+
case file_sexp[0]
|
10
|
+
when :class
|
11
|
+
process_class(file_sexp)
|
12
|
+
when :module
|
13
|
+
process_module(file_sexp)
|
14
|
+
when :block
|
15
|
+
file_sexp.each_of_type(:class) { |sexp| process_class(sexp) }
|
16
|
+
else
|
17
|
+
end
|
18
|
+
end
|
4
19
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
case file_sexp[0]
|
10
|
-
when :class
|
11
|
-
process_class(file_sexp)
|
12
|
-
when :module
|
13
|
-
process_module(file_sexp)
|
14
|
-
when :block
|
15
|
-
file_sexp.each_of_type(:class) { |sexp| process_class(sexp) }
|
16
|
-
else
|
20
|
+
def in_method? line_number
|
21
|
+
!!@locations.detect do |method_name, line_number_range|
|
22
|
+
line_number_range.include?(line_number)
|
23
|
+
end
|
17
24
|
end
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
25
|
+
|
26
|
+
def method_at_line line_number
|
27
|
+
found_method_and_range = @locations.detect do |method_name, line_number_range|
|
28
|
+
line_number_range.include?(line_number)
|
29
|
+
end
|
30
|
+
return nil unless found_method_and_range
|
31
|
+
found_method_and_range.first
|
23
32
|
end
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def process_module(sexp)
|
37
|
+
module_name = sexp[1]
|
38
|
+
sexp.each_of_type(:class) do |sexp|
|
39
|
+
process_class(sexp, module_name)
|
40
|
+
hide_methods_from_next_round(sexp)
|
41
|
+
end
|
42
|
+
process_class(sexp)
|
29
43
|
end
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
module_name = sexp[1]
|
38
|
-
sexp.each_of_type(:class) do |sexp|
|
39
|
-
process_class(sexp, module_name)
|
40
|
-
hide_methods_from_next_round(sexp)
|
44
|
+
|
45
|
+
def process_class(sexp, module_name=nil)
|
46
|
+
class_name = sexp[1]
|
47
|
+
process_class_self_blocks(sexp, class_name)
|
48
|
+
module_name_string = module_name ? "#{module_name}::" : nil
|
49
|
+
sexp.each_of_type(:defn) { |s| @locations["#{module_name_string}#{class_name}##{s[1]}"] = (s.line)..(s.last.line) }
|
50
|
+
sexp.each_of_type(:defs) { |s| @locations["#{module_name_string}#{class_name}::#{s[2]}"] = (s.line)..(s.last.line) }
|
41
51
|
end
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
module_name_string = module_name ? "#{module_name}::" : nil
|
49
|
-
sexp.each_of_type(:defn) { |s| @locations["#{module_name_string}#{class_name}##{s[1]}"] = (s.line)..(s.last.line) }
|
50
|
-
sexp.each_of_type(:defs) { |s| @locations["#{module_name_string}#{class_name}::#{s[2]}"] = (s.line)..(s.last.line) }
|
51
|
-
end
|
52
|
-
|
53
|
-
def process_class_self_blocks(sexp, class_name)
|
54
|
-
sexp.each_of_type(:sclass) do |sexp_in_class_self_block|
|
55
|
-
sexp_in_class_self_block.each_of_type(:defn) { |s| @locations["#{class_name}::#{s[1]}"] = (s.line)..(s.last.line) }
|
56
|
-
hide_methods_from_next_round(sexp_in_class_self_block)
|
52
|
+
|
53
|
+
def process_class_self_blocks(sexp, class_name)
|
54
|
+
sexp.each_of_type(:sclass) do |sexp_in_class_self_block|
|
55
|
+
sexp_in_class_self_block.each_of_type(:defn) { |s| @locations["#{class_name}::#{s[1]}"] = (s.line)..(s.last.line) }
|
56
|
+
hide_methods_from_next_round(sexp_in_class_self_block)
|
57
|
+
end
|
57
58
|
end
|
59
|
+
|
60
|
+
def hide_methods_from_next_round(sexp)
|
61
|
+
sexp.find_and_replace_all(:defn, :ignore_me)
|
62
|
+
sexp.find_and_replace_all(:defs, :ignore_me)
|
63
|
+
end
|
64
|
+
|
58
65
|
end
|
59
|
-
|
60
|
-
def hide_methods_from_next_round(sexp)
|
61
|
-
sexp.find_and_replace_all(:defn, :ignore_me)
|
62
|
-
sexp.find_and_replace_all(:defs, :ignore_me)
|
63
|
-
end
|
64
|
-
|
65
66
|
end
|
data/lib/base/location.rb
CHANGED
@@ -1,83 +1,85 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module MetricFu
|
2
|
+
class Location
|
3
|
+
include Comparable
|
3
4
|
|
4
|
-
|
5
|
+
attr_accessor :class_name, :method_name, :file_path, :simple_method_name, :hash
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
7
|
+
def self.get(file_path, class_name, method_name)
|
8
|
+
# This could be more 'confident' using Maybe, but we want it to be as fast as possible
|
9
|
+
file_path_copy = file_path == nil ? nil : file_path.clone
|
10
|
+
class_name_copy = class_name == nil ? nil : class_name.clone
|
11
|
+
method_name_copy = method_name == nil ? nil : method_name.clone
|
12
|
+
key = [file_path_copy, class_name_copy, method_name_copy]
|
13
|
+
@@locations ||= {}
|
14
|
+
if @@locations.has_key?(key)
|
15
|
+
@@locations[key]
|
16
|
+
else
|
17
|
+
location = self.new(file_path_copy, class_name_copy, method_name_copy)
|
18
|
+
@@locations[key] = location
|
19
|
+
location.freeze # we cache a lot of method call results, so we want location to be immutable
|
20
|
+
location
|
21
|
+
end
|
20
22
|
end
|
21
|
-
end
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
24
|
+
def initialize(file_path, class_name, method_name)
|
25
|
+
@file_path = file_path
|
26
|
+
@class_name = class_name
|
27
|
+
@method_name = method_name
|
28
|
+
@simple_method_name = @method_name.sub(@class_name,'') unless @method_name == nil
|
29
|
+
@hash = [@file_path, @class_name, @method_name].hash
|
30
|
+
end
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
# TODO - we need this method (and hash accessor above) as a temporary hack where we're using Location as a hash key
|
33
|
+
def eql?(other)
|
34
|
+
[self.file_path.to_s, self.class_name.to_s, self.method_name.to_s] == [other.file_path.to_s, other.class_name.to_s, other.method_name.to_s]
|
35
|
+
end
|
36
|
+
# END we need these methods as a temporary hack where we're using Location as a hash key
|
35
37
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
match = nil
|
47
|
-
end
|
38
|
+
def self.for(class_or_method_name)
|
39
|
+
class_or_method_name = strip_modules(class_or_method_name)
|
40
|
+
if(class_or_method_name)
|
41
|
+
begin
|
42
|
+
match = class_or_method_name.match(/(.*)((\.|\#|\:\:[a-z])(.+))/)
|
43
|
+
rescue => error
|
44
|
+
#new error during port to metric_fu occasionally a unintialized
|
45
|
+
#MatchData object shows up here. Not expected.
|
46
|
+
match = nil
|
47
|
+
end
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
49
|
+
# reek reports the method with :: not # on modules like
|
50
|
+
# module ApplicationHelper \n def signed_in?, convert it so it records correctly
|
51
|
+
# but classes have to start with a capital letter... HACK for REEK bug, reported underlying issue to REEK
|
52
|
+
if(match)
|
53
|
+
class_name = strip_modules(match[1])
|
54
|
+
method_name = class_or_method_name.gsub(/\:\:/,"#")
|
55
|
+
else
|
56
|
+
class_name = strip_modules(class_or_method_name)
|
57
|
+
method_name = nil
|
58
|
+
end
|
55
59
|
else
|
56
|
-
class_name =
|
60
|
+
class_name = nil
|
57
61
|
method_name = nil
|
58
62
|
end
|
59
|
-
|
60
|
-
class_name = nil
|
61
|
-
method_name = nil
|
63
|
+
self.get(nil, class_name, method_name)
|
62
64
|
end
|
63
|
-
self.get(nil, class_name, method_name)
|
64
|
-
end
|
65
65
|
|
66
|
-
|
67
|
-
|
68
|
-
|
66
|
+
def <=>(other)
|
67
|
+
[self.file_path.to_s, self.class_name.to_s, self.method_name.to_s] <=> [other.file_path.to_s, other.class_name.to_s, other.method_name.to_s]
|
68
|
+
end
|
69
69
|
|
70
|
-
|
70
|
+
private
|
71
|
+
|
72
|
+
def self.strip_modules(class_or_method_name)
|
73
|
+
# reek reports the method with :: not # on modules like
|
74
|
+
# module ApplicationHelper \n def signed_in?, convert it so it records correctly
|
75
|
+
# but classes have to start with a capital letter... HACK for REEK bug, reported underlying issue to REEK
|
76
|
+
if(class_or_method_name=~/\:\:[A-Z]/)
|
77
|
+
class_or_method_name.split("::").last
|
78
|
+
else
|
79
|
+
class_or_method_name
|
80
|
+
end
|
71
81
|
|
72
|
-
def self.strip_modules(class_or_method_name)
|
73
|
-
# reek reports the method with :: not # on modules like
|
74
|
-
# module ApplicationHelper \n def signed_in?, convert it so it records correctly
|
75
|
-
# but classes have to start with a capital letter... HACK for REEK bug, reported underlying issue to REEK
|
76
|
-
if(class_or_method_name=~/\:\:[A-Z]/)
|
77
|
-
class_or_method_name.split("::").last
|
78
|
-
else
|
79
|
-
class_or_method_name
|
80
82
|
end
|
81
|
-
end
|
82
83
|
|
84
|
+
end
|
83
85
|
end
|
data/lib/base/metric_analyzer.rb
CHANGED
@@ -13,9 +13,9 @@ class MetricAnalyzer
|
|
13
13
|
else
|
14
14
|
@yaml = yaml
|
15
15
|
end
|
16
|
-
@file_ranking = Ranking.new
|
17
|
-
@class_ranking = Ranking.new
|
18
|
-
@method_ranking = Ranking.new
|
16
|
+
@file_ranking = MetricFu::Ranking.new
|
17
|
+
@class_ranking = MetricFu::Ranking.new
|
18
|
+
@method_ranking = MetricFu::Ranking.new
|
19
19
|
rankings = [@file_ranking, @class_ranking, @method_ranking]
|
20
20
|
|
21
21
|
tool_analyzers = [ReekAnalyzer.new, RoodiAnalyzer.new,
|
@@ -27,14 +27,14 @@ class MetricAnalyzer
|
|
27
27
|
columns = COMMON_COLUMNS + GRANULARITIES + tool_analyzers.map{|analyzer| analyzer.columns}.flatten
|
28
28
|
|
29
29
|
@table = make_table(columns)
|
30
|
-
|
30
|
+
|
31
31
|
# These tables are an optimization. They contain subsets of the master table.
|
32
32
|
# TODO - these should be pushed into the Table class now
|
33
33
|
@tool_tables = make_table_hash(columns)
|
34
34
|
@file_tables = make_table_hash(columns)
|
35
35
|
@class_tables = make_table_hash(columns)
|
36
36
|
@method_tables = make_table_hash(columns)
|
37
|
-
|
37
|
+
|
38
38
|
tool_analyzers.each do |analyzer|
|
39
39
|
analyzer.generate_records(@yaml[analyzer.name], @table)
|
40
40
|
end
|
@@ -62,11 +62,11 @@ class MetricAnalyzer
|
|
62
62
|
first_row = sub_table[0]
|
63
63
|
case item
|
64
64
|
when :class
|
65
|
-
Location.get(first_row.file_path, first_row.class_name, nil)
|
65
|
+
MetricFu::Location.get(first_row.file_path, first_row.class_name, nil)
|
66
66
|
when :method
|
67
|
-
Location.get(first_row.file_path, first_row.class_name, first_row.method_name)
|
67
|
+
MetricFu::Location.get(first_row.file_path, first_row.class_name, first_row.method_name)
|
68
68
|
when :file
|
69
|
-
Location.get(first_row.file_path, nil, nil)
|
69
|
+
MetricFu::Location.get(first_row.file_path, nil, nil)
|
70
70
|
else
|
71
71
|
raise ArgumentError, "Item must be :class, :method, or :file"
|
72
72
|
end
|
@@ -147,7 +147,7 @@ class MetricAnalyzer
|
|
147
147
|
|
148
148
|
def fix_row_file_path!(row)
|
149
149
|
# We know that Saikuro rows are broken
|
150
|
-
next unless row['metric'] == :saikuro
|
150
|
+
# next unless row['metric'] == :saikuro
|
151
151
|
key = [row['class_name'], row['method_name']]
|
152
152
|
current_file_path = row['file_path'].to_s
|
153
153
|
correct_file_path = @class_and_method_to_file[key]
|
@@ -155,7 +155,7 @@ class MetricAnalyzer
|
|
155
155
|
row['file_path'] = correct_file_path
|
156
156
|
else
|
157
157
|
# There wasn't an exact match, so we can do a substring match
|
158
|
-
matching_file_path = file_paths.detect {|file_path|
|
158
|
+
matching_file_path = file_paths.detect {|file_path|
|
159
159
|
file_path!=nil && file_path.include?(current_file_path)
|
160
160
|
}
|
161
161
|
if(matching_file_path)
|
@@ -180,32 +180,32 @@ class MetricAnalyzer
|
|
180
180
|
raise ArgumentError, "Invalid column name #{column_name}"
|
181
181
|
end
|
182
182
|
end
|
183
|
-
|
183
|
+
|
184
184
|
def calculate_metric_scores(granularity, analyzer)
|
185
|
-
metric_ranking = Ranking.new
|
185
|
+
metric_ranking = MetricFu::Ranking.new
|
186
186
|
metric_violations = @tool_tables[analyzer.name]
|
187
187
|
metric_violations.each do |row|
|
188
188
|
location = row[granularity]
|
189
189
|
metric_ranking[location] ||= []
|
190
190
|
metric_ranking[location] << analyzer.map(row)
|
191
191
|
end
|
192
|
-
|
192
|
+
|
193
193
|
metric_ranking.each do |item, scores|
|
194
194
|
metric_ranking[item] = analyzer.reduce(scores)
|
195
195
|
end
|
196
|
-
|
196
|
+
|
197
197
|
metric_ranking
|
198
198
|
end
|
199
199
|
|
200
200
|
def add_to_master_ranking(master_ranking, metric_ranking, analyzer)
|
201
201
|
metric_ranking.each do |item, _|
|
202
202
|
master_ranking[item] ||= 0
|
203
|
-
master_ranking[item] += analyzer.score(metric_ranking, item)
|
203
|
+
master_ranking[item] += analyzer.score(metric_ranking, item) # scaling? Do we just add in the raw score?
|
204
204
|
end
|
205
205
|
end
|
206
206
|
|
207
207
|
def most_common_column(column_name, size)
|
208
|
-
#grouping = Ruport::Data::Grouping.new(@table,
|
208
|
+
#grouping = Ruport::Data::Grouping.new(@table,
|
209
209
|
# :by => column_name,
|
210
210
|
# :order => lambda { |g| -g.size})
|
211
211
|
get_grouping(@table, :by => column_name, :order => lambda {|g| -g.size})
|
@@ -223,7 +223,7 @@ class MetricAnalyzer
|
|
223
223
|
return values
|
224
224
|
end
|
225
225
|
end
|
226
|
-
|
226
|
+
|
227
227
|
# TODO: As we get fancier, the presenter should
|
228
228
|
# be its own class, not just a method with a long
|
229
229
|
# case statement
|
@@ -246,7 +246,7 @@ class MetricAnalyzer
|
|
246
246
|
"found #{occurences} code duplications"
|
247
247
|
when :rcov
|
248
248
|
average_code_uncoverage = get_mean(group.column("percentage_uncovered"))
|
249
|
-
"#{"average " if occurences > 1}uncovered code is %.1f%" % average_code_uncoverage
|
249
|
+
"#{"average " if occurences > 1}uncovered code is %.1f%" % average_code_uncoverage
|
250
250
|
else
|
251
251
|
raise AnalysisError, "Unknown metric #{metric}"
|
252
252
|
end
|
@@ -320,13 +320,13 @@ class MetricAnalyzer
|
|
320
320
|
sum = collection.inject( nil ) { |sum,x| sum ? sum+x : x }
|
321
321
|
(sum.to_f / collection_length.to_f)
|
322
322
|
end
|
323
|
-
|
323
|
+
|
324
324
|
end
|
325
325
|
|
326
326
|
class Record
|
327
327
|
|
328
328
|
attr_reader :data
|
329
|
-
|
329
|
+
|
330
330
|
def initialize(data, columns)
|
331
331
|
@data = data
|
332
332
|
@columns = columns
|
@@ -380,7 +380,7 @@ class Grouping
|
|
380
380
|
end
|
381
381
|
end
|
382
382
|
if order
|
383
|
-
@arr = hash.sort_by &order
|
383
|
+
@arr = hash.sort_by &order
|
384
384
|
else
|
385
385
|
@arr = hash.to_a
|
386
386
|
end
|