flyerhzm-metric_fu 1.0.0

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.
Files changed (92) hide show
  1. data/HISTORY +164 -0
  2. data/MIT-LICENSE +22 -0
  3. data/Manifest.txt +25 -0
  4. data/README.textile +27 -0
  5. data/Rakefile +18 -0
  6. data/TODO +9 -0
  7. data/lib/base/base_template.rb +145 -0
  8. data/lib/base/configuration.rb +180 -0
  9. data/lib/base/generator.rb +167 -0
  10. data/lib/base/graph.rb +39 -0
  11. data/lib/base/md5_tracker.rb +52 -0
  12. data/lib/base/report.rb +100 -0
  13. data/lib/generators/churn.rb +90 -0
  14. data/lib/generators/flay.rb +34 -0
  15. data/lib/generators/flog.rb +167 -0
  16. data/lib/generators/rails_best_practices.rb +31 -0
  17. data/lib/generators/rcov.rb +87 -0
  18. data/lib/generators/reek.rb +64 -0
  19. data/lib/generators/roodi.rb +32 -0
  20. data/lib/generators/saikuro.rb +208 -0
  21. data/lib/generators/stats.rb +43 -0
  22. data/lib/graphs/engines/bluff.rb +98 -0
  23. data/lib/graphs/engines/gchart.rb +119 -0
  24. data/lib/graphs/flay_grapher.rb +20 -0
  25. data/lib/graphs/flog_grapher.rb +40 -0
  26. data/lib/graphs/grapher.rb +11 -0
  27. data/lib/graphs/rails_best_practices_grapher.rb +20 -0
  28. data/lib/graphs/rcov_grapher.rb +20 -0
  29. data/lib/graphs/reek_grapher.rb +32 -0
  30. data/lib/graphs/roodi_grapher.rb +20 -0
  31. data/lib/metric_fu.rb +31 -0
  32. data/lib/templates/awesome/awesome_template.rb +37 -0
  33. data/lib/templates/awesome/churn.html.erb +19 -0
  34. data/lib/templates/awesome/css/buttons.css +82 -0
  35. data/lib/templates/awesome/css/default.css +75 -0
  36. data/lib/templates/awesome/css/integrity.css +335 -0
  37. data/lib/templates/awesome/css/reset.css +7 -0
  38. data/lib/templates/awesome/flay.html.erb +33 -0
  39. data/lib/templates/awesome/flog.html.erb +53 -0
  40. data/lib/templates/awesome/index.html.erb +31 -0
  41. data/lib/templates/awesome/layout.html.erb +30 -0
  42. data/lib/templates/awesome/rails_best_practices.html.erb +27 -0
  43. data/lib/templates/awesome/rcov.html.erb +42 -0
  44. data/lib/templates/awesome/reek.html.erb +40 -0
  45. data/lib/templates/awesome/roodi.html.erb +27 -0
  46. data/lib/templates/awesome/saikuro.html.erb +71 -0
  47. data/lib/templates/awesome/stats.html.erb +41 -0
  48. data/lib/templates/javascripts/bluff-min.js +1 -0
  49. data/lib/templates/javascripts/excanvas.js +19 -0
  50. data/lib/templates/javascripts/js-class.js +1 -0
  51. data/lib/templates/standard/churn.html.erb +31 -0
  52. data/lib/templates/standard/default.css +64 -0
  53. data/lib/templates/standard/flay.html.erb +34 -0
  54. data/lib/templates/standard/flog.html.erb +53 -0
  55. data/lib/templates/standard/index.html.erb +41 -0
  56. data/lib/templates/standard/rails_best_practices.html.erb +29 -0
  57. data/lib/templates/standard/rcov.html.erb +43 -0
  58. data/lib/templates/standard/reek.html.erb +42 -0
  59. data/lib/templates/standard/roodi.html.erb +29 -0
  60. data/lib/templates/standard/saikuro.html.erb +84 -0
  61. data/lib/templates/standard/standard_template.rb +26 -0
  62. data/lib/templates/standard/stats.html.erb +55 -0
  63. data/spec/base/base_template_spec.rb +161 -0
  64. data/spec/base/configuration_spec.rb +269 -0
  65. data/spec/base/generator_spec.rb +244 -0
  66. data/spec/base/graph_spec.rb +24 -0
  67. data/spec/base/md5_tracker_spec.rb +57 -0
  68. data/spec/base/report_spec.rb +139 -0
  69. data/spec/generators/churn_spec.rb +152 -0
  70. data/spec/generators/flay_spec.rb +104 -0
  71. data/spec/generators/flog_spec.rb +238 -0
  72. data/spec/generators/reek_spec.rb +126 -0
  73. data/spec/generators/saikuro_spec.rb +58 -0
  74. data/spec/generators/stats_spec.rb +74 -0
  75. data/spec/graphs/engines/bluff_spec.rb +15 -0
  76. data/spec/graphs/engines/gchart_spec.rb +15 -0
  77. data/spec/graphs/flay_grapher_spec.rb +37 -0
  78. data/spec/graphs/flog_grapher_spec.rb +45 -0
  79. data/spec/graphs/rcov_grapher_spec.rb +37 -0
  80. data/spec/graphs/reek_grapher_spec.rb +46 -0
  81. data/spec/graphs/roodi_grapher_spec.rb +37 -0
  82. data/spec/resources/saikuro/app/controllers/sessions_controller.rb_cyclo.html +10 -0
  83. data/spec/resources/saikuro/app/controllers/users_controller.rb_cyclo.html +16 -0
  84. data/spec/resources/saikuro/index_cyclo.html +155 -0
  85. data/spec/resources/saikuro_sfiles/thing.rb_cyclo.html +11 -0
  86. data/spec/resources/yml/20090630.yml +7844 -0
  87. data/spec/spec.opts +8 -0
  88. data/spec/spec_helper.rb +7 -0
  89. data/tasks/metric_fu.rake +22 -0
  90. data/vendor/_fonts/monaco.ttf +0 -0
  91. data/vendor/saikuro/saikuro.rb +1219 -0
  92. metadata +234 -0
