rferraz-metric_fu 2.1.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 +237 -0
- data/MIT-LICENSE +22 -0
- data/README +29 -0
- data/Rakefile +18 -0
- data/TODO +6 -0
- data/lib/base/base_template.rb +172 -0
- data/lib/base/churn_analyzer.rb +38 -0
- data/lib/base/code_issue.rb +97 -0
- data/lib/base/configuration.rb +199 -0
- data/lib/base/flay_analyzer.rb +50 -0
- data/lib/base/flog_analyzer.rb +43 -0
- data/lib/base/generator.rb +166 -0
- data/lib/base/graph.rb +44 -0
- data/lib/base/line_numbers.rb +74 -0
- data/lib/base/location.rb +85 -0
- data/lib/base/md5_tracker.rb +52 -0
- data/lib/base/metric_analyzer.rb +404 -0
- data/lib/base/ranking.rb +34 -0
- data/lib/base/rcov_analyzer.rb +43 -0
- data/lib/base/reek_analyzer.rb +163 -0
- data/lib/base/report.rb +108 -0
- data/lib/base/roodi_analyzer.rb +37 -0
- data/lib/base/saikuro_analyzer.rb +48 -0
- data/lib/base/scoring_strategies.rb +29 -0
- data/lib/base/stats_analyzer.rb +37 -0
- data/lib/base/table.rb +102 -0
- data/lib/generators/churn.rb +28 -0
- data/lib/generators/flay.rb +31 -0
- data/lib/generators/flog.rb +111 -0
- data/lib/generators/hotspots.rb +52 -0
- data/lib/generators/rails_best_practices.rb +53 -0
- data/lib/generators/rcov.rb +122 -0
- data/lib/generators/reek.rb +81 -0
- data/lib/generators/roodi.rb +35 -0
- data/lib/generators/saikuro.rb +256 -0
- data/lib/generators/stats.rb +58 -0
- data/lib/graphs/engines/bluff.rb +113 -0
- data/lib/graphs/engines/gchart.rb +157 -0
- data/lib/graphs/flay_grapher.rb +18 -0
- data/lib/graphs/flog_grapher.rb +57 -0
- data/lib/graphs/grapher.rb +11 -0
- data/lib/graphs/rails_best_practices_grapher.rb +19 -0
- data/lib/graphs/rcov_grapher.rb +18 -0
- data/lib/graphs/reek_grapher.rb +30 -0
- data/lib/graphs/roodi_grapher.rb +18 -0
- data/lib/graphs/stats_grapher.rb +20 -0
- data/lib/metric_fu.rb +40 -0
- data/lib/templates/awesome/awesome_template.rb +73 -0
- data/lib/templates/awesome/churn.html.erb +58 -0
- data/lib/templates/awesome/css/buttons.css +82 -0
- data/lib/templates/awesome/css/default.css +91 -0
- data/lib/templates/awesome/css/integrity.css +334 -0
- data/lib/templates/awesome/css/reset.css +7 -0
- data/lib/templates/awesome/css/syntax.css +19 -0
- data/lib/templates/awesome/flay.html.erb +34 -0
- data/lib/templates/awesome/flog.html.erb +55 -0
- data/lib/templates/awesome/hotspots.html.erb +62 -0
- data/lib/templates/awesome/index.html.erb +34 -0
- data/lib/templates/awesome/layout.html.erb +30 -0
- data/lib/templates/awesome/rails_best_practices.html.erb +27 -0
- data/lib/templates/awesome/rcov.html.erb +42 -0
- data/lib/templates/awesome/reek.html.erb +40 -0
- data/lib/templates/awesome/roodi.html.erb +27 -0
- data/lib/templates/awesome/saikuro.html.erb +71 -0
- data/lib/templates/awesome/stats.html.erb +51 -0
- data/lib/templates/javascripts/bluff-min.js +1 -0
- data/lib/templates/javascripts/excanvas.js +35 -0
- data/lib/templates/javascripts/js-class.js +1 -0
- data/lib/templates/standard/churn.html.erb +31 -0
- data/lib/templates/standard/default.css +64 -0
- data/lib/templates/standard/flay.html.erb +34 -0
- data/lib/templates/standard/flog.html.erb +57 -0
- data/lib/templates/standard/hotspots.html.erb +54 -0
- data/lib/templates/standard/index.html.erb +41 -0
- data/lib/templates/standard/rails_best_practices.html.erb +29 -0
- data/lib/templates/standard/rcov.html.erb +43 -0
- data/lib/templates/standard/reek.html.erb +42 -0
- data/lib/templates/standard/roodi.html.erb +29 -0
- data/lib/templates/standard/saikuro.html.erb +84 -0
- data/lib/templates/standard/standard_template.rb +26 -0
- data/lib/templates/standard/stats.html.erb +55 -0
- data/spec/base/base_template_spec.rb +177 -0
- data/spec/base/configuration_spec.rb +276 -0
- data/spec/base/generator_spec.rb +223 -0
- data/spec/base/graph_spec.rb +61 -0
- data/spec/base/line_numbers_spec.rb +62 -0
- data/spec/base/md5_tracker_spec.rb +57 -0
- data/spec/base/report_spec.rb +146 -0
- data/spec/generators/churn_spec.rb +41 -0
- data/spec/generators/flay_spec.rb +105 -0
- data/spec/generators/flog_spec.rb +70 -0
- data/spec/generators/rails_best_practices_spec.rb +52 -0
- data/spec/generators/rcov_spec.rb +180 -0
- data/spec/generators/reek_spec.rb +134 -0
- data/spec/generators/roodi_spec.rb +24 -0
- data/spec/generators/saikuro_spec.rb +74 -0
- data/spec/generators/stats_spec.rb +74 -0
- data/spec/graphs/engines/bluff_spec.rb +19 -0
- data/spec/graphs/engines/gchart_spec.rb +156 -0
- data/spec/graphs/flay_grapher_spec.rb +56 -0
- data/spec/graphs/flog_grapher_spec.rb +108 -0
- data/spec/graphs/rails_best_practices_grapher_spec.rb +61 -0
- data/spec/graphs/rcov_grapher_spec.rb +56 -0
- data/spec/graphs/reek_grapher_spec.rb +65 -0
- data/spec/graphs/roodi_grapher_spec.rb +56 -0
- data/spec/graphs/stats_grapher_spec.rb +68 -0
- data/spec/resources/line_numbers/foo.rb +33 -0
- data/spec/resources/line_numbers/module.rb +11 -0
- data/spec/resources/line_numbers/module_surrounds_class.rb +15 -0
- data/spec/resources/line_numbers/two_classes.rb +11 -0
- data/spec/resources/saikuro/app/controllers/sessions_controller.rb_cyclo.html +10 -0
- data/spec/resources/saikuro/app/controllers/users_controller.rb_cyclo.html +16 -0
- data/spec/resources/saikuro/index_cyclo.html +155 -0
- data/spec/resources/saikuro_sfiles/thing.rb_cyclo.html +11 -0
- data/spec/resources/yml/20090630.yml +7922 -0
- data/spec/resources/yml/metric_missing.yml +1 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +7 -0
- data/tasks/metric_fu.rake +22 -0
- metadata +448 -0
@@ -0,0 +1,157 @@
|
|
1
|
+
module MetricFu
|
2
|
+
module GchartGrapher
|
3
|
+
COLORS = %w{009999 FF7400 A60000 008500 E6399B 344AD7 00B860 D5CCB9}
|
4
|
+
GCHART_GRAPH_SIZE = "945x317" # maximum permitted image size is 300000 pixels
|
5
|
+
|
6
|
+
NUMBER_OF_TICKS = 6
|
7
|
+
def determine_y_axis_scale(values)
|
8
|
+
values.collect! {|val| val || 0.0 }
|
9
|
+
if values.empty?
|
10
|
+
@max_value = 10
|
11
|
+
@yaxis = [0, 2, 4, 6, 8, 10]
|
12
|
+
else
|
13
|
+
@max_value = values.max + Integer(0.1 * values.max)
|
14
|
+
portion_size = (@max_value / (NUMBER_OF_TICKS - 1).to_f).ceil
|
15
|
+
@yaxis = []
|
16
|
+
NUMBER_OF_TICKS.times {|n| @yaxis << Integer(portion_size * n) }
|
17
|
+
@max_value = @yaxis.last
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Grapher
|
23
|
+
include MetricFu::GchartGrapher
|
24
|
+
|
25
|
+
def self.require_graphing_gem
|
26
|
+
require 'gchart' if MetricFu.graph_engine == :gchart
|
27
|
+
rescue LoadError
|
28
|
+
puts "#"*99 + "\n" +
|
29
|
+
"If you want to use google charts for graphing, you'll need to install the googlecharts rubygem." +
|
30
|
+
"\n" + "#"*99
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class FlayGchartGrapher < FlayGrapher
|
35
|
+
def graph!
|
36
|
+
determine_y_axis_scale(@flay_score)
|
37
|
+
url = Gchart.line(
|
38
|
+
:size => GCHART_GRAPH_SIZE,
|
39
|
+
:title => URI.escape("Flay: duplication"),
|
40
|
+
:data => @flay_score,
|
41
|
+
:max_value => @max_value,
|
42
|
+
:axis_with_labels => 'x,y',
|
43
|
+
:axis_labels => [@labels.values, @yaxis],
|
44
|
+
:format => 'file',
|
45
|
+
:filename => File.join(MetricFu.output_directory, 'flay.png'))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class FlogGchartGrapher < FlogGrapher
|
50
|
+
def graph!
|
51
|
+
determine_y_axis_scale(@top_five_percent_average + @flog_average)
|
52
|
+
url = Gchart.line(
|
53
|
+
:size => GCHART_GRAPH_SIZE,
|
54
|
+
:title => URI.escape("Flog: code complexity"),
|
55
|
+
:data => [@flog_average, @top_five_percent_average],
|
56
|
+
:stacked => false,
|
57
|
+
:bar_colors => COLORS[0..1],
|
58
|
+
:legend => ['average', 'top 5% average'],
|
59
|
+
:custom => "chdlp=t",
|
60
|
+
:max_value => @max_value,
|
61
|
+
:axis_with_labels => 'x,y',
|
62
|
+
:axis_labels => [@labels.values, @yaxis],
|
63
|
+
:format => 'file',
|
64
|
+
:filename => File.join(MetricFu.output_directory, 'flog.png'))
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class RcovGchartGrapher < RcovGrapher
|
69
|
+
def graph!
|
70
|
+
url = Gchart.line(
|
71
|
+
:size => GCHART_GRAPH_SIZE,
|
72
|
+
:title => URI.escape("Rcov: code coverage"),
|
73
|
+
:data => self.rcov_percent,
|
74
|
+
:max_value => 101,
|
75
|
+
:axis_with_labels => 'x,y',
|
76
|
+
:axis_labels => [self.labels.values, [0,20,40,60,80,100]],
|
77
|
+
:format => 'file',
|
78
|
+
:filename => File.join(MetricFu.output_directory, 'rcov.png')
|
79
|
+
)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class ReekGchartGrapher < ReekGrapher
|
84
|
+
def graph!
|
85
|
+
determine_y_axis_scale(@reek_count.values.flatten.uniq)
|
86
|
+
values = []
|
87
|
+
legend = @reek_count.keys.sort
|
88
|
+
legend.collect {|k| values << @reek_count[k]}
|
89
|
+
|
90
|
+
url = Gchart.line(
|
91
|
+
:size => GCHART_GRAPH_SIZE,
|
92
|
+
:title => URI.escape("Reek: code smells"),
|
93
|
+
:data => values,
|
94
|
+
:stacked => false,
|
95
|
+
:bar_colors => COLORS,
|
96
|
+
:legend => legend,
|
97
|
+
:custom => "chdlp=t",
|
98
|
+
:max_value => @max_value,
|
99
|
+
:axis_with_labels => 'x,y',
|
100
|
+
:axis_labels => [@labels.values, @yaxis],
|
101
|
+
:format => 'file',
|
102
|
+
:filename => File.join(MetricFu.output_directory, 'reek.png'))
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class RoodiGchartGrapher < RoodiGrapher
|
107
|
+
def graph!
|
108
|
+
determine_y_axis_scale(@roodi_count)
|
109
|
+
url = Gchart.line(
|
110
|
+
:size => GCHART_GRAPH_SIZE,
|
111
|
+
:title => URI.escape("Roodi: potential design problems"),
|
112
|
+
:data => @roodi_count,
|
113
|
+
:max_value => @max_value,
|
114
|
+
:axis_with_labels => 'x,y',
|
115
|
+
:axis_labels => [@labels.values, @yaxis],
|
116
|
+
:format => 'file',
|
117
|
+
:filename => File.join(MetricFu.output_directory, 'roodi.png'))
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class StatsGchartGrapher < StatsGrapher
|
122
|
+
def graph!
|
123
|
+
determine_y_axis_scale(@loc_counts + @lot_counts)
|
124
|
+
url = Gchart.line(
|
125
|
+
:size => GCHART_GRAPH_SIZE,
|
126
|
+
:title => URI.escape("Stats: LOC & LOT"),
|
127
|
+
:data => [@loc_counts, @lot_counts],
|
128
|
+
:bar_colors => COLORS[0..1],
|
129
|
+
:legend => ['Lines of code', 'Lines of test'],
|
130
|
+
:custom => "chdlp=t",
|
131
|
+
:max_value => @max_value,
|
132
|
+
:axis_with_labels => 'x,y',
|
133
|
+
:axis_labels => [@labels.values, @yaxis],
|
134
|
+
:format => 'file',
|
135
|
+
:filename => File.join(MetricFu.output_directory, 'stats.png'))
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class RailsBestPracticesGchartGrapher < RailsBestPracticesGrapher
|
140
|
+
def graph!
|
141
|
+
determine_y_axis_scale(@rails_best_practices_count)
|
142
|
+
url = Gchart.line(
|
143
|
+
:size => GCHART_GRAPH_SIZE,
|
144
|
+
:title => URI.escape("Rails Best Practices: design problems"),
|
145
|
+
:data => self.rails_best_practices_count,
|
146
|
+
:bar_colors => COLORS[0..1],
|
147
|
+
:legend => ['Problems'],
|
148
|
+
:custom => "chdlp=t",
|
149
|
+
:max_value => @max_value,
|
150
|
+
:axis_with_labels => 'x,y',
|
151
|
+
:axis_labels => [@labels.values, @yaxis],
|
152
|
+
:format => 'file',
|
153
|
+
:filename => File.join(MetricFu.output_directory, 'rails_best_practices.png')
|
154
|
+
)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module MetricFu
|
2
|
+
class FlayGrapher < Grapher
|
3
|
+
attr_accessor :flay_score, :labels
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
super
|
7
|
+
@flay_score = []
|
8
|
+
@labels = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_metrics(metrics, date)
|
12
|
+
if metrics && metrics[:flay]
|
13
|
+
@flay_score.push(metrics[:flay][:total_score].to_i)
|
14
|
+
@labels.update( { @labels.size => date })
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module MetricFu
|
2
|
+
class FlogGrapher < Grapher
|
3
|
+
attr_accessor :flog_average, :labels, :top_five_percent_average
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
super
|
7
|
+
@flog_average = []
|
8
|
+
@labels = {}
|
9
|
+
@top_five_percent_average =[]
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_metrics(metrics, date)
|
13
|
+
if metrics && metrics[:flog]
|
14
|
+
@top_five_percent_average.push(calc_top_five_percent_average(metrics))
|
15
|
+
@flog_average.push(metrics[:flog][:average])
|
16
|
+
@labels.update( { @labels.size => date })
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def calc_top_five_percent_average(metrics)
|
23
|
+
return calc_top_five_percent_average_legacy(metrics) if metrics[:flog][:pages]
|
24
|
+
|
25
|
+
method_scores = metrics[:flog][:method_containers].inject([]) do |method_scores, container|
|
26
|
+
method_scores += container[:methods].values.map {|v| v[:score]}
|
27
|
+
end
|
28
|
+
method_scores.sort!.reverse!
|
29
|
+
|
30
|
+
number_of_methods_that_is_five_percent = (method_scores.size * 0.05).ceil
|
31
|
+
|
32
|
+
total_for_five_percent =
|
33
|
+
method_scores[0...number_of_methods_that_is_five_percent].inject(0) { |total, score| total += score }
|
34
|
+
if number_of_methods_that_is_five_percent == 0
|
35
|
+
0.0
|
36
|
+
else
|
37
|
+
total_for_five_percent / number_of_methods_that_is_five_percent.to_f
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def calc_top_five_percent_average_legacy(metrics)
|
42
|
+
methods = metrics[:flog][:pages].inject([]) {|methods, page| methods << page[:scanned_methods]}
|
43
|
+
methods.flatten!
|
44
|
+
methods = methods.sort_by {|method| method[:score]}.reverse
|
45
|
+
|
46
|
+
number_of_methods_that_is_five_percent = (methods.size * 0.05).ceil
|
47
|
+
|
48
|
+
total_for_five_percent =
|
49
|
+
methods[0...number_of_methods_that_is_five_percent].inject(0) {|total, method| total += method[:score] }
|
50
|
+
if number_of_methods_that_is_five_percent == 0
|
51
|
+
0.0
|
52
|
+
else
|
53
|
+
total_for_five_percent / number_of_methods_that_is_five_percent.to_f
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module MetricFu
|
2
|
+
class RailsBestPracticesGrapher < Grapher
|
3
|
+
attr_accessor :rails_best_practices_count, :labels
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
super
|
7
|
+
@rails_best_practices_count = []
|
8
|
+
@labels = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_metrics(metrics, date)
|
12
|
+
if metrics && metrics[:rails_best_practices]
|
13
|
+
size = (metrics[:rails_best_practices][:problems] || []).size
|
14
|
+
@rails_best_practices_count.push(size)
|
15
|
+
@labels.update( { @labels.size => date })
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module MetricFu
|
2
|
+
class RcovGrapher < Grapher
|
3
|
+
attr_accessor :rcov_percent, :labels
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
super
|
7
|
+
self.rcov_percent = []
|
8
|
+
self.labels = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_metrics(metrics, date)
|
12
|
+
if metrics && metrics[:rcov]
|
13
|
+
self.rcov_percent.push(metrics[:rcov][:global_percent_run])
|
14
|
+
self.labels.update( { self.labels.size => date })
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module MetricFu
|
2
|
+
class ReekGrapher < Grapher
|
3
|
+
attr_accessor :reek_count, :labels
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
super
|
7
|
+
@reek_count = {}
|
8
|
+
@labels = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_metrics(metrics, date)
|
12
|
+
if metrics && metrics[:reek]
|
13
|
+
counter = @labels.size
|
14
|
+
@labels.update( { @labels.size => date })
|
15
|
+
|
16
|
+
metrics[:reek][:matches].each do |reek_chunk|
|
17
|
+
reek_chunk[:code_smells].each do |code_smell|
|
18
|
+
# speaking of code smell...
|
19
|
+
@reek_count[code_smell[:type]] = [] if @reek_count[code_smell[:type]].nil?
|
20
|
+
if @reek_count[code_smell[:type]][counter].nil?
|
21
|
+
@reek_count[code_smell[:type]][counter] = 1
|
22
|
+
else
|
23
|
+
@reek_count[code_smell[:type]][counter] += 1
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module MetricFu
|
2
|
+
class RoodiGrapher < Grapher
|
3
|
+
attr_accessor :roodi_count, :labels
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
super
|
7
|
+
@roodi_count = []
|
8
|
+
@labels = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_metrics(metrics, date)
|
12
|
+
if metrics && metrics[:roodi]
|
13
|
+
@roodi_count.push(metrics[:roodi][:problems].size)
|
14
|
+
@labels.update( { @labels.size => date })
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module MetricFu
|
2
|
+
class StatsGrapher < Grapher
|
3
|
+
attr_accessor :loc_counts, :lot_counts, :labels
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
super
|
7
|
+
self.loc_counts = []
|
8
|
+
self.lot_counts = []
|
9
|
+
self.labels = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_metrics(metrics, date)
|
13
|
+
if metrics && metrics[:stats]
|
14
|
+
self.loc_counts.push(metrics[:stats][:codeLOC].to_i)
|
15
|
+
self.lot_counts.push(metrics[:stats][:testLOC].to_i)
|
16
|
+
self.labels.update( { self.labels.size => date })
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/metric_fu.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'yaml'
|
3
|
+
begin
|
4
|
+
require 'active_support/core_ext/object/to_json'
|
5
|
+
require 'active_support/core_ext/object/blank'
|
6
|
+
require 'active_support/inflector'
|
7
|
+
rescue LoadError
|
8
|
+
require 'activesupport'
|
9
|
+
end
|
10
|
+
|
11
|
+
# Load a few things to make our lives easier elsewhere.
|
12
|
+
module MetricFu
|
13
|
+
LIB_ROOT = File.dirname(__FILE__)
|
14
|
+
end
|
15
|
+
base_dir = File.join(MetricFu::LIB_ROOT, 'base')
|
16
|
+
generator_dir = File.join(MetricFu::LIB_ROOT, 'generators')
|
17
|
+
template_dir = File.join(MetricFu::LIB_ROOT, 'templates')
|
18
|
+
graph_dir = File.join(MetricFu::LIB_ROOT, 'graphs')
|
19
|
+
|
20
|
+
# We need to require these two things first because our other classes
|
21
|
+
# depend on them.
|
22
|
+
require File.join(base_dir, 'report')
|
23
|
+
require File.join(base_dir, 'generator')
|
24
|
+
require File.join(base_dir, 'graph')
|
25
|
+
require File.join(base_dir, 'scoring_strategies')
|
26
|
+
|
27
|
+
# prevent the task from being run multiple times.
|
28
|
+
unless Rake::Task.task_defined? "metrics:all"
|
29
|
+
# Load the rakefile so users of the gem get the default metric_fu task
|
30
|
+
load File.join(MetricFu::LIB_ROOT, '..', 'tasks', 'metric_fu.rake')
|
31
|
+
end
|
32
|
+
|
33
|
+
# Now load everything else that's in the directory
|
34
|
+
Dir[File.join(base_dir, '*.rb')].each{|l| require l }
|
35
|
+
Dir[File.join(generator_dir, '*.rb')].each {|l| require l }
|
36
|
+
Dir[File.join(template_dir, 'standard/*.rb')].each {|l| require l}
|
37
|
+
Dir[File.join(template_dir, 'awesome/*.rb')].each {|l| require l}
|
38
|
+
require graph_dir + "/grapher"
|
39
|
+
Dir[File.join(graph_dir, '*.rb')].each {|l| require l}
|
40
|
+
Dir[File.join(graph_dir, 'engines', '*.rb')].each {|l| require l}
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'syntax/convertors/html'
|
3
|
+
|
4
|
+
class AwesomeTemplate < MetricFu::Template
|
5
|
+
|
6
|
+
def write
|
7
|
+
# Getting rid of the crap before and after the project name from integrity
|
8
|
+
@name = File.basename(Dir.pwd).gsub(/^\w+-|-\w+$/, "")
|
9
|
+
|
10
|
+
# Copy Bluff javascripts to output directory
|
11
|
+
Dir[File.join(this_directory, '..', 'javascripts', '*')].each do |f|
|
12
|
+
FileUtils.copy(f, File.join(MetricFu.output_directory, File.basename(f)))
|
13
|
+
end
|
14
|
+
|
15
|
+
report.each_pair do |section, contents|
|
16
|
+
if template_exists?(section)
|
17
|
+
create_instance_var(section, contents)
|
18
|
+
create_instance_var(:per_file_data, per_file_data)
|
19
|
+
@html = erbify(section)
|
20
|
+
html = erbify('layout')
|
21
|
+
fn = output_filename(section)
|
22
|
+
MetricFu.report.save_output(html, MetricFu.output_directory, fn)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Instance variables we need should already be created from above
|
27
|
+
if template_exists?('index')
|
28
|
+
@html = erbify('index')
|
29
|
+
html = erbify('layout')
|
30
|
+
fn = output_filename('index')
|
31
|
+
MetricFu.report.save_output(html, MetricFu.output_directory, fn)
|
32
|
+
end
|
33
|
+
|
34
|
+
write_file_data
|
35
|
+
end
|
36
|
+
|
37
|
+
def write_file_data
|
38
|
+
convertor = Syntax::Convertors::HTML.for_syntax('ruby')
|
39
|
+
|
40
|
+
per_file_data.each_pair do |file, lines|
|
41
|
+
data = File.open(file, 'r').readlines
|
42
|
+
fn = "#{file.gsub(%r{/}, '_')}.html"
|
43
|
+
|
44
|
+
out = "<html><head><style>#{inline_css('css/syntax.css')}</style></head><body>"
|
45
|
+
out << "<table cellpadding='0' cellspacing='0' class='ruby'>"
|
46
|
+
data.each_with_index do |line, idx|
|
47
|
+
out << "<tr><td valign='top'><small>#{idx + 1}</small></td>"
|
48
|
+
out << "<td valign='top'>"
|
49
|
+
if lines.has_key?((idx + 1).to_s)
|
50
|
+
out << "<ul>"
|
51
|
+
lines[(idx + 1).to_s].each do |problem|
|
52
|
+
out << "<li>#{problem[:description]} » #{problem[:type]}</li>"
|
53
|
+
end
|
54
|
+
out << "</ul>"
|
55
|
+
else
|
56
|
+
out << " "
|
57
|
+
end
|
58
|
+
out << "</td>"
|
59
|
+
line_for_display = MetricFu.configuration.syntax_highlighting ? convertor.convert(line) : line
|
60
|
+
out << "<td valign='top'><a name='line#{idx + 1}'>#{line_for_display}</a></td>"
|
61
|
+
out << "</tr>"
|
62
|
+
end
|
63
|
+
out << "<table></body></html>"
|
64
|
+
|
65
|
+
MetricFu.report.save_output(out, MetricFu.output_directory, fn)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def this_directory
|
70
|
+
File.dirname(__FILE__)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
@@ -0,0 +1,58 @@
|
|
1
|
+
<h3>Source Control Churn Results</h3>
|
2
|
+
<p>Files that change a lot in your project may be bad a sign.
|
3
|
+
This task uses your source control log to identify those files.
|
4
|
+
</p>
|
5
|
+
<table>
|
6
|
+
<tr>
|
7
|
+
<th>File Path</th>
|
8
|
+
<th>Times Changed</th>
|
9
|
+
</tr>
|
10
|
+
<% count = 0 %>
|
11
|
+
<% @churn[:changes].each do |change| %>
|
12
|
+
<tr>
|
13
|
+
<td><%= link_to_filename(change[:file_path]) %></td>
|
14
|
+
<td><%= change[:times_changed] %></td>
|
15
|
+
</tr>
|
16
|
+
<% count += 1 %>
|
17
|
+
<% end %>
|
18
|
+
</table>
|
19
|
+
|
20
|
+
<% if @churn[:class_churn] %>
|
21
|
+
<p>Classes that change a lot in your project may be bad a sign.</p>
|
22
|
+
<table>
|
23
|
+
<tr>
|
24
|
+
<th>File Path</th>
|
25
|
+
<th>Times Changed</th>
|
26
|
+
</tr>
|
27
|
+
<% count = 0 %>
|
28
|
+
<% @churn[:class_churn].each do |change| %>
|
29
|
+
<tr>
|
30
|
+
<td><%= link_to_filename(change['klass']['file']) %> <%= change['klass']['klass'] %></td>
|
31
|
+
<td><%= change['times_changed'] %></td>
|
32
|
+
</tr>
|
33
|
+
<% count += 1 %>
|
34
|
+
<% end %>
|
35
|
+
</table>
|
36
|
+
<% end %>
|
37
|
+
|
38
|
+
<% if @churn[:method_churn] %>
|
39
|
+
<p>Methods that change a lot in your project may be bad a sign.</p>
|
40
|
+
<table>
|
41
|
+
<tr>
|
42
|
+
<th>File Path</th>
|
43
|
+
<th>Times Changed</th>
|
44
|
+
</tr>
|
45
|
+
<% count = 0 %>
|
46
|
+
<% @churn[:method_churn].each do |change| %>
|
47
|
+
<tr>
|
48
|
+
<td><%= link_to_filename(change['method']['file']) %> <%= change['method']['method'] %></td>
|
49
|
+
<td><%= change['times_changed'] %></td>
|
50
|
+
</tr>
|
51
|
+
<% count += 1 %>
|
52
|
+
<% end %>
|
53
|
+
</table>
|
54
|
+
<% end %>
|
55
|
+
|
56
|
+
|
57
|
+
|
58
|
+
<p>Generated on <%= Time.now.localtime %></p>
|
@@ -0,0 +1,82 @@
|
|
1
|
+
/* --------------------------------------------------------------
|
2
|
+
|
3
|
+
buttons.css
|
4
|
+
* Gives you some great CSS-only buttons.
|
5
|
+
|
6
|
+
Created by Kevin Hale [particletree.com]
|
7
|
+
* particletree.com/features/rediscovering-the-button-element
|
8
|
+
|
9
|
+
See Readme.txt in this folder for instructions.
|
10
|
+
|
11
|
+
-------------------------------------------------------------- */
|
12
|
+
|
13
|
+
button {
|
14
|
+
display:block;
|
15
|
+
float:left;
|
16
|
+
margin:0 0.583em 0.667em 0;
|
17
|
+
padding:5px 10px 5px 7px; /* Links */
|
18
|
+
|
19
|
+
border:1px solid #dedede;
|
20
|
+
border-top:1px solid #eee;
|
21
|
+
border-left:1px solid #eee;
|
22
|
+
|
23
|
+
background-color:#f5f5f5;
|
24
|
+
font-family:"Lucida Grande", Tahoma, Arial, Verdana, sans-serif;
|
25
|
+
font-size:100%;
|
26
|
+
line-height:130%;
|
27
|
+
text-decoration:none;
|
28
|
+
font-weight:bold;
|
29
|
+
color:#565656;
|
30
|
+
cursor:pointer;
|
31
|
+
}
|
32
|
+
button {
|
33
|
+
width:auto;
|
34
|
+
overflow:visible;
|
35
|
+
padding:4px 10px 3px 7px; /* IE6 */
|
36
|
+
}
|
37
|
+
button[type] {
|
38
|
+
padding:4px 10px 4px 7px; /* Firefox */
|
39
|
+
line-height:17px; /* Safari */
|
40
|
+
}
|
41
|
+
*:first-child+html button[type] {
|
42
|
+
padding:4px 10px 3px 7px; /* IE7 */
|
43
|
+
}
|
44
|
+
button img {
|
45
|
+
margin:0 3px -3px 0 !important;
|
46
|
+
padding:0;
|
47
|
+
border:none;
|
48
|
+
width:16px;
|
49
|
+
height:16px;
|
50
|
+
float:none;
|
51
|
+
}
|
52
|
+
|
53
|
+
|
54
|
+
/* Button colors
|
55
|
+
-------------------------------------------------------------- */
|
56
|
+
|
57
|
+
/* Standard */
|
58
|
+
button:hover {
|
59
|
+
background-color:#dff4ff;
|
60
|
+
border:1px solid #c2e1ef;
|
61
|
+
color:#336699;
|
62
|
+
}
|
63
|
+
|
64
|
+
/* Positive */
|
65
|
+
body .positive {
|
66
|
+
color:#529214;
|
67
|
+
}
|
68
|
+
button.positive:hover {
|
69
|
+
background-color:#E6EFC2;
|
70
|
+
border:1px solid #C6D880;
|
71
|
+
color:#529214;
|
72
|
+
}
|
73
|
+
|
74
|
+
/* Negative */
|
75
|
+
body .negative {
|
76
|
+
color:#d12f19;
|
77
|
+
}
|
78
|
+
button.negative:hover {
|
79
|
+
background:#fbe3e4;
|
80
|
+
border:1px solid #fbc2c4;
|
81
|
+
color:#d12f19;
|
82
|
+
}
|