@@ -0,0 +1,43 @@
1
+ module MetricFu
2
+
3
+ class Stats < Generator
4
+
5
+ def emit
6
+ `rake stats > #{metric_directory + '/stats.txt'}`
7
+ end
8
+
9
+ def analyze
10
+ output = File.open(metric_directory + '/stats.txt').read
11
+ output = output.split("\n")
12
+ output = output.find_all {|line| line[0].chr != "+" }
13
+ output = output.find_all {|line| line[0].chr != "(" }
14
+ output.shift
15
+ totals = output.pop
16
+ totals = totals.split(" ").find_all {|el| ! el.empty? }
17
+ @stats = {}
18
+ @stats[:codeLOC] = totals[0].match(/\d.*/)[0].to_i
19
+ @stats[:testLOC] = totals[1].match(/\d.*/)[0].to_i
20
+ @stats[:code_to_test_ratio] = totals[2].match(/1\:(\d.*)/)[1].to_f
21
+
22
+ @stats[:lines] = output.map do |line|
23
+ elements = line.split("|")
24
+ elements.map! {|el| el.strip }
25
+ elements = elements.find_all {|el| ! el.empty? }
26
+ info_line = {}
27
+ info_line[:name] = elements.shift
28
+ elements.map! {|el| el.to_i }
29
+ [:lines, :loc, :classes, :methods,
30
+ :methods_per_class, :loc_per_method].each do |sym|
31
+ info_line[sym] = elements.shift
32
+ end
33
+ info_line
34
+ end
35
+ @stats
36
+ end
37
+
38
+ def to_h
39
+ {:stats => @stats}
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,98 @@
1
+ require 'activesupport'
2
+
3
+ module MetricFu
4
+ class Grapher
5
+ BLUFF_GRAPH_SIZE = "1000x600"
6
+ BLUFF_DEFAULT_OPTIONS = <<-EOS
7
+ var g = new Bluff.Line('graph', "#{BLUFF_GRAPH_SIZE}");
8
+ g.theme_37signals();
9
+ g.title_font_size = "24px"
10
+ g.legend_font_size = "12px"
11
+ g.marker_font_size = "10px"
12
+ EOS
13
+ end
14
+
15
+ class FlayBluffGrapher < FlayGrapher
16
+ def graph!
17
+ content = <<-EOS
18
+ #{BLUFF_DEFAULT_OPTIONS}
19
+ g.title = 'Flay: duplication';
20
+ g.data('flay', [#{@flay_score.join(',')}]);
21
+ g.labels = #{@labels.to_json};
22
+ g.draw();
23
+ EOS
24
+ File.open(File.join(MetricFu.output_directory, 'flay.js'), 'w') {|f| f << content }
25
+ end
26
+ end
27
+
28
+ class FlogBluffGrapher < FlogGrapher
29
+ def graph!
30
+ content = <<-EOS
31
+ #{BLUFF_DEFAULT_OPTIONS}
32
+ g.title = 'Flog: code complexity';
33
+ g.data('average', [#{@flog_average.join(',')}]);
34
+ g.data('top 5% average', [#{@top_five_percent_average.join(',')}])
35
+ g.labels = #{@labels.to_json};
36
+ g.draw();
37
+ EOS
38
+ File.open(File.join(MetricFu.output_directory, 'flog.js'), 'w') {|f| f << content }
39
+ end
40
+ end
41
+
42
+ class RcovBluffGrapher < RcovGrapher
43
+ def graph!
44
+ content = <<-EOS
45
+ #{BLUFF_DEFAULT_OPTIONS}
46
+ g.title = 'Rcov: code coverage';
47
+ g.data('rcov', [#{@rcov_percent.join(',')}]);
48
+ g.labels = #{@labels.to_json};
49
+ g.draw();
50
+ EOS
51
+ File.open(File.join(MetricFu.output_directory, 'rcov.js'), 'w') {|f| f << content }
52
+ end
53
+ end
54
+
55
+ class ReekBluffGrapher < ReekGrapher
56
+ def graph!
57
+ legend = @reek_count.keys.sort
58
+ data = ""
59
+ legend.each do |name|
60
+ data += "g.data('#{name}', [#{@reek_count[name].join(',')}])\n"
61
+ end
62
+ content = <<-EOS
63
+ #{BLUFF_DEFAULT_OPTIONS}
64
+ g.title = 'Reek: code smells';
65
+ #{data}
66
+ g.labels = #{@labels.to_json};
67
+ g.draw();
68
+ EOS
69
+ File.open(File.join(MetricFu.output_directory, 'reek.js'), 'w') {|f| f << content }
70
+ end
71
+ end
72
+
73
+ class RoodiBluffGrapher < RoodiGrapher
74
+ def graph!
75
+ content = <<-EOS
76
+ #{BLUFF_DEFAULT_OPTIONS}
77
+ g.title = 'Roodi: design problems';
78
+ g.data('roodi', [#{@roodi_count.join(',')}]);
79
+ g.labels = #{@labels.to_json};
80
+ g.draw();
81
+ EOS
82
+ File.open(File.join(MetricFu.output_directory, 'roodi.js'), 'w') {|f| f << content }
83
+ end
84
+ end
85
+
86
+ class RailsBestPracticesBluffGrapher < RailsBestPracticesGrapher
87
+ def graph!
88
+ content = <<-EOS
89
+ #{BLUFF_DEFAULT_OPTIONS}
90
+ g.title = 'Rails Best Practices: design problems';
91
+ g.data('rails_best_practices', [#{@rails_best_practices_count.join(',')}]);
92
+ g.labels = #{@labels.to_json};
93
+ g.draw();
94
+ EOS
95
+ File.open(File.join(MetricFu.output_directory, 'rails_best_practices.js'), 'w') {|f| f << content }
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,119 @@
1
+ module MetricFu
2
+ module GchartGrapher
3
+ COLORS = %w{009999 FF7400 A60000 008500 E6399B 344AD7 00B860 D5CCB9}
4
+ GCHART_GRAPH_SIZE = "1000x300" # 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'
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%25 average'],
59
+ :max_value => @max_value,
60
+ :axis_with_labels => 'x,y',
61
+ :axis_labels => [@labels.values, @yaxis],
62
+ :format => 'file',
63
+ :filename => File.join(MetricFu.output_directory, 'flog.png'))
64
+ end
65
+ end
66
+
67
+ class RcovGchartGrapher < RcovGrapher
68
+ def graph!
69
+ determine_y_axis_scale(self.rcov_percent)
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
+ :max_value => @max_value,
98
+ :axis_with_labels => 'x,y',
99
+ :axis_labels => [@labels.values, @yaxis],
100
+ :format => 'file',
101
+ :filename => File.join(MetricFu.output_directory, 'reek.png'))
102
+ end
103
+ end
104
+
105
+ class RoodiGchartGrapher < RoodiGrapher
106
+ def graph!
107
+ determine_y_axis_scale(@roodi_count)
108
+ url = Gchart.line(
109
+ :size => GCHART_GRAPH_SIZE,
110
+ :title => URI.escape("Roodi: potential design problems"),
111
+ :data => @roodi_count,
112
+ :max_value => @max_value,
113
+ :axis_with_labels => 'x,y',
114
+ :axis_labels => [@labels.values, @yaxis],
115
+ :format => 'file',
116
+ :filename => File.join(MetricFu.output_directory, 'roodi.png'))
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,20 @@
1
+ module MetricFu
2
+
3
+ class FlayGrapher < Grapher
4
+
5
+ attr_accessor :flay_score, :labels
6
+
7
+ def initialize
8
+ super
9
+ @flay_score = []
10
+ @labels = {}
11
+ end
12
+
13
+ def get_metrics(metrics, date)
14
+ @flay_score.push(metrics[:flay][:total_score].to_i)
15
+ @labels.update( { @labels.size => date })
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,40 @@
1
+ module MetricFu
2
+
3
+ class FlogGrapher < Grapher
4
+
5
+ attr_accessor :flog_average, :labels, :top_five_percent_average
6
+
7
+ def initialize
8
+ super
9
+ @flog_average = []
10
+ @labels = {}
11
+ @top_five_percent_average =[]
12
+ end
13
+
14
+ def get_metrics(metrics, date)
15
+ @top_five_percent_average.push(calc_top_five_percent_average(metrics))
16
+ @flog_average.push(metrics[:flog][:average])
17
+ @labels.update( { @labels.size => date })
18
+ end
19
+
20
+ private
21
+
22
+ def calc_top_five_percent_average(metrics)
23
+ methods = metrics[:flog][:pages].inject([]) {|methods, page| methods << page[:scanned_methods]}
24
+ methods.flatten!
25
+ methods = methods.sort_by {|method| method[:score]}.reverse
26
+
27
+ number_of_methods_that_is_five_percent = (methods.size * 0.05).ceil
28
+
29
+ total_for_five_percent =
30
+ methods[0...number_of_methods_that_is_five_percent].inject(0) {|total, method| total += method[:score] }
31
+ if number_of_methods_that_is_five_percent == 0
32
+ 0.0
33
+ else
34
+ total_for_five_percent / number_of_methods_that_is_five_percent.to_f
35
+ end
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,11 @@
1
+ module MetricFu
2
+ class Grapher
3
+ def initialize
4
+ self.class.require_graphing_gem
5
+ end
6
+
7
+ def self.require_graphing_gem
8
+ # to be overridden by charting engines
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ module MetricFu
2
+
3
+ class RailsBestPracticesGrapher < Grapher
4
+
5
+ attr_accessor :rails_best_practices_count, :labels
6
+
7
+ def initialize
8
+ super
9
+ @rails_best_practices_count = []
10
+ @labels = {}
11
+ end
12
+
13
+ def get_metrics(metrics, date)
14
+ @rails_best_practices_count.push(metrics[:rails_best_practices][:problems].size)
15
+ @labels.update( { @labels.size => date })
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,20 @@
1
+ module MetricFu
2
+
3
+ class RcovGrapher < Grapher
4
+
5
+ attr_accessor :rcov_percent, :labels
6
+
7
+ def initialize
8
+ super
9
+ self.rcov_percent = []
10
+ self.labels = {}
11
+ end
12
+
13
+ def get_metrics(metrics, date)
14
+ self.rcov_percent.push(metrics[:rcov][:global_percent_run])
15
+ self.labels.update( { self.labels.size => date })
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,32 @@
1
+ module MetricFu
2
+
3
+ class ReekGrapher < Grapher
4
+
5
+ attr_accessor :reek_count, :labels
6
+
7
+ def initialize
8
+ super
9
+ @reek_count = {}
10
+ @labels = {}
11
+ end
12
+
13
+ def get_metrics(metrics, date)
14
+ counter = @labels.size
15
+ @labels.update( { @labels.size => date })
16
+
17
+ metrics[:reek][:matches].each do |reek_chunk|
18
+ reek_chunk[:code_smells].each do |code_smell|
19
+ # speaking of code smell...
20
+ @reek_count[code_smell[:type]] = [] if @reek_count[code_smell[:type]].nil?
21
+ if @reek_count[code_smell[:type]][counter].nil?
22
+ @reek_count[code_smell[:type]][counter] = 1
23
+ else
24
+ @reek_count[code_smell[:type]][counter] += 1
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,20 @@
1
+ module MetricFu
2
+
3
+ class RoodiGrapher < Grapher
4
+
5
+ attr_accessor :roodi_count, :labels
6
+
7
+ def initialize
8
+ super
9
+ @roodi_count = []
10
+ @labels = {}
11
+ end
12
+
13
+ def get_metrics(metrics, date)
14
+ @roodi_count.push(metrics[:roodi][:problems].size)
15
+ @labels.update( { @labels.size => date })
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,31 @@
1
+ require 'rake'
2
+ require 'yaml'
3
+ # Load a few things to make our lives easier elsewhere.
4
+ module MetricFu
5
+ LIB_ROOT = File.dirname(__FILE__)
6
+ end
7
+ base_dir = File.join(MetricFu::LIB_ROOT, 'base')
8
+ generator_dir = File.join(MetricFu::LIB_ROOT, 'generators')
9
+ template_dir = File.join(MetricFu::LIB_ROOT, 'templates')
10
+ graph_dir = File.join(MetricFu::LIB_ROOT, 'graphs')
11
+
12
+ # We need to require these two things first because our other classes
13
+ # depend on them.
14
+ require File.join(base_dir, 'report')
15
+ require File.join(base_dir, 'generator')
16
+ require File.join(base_dir, 'graph')
17
+
18
+ # prevent the task from being run multiple times.
19
+ unless Rake::Task.task_defined? "metrics:all"
20
+ # Load the rakefile so users of the gem get the default metric_fu task
21
+ load File.join(MetricFu::LIB_ROOT, '..', 'tasks', 'metric_fu.rake')
22
+ end
23
+
24
+ # Now load everything else that's in the directory
25
+ Dir[File.join(base_dir, '*.rb')].each{|l| require l }
26
+ Dir[File.join(generator_dir, '*.rb')].each {|l| require l }
27
+ Dir[File.join(template_dir, 'standard/*.rb')].each {|l| require l}
28
+ Dir[File.join(template_dir, 'awesome/*.rb')].each {|l| require l}
29
+ require graph_dir + "/grapher"
30
+ Dir[File.join(graph_dir, '*.rb')].each {|l| require l}
31
+ Dir[File.join(graph_dir, 'engines', '*.rb')].each {|l| require l}