metric_fu 2.0.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. data/HISTORY +13 -5
  2. data/README +2 -2
  3. data/Rakefile +1 -1
  4. data/TODO +3 -1
  5. data/lib/base/base_template.rb +16 -16
  6. data/lib/base/churn_analyzer.rb +3 -3
  7. data/lib/base/code_issue.rb +8 -8
  8. data/lib/base/configuration.rb +24 -22
  9. data/lib/base/flay_analyzer.rb +2 -2
  10. data/lib/base/flog_analyzer.rb +4 -4
  11. data/lib/base/generator.rb +21 -21
  12. data/lib/base/graph.rb +15 -10
  13. data/lib/base/line_numbers.rb +56 -55
  14. data/lib/base/location.rb +68 -66
  15. data/lib/base/metric_analyzer.rb +21 -21
  16. data/lib/base/ranking.rb +23 -22
  17. data/lib/base/rcov_analyzer.rb +3 -3
  18. data/lib/base/reek_analyzer.rb +12 -10
  19. data/lib/base/report.rb +9 -9
  20. data/lib/base/roodi_analyzer.rb +2 -2
  21. data/lib/base/saikuro_analyzer.rb +4 -4
  22. data/lib/base/scoring_strategies.rb +5 -5
  23. data/lib/base/stats_analyzer.rb +2 -2
  24. data/lib/base/table.rb +4 -4
  25. data/lib/generators/churn.rb +1 -1
  26. data/lib/generators/flay.rb +1 -1
  27. data/lib/generators/hotspots.rb +4 -4
  28. data/lib/generators/rails_best_practices.rb +1 -1
  29. data/lib/generators/rcov.rb +17 -17
  30. data/lib/generators/reek.rb +2 -2
  31. data/lib/generators/saikuro.rb +42 -42
  32. data/lib/generators/stats.rb +6 -6
  33. data/lib/graphs/engines/bluff.rb +1 -1
  34. data/lib/graphs/engines/gchart.rb +7 -7
  35. data/lib/graphs/flog_grapher.rb +3 -3
  36. data/lib/graphs/grapher.rb +1 -1
  37. data/lib/metric_fu.rb +2 -2
  38. data/lib/templates/awesome/churn.html.erb +1 -1
  39. data/lib/templates/awesome/css/integrity.css +0 -1
  40. data/lib/templates/awesome/flay.html.erb +2 -2
  41. data/lib/templates/awesome/flog.html.erb +2 -2
  42. data/lib/templates/awesome/hotspots.html.erb +4 -4
  43. data/lib/templates/awesome/index.html.erb +2 -2
  44. data/lib/templates/awesome/rails_best_practices.html.erb +1 -1
  45. data/lib/templates/awesome/rcov.html.erb +1 -1
  46. data/lib/templates/awesome/roodi.html.erb +1 -1
  47. data/lib/templates/awesome/saikuro.html.erb +3 -3
  48. data/lib/templates/awesome/stats.html.erb +1 -1
  49. data/lib/templates/standard/churn.html.erb +2 -2
  50. data/lib/templates/standard/default.css +4 -4
  51. data/lib/templates/standard/flay.html.erb +4 -4
  52. data/lib/templates/standard/flog.html.erb +1 -1
  53. data/lib/templates/standard/hotspots.html.erb +4 -4
  54. data/lib/templates/standard/index.html.erb +2 -2
  55. data/lib/templates/standard/rails_best_practices.html.erb +2 -2
  56. data/lib/templates/standard/rcov.html.erb +2 -2
  57. data/lib/templates/standard/reek.html.erb +1 -1
  58. data/lib/templates/standard/roodi.html.erb +2 -2
  59. data/lib/templates/standard/saikuro.html.erb +4 -4
  60. data/lib/templates/standard/stats.html.erb +2 -2
  61. data/spec/base/base_template_spec.rb +1 -1
  62. data/spec/base/configuration_spec.rb +36 -36
  63. data/spec/base/generator_spec.rb +10 -10
  64. data/spec/base/graph_spec.rb +41 -4
  65. data/spec/base/line_numbers_spec.rb +22 -22
  66. data/spec/base/report_spec.rb +9 -9
  67. data/spec/generators/churn_spec.rb +4 -4
  68. data/spec/generators/flay_spec.rb +31 -31
  69. data/spec/generators/flog_spec.rb +18 -18
  70. data/spec/generators/rails_best_practices_spec.rb +6 -6
  71. data/spec/generators/rcov_spec.rb +18 -18
  72. data/spec/generators/reek_spec.rb +10 -10
  73. data/spec/generators/roodi_spec.rb +2 -2
  74. data/spec/generators/saikuro_spec.rb +7 -7
  75. data/spec/generators/stats_spec.rb +6 -6
  76. data/spec/graphs/engines/gchart_spec.rb +8 -8
  77. data/spec/graphs/flog_grapher_spec.rb +8 -8
  78. data/spec/resources/line_numbers/foo.rb +7 -7
  79. data/spec/resources/line_numbers/module.rb +2 -2
  80. data/spec/resources/line_numbers/module_surrounds_class.rb +6 -6
  81. data/spec/resources/saikuro/index_cyclo.html +2 -2
  82. data/spec/resources/yml/20090630.yml +349 -349
  83. data/spec/resources/yml/metric_missing.yml +1 -1
  84. data/tasks/metric_fu.rake +4 -4
  85. metadata +4 -4
@@ -1,33 +1,34 @@
1
1
  require 'forwardable'
2
+ module MetricFu
3
+ class Ranking
4
+ extend Forwardable
2
5
 
3
- class Ranking
4
- extend Forwardable
6
+ def initialize
7
+ @items_to_score = {}
8
+ end
5
9
 
6
- def initialize
7
- @items_to_score = {}
8
- end
10
+ def top(num=nil)
11
+ if(num.is_a?(Numeric))
12
+ sorted_items[0,num]
13
+ else
14
+ sorted_items
15
+ end
16
+ end
9
17
 
10
- def top(num=nil)
11
- if(num.is_a?(Numeric))
12
- sorted_items[0,num]
13
- else
14
- sorted_items
18
+ def percentile(item)
19
+ index = sorted_items.index(item)
20
+ worse_item_count = (length - (index+1))
21
+ worse_item_count.to_f/length
15
22
  end
16
- end
17
23
 
18
- def percentile(item)
19
- index = sorted_items.index(item)
20
- worse_item_count = (length - (index+1))
21
- worse_item_count.to_f/length
22
- end
24
+ def_delegator :@items_to_score, :has_key?, :scored?
25
+ def_delegators :@items_to_score, :[], :[]=, :length, :each, :delete
23
26
 
24
- def_delegator :@items_to_score, :has_key?, :scored?
25
- def_delegators :@items_to_score, :[], :[]=, :length, :each, :delete
27
+ private
26
28
 
27
- private
29
+ def sorted_items
30
+ @sorted_items ||= @items_to_score.sort_by {|item, score| -score}.map {|item, score| item}
31
+ end
28
32
 
29
- def sorted_items
30
- @sorted_items ||= @items_to_score.sort_by {|item, score| -score}.map {|item, score| item}
31
33
  end
32
-
33
34
  end
@@ -6,7 +6,7 @@ class RcovAnalyzer
6
6
  def columns
7
7
  COLUMNS
8
8
  end
9
-
9
+
10
10
  def name
11
11
  :rcov
12
12
  end
@@ -22,13 +22,13 @@ class RcovAnalyzer
22
22
  def score(metric_ranking, item)
23
23
  ScoringStrategies.identity(metric_ranking, item)
24
24
  end
25
-
25
+
26
26
  def generate_records(data, table)
27
27
  return if data==nil
28
28
  data.each do |file_name, info|
29
29
  next if (file_name == :global_percent_run) || (info[:methods].nil?)
30
30
  info[:methods].each do |method_name, percentage_uncovered|
31
- location = Location.for(method_name)
31
+ location = MetricFu::Location.for(method_name)
32
32
  table << {
33
33
  "metric" => :rcov,
34
34
  'file_path' => file_name,
@@ -1,7 +1,9 @@
1
+ # coding: utf-8
2
+
1
3
  class ReekAnalyzer
2
4
  include ScoringStrategies
3
5
 
4
- REEK_ISSUE_INFO = {'Uncommunicative Name' =>
6
+ REEK_ISSUE_INFO = {'Uncommunicative Name' =>
5
7
  {'link' => 'http://wiki.github.com/kevinrutherford/reek/uncommunicative-name', 'info' => 'An Uncommunicative Name is a name that doesn’t communicate its intent well enough.'},
6
8
  'Class Variable' =>
7
9
  {'link' => 'http://wiki.github.com/kevinrutherford/reek/class-variable', 'info' => 'Class variables form part of the global runtime state, and as such make it easy for one part of the system to accidentally or inadvertently depend on another part of the system.'},
@@ -32,7 +34,7 @@ class ReekAnalyzer
32
34
  def columns
33
35
  COLUMNS.map{|column| "#{name}__#{column}"}
34
36
  end
35
-
37
+
36
38
  def name
37
39
  :reek
38
40
  end
@@ -46,20 +48,20 @@ class ReekAnalyzer
46
48
  end
47
49
 
48
50
  def score(metric_ranking, item)
49
- ScoringStrategies.percentile(metric_ranking, item)
51
+ ScoringStrategies.percentile(metric_ranking, item)
50
52
  end
51
-
53
+
52
54
  def generate_records(data, table)
53
55
  return if data==nil
54
56
  data[:matches].each do |match|
55
57
  file_path = match[:file_path]
56
58
  match[:code_smells].each do |smell|
57
- location = Location.for(smell[:method])
59
+ location = MetricFu::Location.for(smell[:method])
58
60
  smell_type = smell[:type]
59
61
  message = smell[:message]
60
62
  table << {
61
- "metric" => name,
62
- "file_path" => file_path,
63
+ "metric" => name, # important
64
+ "file_path" => file_path, # important
63
65
  # NOTE: ReekAnalyzer is currently different than other analyzers with regard
64
66
  # to column name. Note the COLUMNS constant and #columns method
65
67
  "reek__message" => message,
@@ -67,8 +69,8 @@ class ReekAnalyzer
67
69
  "reek__value" => parse_value(message),
68
70
  "reek__value_description" => build_value_description(smell_type, message),
69
71
  "reek__comparable_message" => comparable_message(smell_type, message),
70
- "class_name" => location.class_name,
71
- "method_name" => location.method_name,
72
+ "class_name" => location.class_name, # important
73
+ "method_name" => location.method_name, # important
72
74
  }
73
75
  end
74
76
  end
@@ -101,7 +103,7 @@ class ReekAnalyzer
101
103
  nil
102
104
  end
103
105
  end
104
-
106
+
105
107
  def parse_value(message)
106
108
  match = message.match(/\d+/)
107
109
  if(match)
@@ -10,25 +10,25 @@ module MetricFu
10
10
  #
11
11
  # The Report class is responsible two things:
12
12
  #
13
- # It adds information to the yaml report, produced by the system
13
+ # It adds information to the yaml report, produced by the system
14
14
  # as a whole, for each of the generators used in this test run.
15
15
  #
16
16
  # It also handles passing the information from each generator used
17
- # in this test run out to the template class set in
17
+ # in this test run out to the template class set in
18
18
  # MetricFu::Configuration.
19
19
  class Report
20
-
20
+
21
21
  # Renders the result of the report_hash into a yaml serialization
22
22
  # ready for writing out to a file.
23
23
  #
24
24
  # @return YAML
25
- # A YAML object containing the results of the report generation
25
+ # A YAML object containing the results of the report generation
26
26
  # process
27
27
  def to_yaml
28
28
  report_hash.to_yaml
29
29
  end
30
30
 
31
-
31
+
32
32
  def report_hash #:nodoc:
33
33
  @report_hash ||= {}
34
34
  end
@@ -43,7 +43,7 @@ module MetricFu
43
43
  @template.report = report_hash
44
44
  @template.write
45
45
  end
46
-
46
+
47
47
  # Adds a hash from a passed report, produced by one of the Generator
48
48
  # classes to the aggregate report_hash managed by this hash.
49
49
  #
@@ -55,7 +55,7 @@ module MetricFu
55
55
  end
56
56
 
57
57
  # Saves the passed in content to the passed in directory. If
58
- # a filename is passed in it will be used as the name of the
58
+ # a filename is passed in it will be used as the name of the
59
59
  # file, otherwise it will default to 'index.html'
60
60
  #
61
61
  # @param content String
@@ -76,7 +76,7 @@ module MetricFu
76
76
 
77
77
  # Checks to discover whether we should try and open the results
78
78
  # of the report in the browser on this system. We only try and open
79
- # in the browser if we're on OS X and we're not running in a
79
+ # in the browser if we're on OS X and we're not running in a
80
80
  # CruiseControl.rb environment. See MetricFu.configuration for more
81
81
  # details about how we make those guesses.
82
82
  #
@@ -91,7 +91,7 @@ module MetricFu
91
91
  # if we're able to open the browser on this platform.
92
92
  #
93
93
  # @param dir String
94
- # The directory path where the 'index.html' we want to open is
94
+ # The directory path where the 'index.html' we want to open is
95
95
  # stored
96
96
  def show_in_browser(dir)
97
97
  system("open #{dir}/index.html") if open_in_browser?
@@ -6,7 +6,7 @@ class RoodiAnalyzer
6
6
  def columns
7
7
  COLUMNS
8
8
  end
9
-
9
+
10
10
  def name
11
11
  :roodi
12
12
  end
@@ -22,7 +22,7 @@ class RoodiAnalyzer
22
22
  def score(metric_ranking, item)
23
23
  ScoringStrategies.percentile(metric_ranking, item)
24
24
  end
25
-
25
+
26
26
  def generate_records(data, table)
27
27
  return if data==nil
28
28
  Array(data[:problems]).each do |problem|
@@ -6,7 +6,7 @@ class SaikuroAnalyzer
6
6
  def columns
7
7
  COLUMNS
8
8
  end
9
-
9
+
10
10
  def name
11
11
  :saikuro
12
12
  end
@@ -22,16 +22,16 @@ class SaikuroAnalyzer
22
22
  def score(metric_ranking, item)
23
23
  ScoringStrategies.identity(metric_ranking, item)
24
24
  end
25
-
25
+
26
26
  def generate_records(data, table)
27
27
  return if data == nil
28
28
  data[:files].each do |file|
29
29
  file_name = file[:filename]
30
30
  file[:classes].each do |klass|
31
- location = Location.for(klass[:class_name])
31
+ location = MetricFu::Location.for(klass[:class_name])
32
32
  offending_class = location.class_name
33
33
  klass[:methods].each do |match|
34
- offending_method = Location.for(match[:name]).method_name
34
+ offending_method = MetricFu::Location.for(match[:name]).method_name
35
35
  table << {
36
36
  "metric" => name,
37
37
  "lines" => match[:lines],
@@ -1,15 +1,15 @@
1
1
  module ScoringStrategies
2
2
 
3
3
  def percentile(ranking, item)
4
- ranking.percentile(item)
4
+ ranking.percentile(item) # per project score percentile
5
5
  end
6
-
6
+
7
7
  def identity(ranking, item)
8
- ranking[item]
8
+ ranking[item] # Use the score you got (ex flog score of 20 is not bad even if it is the top one in project)
9
9
  end
10
10
 
11
11
  def present(row)
12
- 1
12
+ 1 # If present it's a one, not present it's a zero - For things like Reek that don't have a number
13
13
  end
14
14
 
15
15
  def sum(scores)
@@ -24,6 +24,6 @@ module ScoringStrategies
24
24
  sum = scores.inject( nil ) { |sum,x| sum ? sum+x : x }
25
25
  (sum.to_f / score_length.to_f)
26
26
  end
27
-
27
+
28
28
  extend self
29
29
  end
@@ -5,7 +5,7 @@ class StatsAnalyzer
5
5
  def columns
6
6
  COLUMNS
7
7
  end
8
-
8
+
9
9
  def name
10
10
  :stats
11
11
  end
@@ -13,7 +13,7 @@ class StatsAnalyzer
13
13
  def map(row)
14
14
  0
15
15
  end
16
-
16
+
17
17
  def reduce(scores)
18
18
  0
19
19
  end
@@ -1,9 +1,9 @@
1
1
  class Table
2
-
2
+
3
3
  def initialize(opts = {})
4
4
  @rows = []
5
5
  @columns = opts.fetch(:column_names)
6
-
6
+
7
7
  @make_index = opts.fetch(:make_index) {true}
8
8
  @metric_index = {}
9
9
  end
@@ -44,7 +44,7 @@ class Table
44
44
  end
45
45
  arr
46
46
  end
47
-
47
+
48
48
  def group_by_metric
49
49
  @metric_index.to_a
50
50
  end
@@ -64,7 +64,7 @@ class Table
64
64
  def to_a
65
65
  @rows
66
66
  end
67
-
67
+
68
68
  def map
69
69
  new_table = Table.new(:column_names => @columns)
70
70
  @rows.map do |row|
@@ -5,7 +5,7 @@ module MetricFu
5
5
  def initialize(options={})
6
6
  super
7
7
  end
8
-
8
+
9
9
  def emit
10
10
  @output = `churn --yaml`
11
11
  yaml_start = @output.index("---")
@@ -1,7 +1,7 @@
1
1
  require 'flay'
2
2
 
3
3
  module MetricFu
4
-
4
+
5
5
  class Flay < Generator
6
6
 
7
7
  def emit
@@ -18,23 +18,23 @@ module MetricFu
18
18
  num = nil
19
19
  worst_items = {}
20
20
  if @analyzer
21
- worst_items[:files] =
21
+ worst_items[:files] =
22
22
  @analyzer.worst_files(num).inject([]) do |array, worst_file|
23
- array <<
23
+ array <<
24
24
  {:location => @analyzer.location(:file, worst_file),
25
25
  :details => @analyzer.problems_with(:file, worst_file)}
26
26
  array
27
27
  end
28
28
  worst_items[:classes] = @analyzer.worst_classes(num).inject([]) do |array, class_name|
29
29
  location = @analyzer.location(:class, class_name)
30
- array <<
30
+ array <<
31
31
  {:location => location,
32
32
  :details => @analyzer.problems_with(:class, class_name)}
33
33
  array
34
34
  end
35
35
  worst_items[:methods] = @analyzer.worst_methods(num).inject([]) do |array, method_name|
36
36
  location = @analyzer.location(:method, method_name)
37
- array <<
37
+ array <<
38
38
  {:location => location,
39
39
  :details => @analyzer.problems_with(:method, method_name)}
40
40
  array
@@ -1,6 +1,6 @@
1
1
  module MetricFu
2
2
  class RailsBestPractices < Generator
3
-
3
+
4
4
  def emit
5
5
  @output = `rails_best_practices .`
6
6
  end
@@ -35,43 +35,43 @@ module MetricFu
35
35
  output_file = MetricFu.rcov[:external] ? MetricFu.rcov[:external] : MetricFu::Rcov.metric_directory + '/rcov.txt'
36
36
  output = File.open(output_file).read
37
37
  output = output.split(NEW_FILE_MARKER)
38
-
38
+
39
39
  output.shift # Throw away the first entry - it's the execution time etc.
40
-
40
+
41
41
  files = assemble_files(output)
42
42
 
43
43
  @global_total_lines = 0
44
44
  @global_total_lines_run = 0
45
-
45
+
46
46
  @rcov = add_coverage_percentage(files)
47
47
  end
48
48
 
49
49
  def to_h
50
50
  global_percent_run = ((@global_total_lines_run.to_f / @global_total_lines.to_f) * 100)
51
51
  add_method_data
52
- {:rcov => @rcov.merge({:global_percent_run => round_to_tenths(global_percent_run) })}
52
+ {:rcov => @rcov.merge({:global_percent_run => round_to_tenths(global_percent_run) })}
53
53
  end
54
-
54
+
55
55
  private
56
-
56
+
57
57
  def add_method_data
58
58
  @rcov.each_pair do |file_path, info|
59
59
  file_contents = ""
60
60
  coverage = []
61
-
61
+
62
62
  info[:lines].each_with_index do |line, index|
63
63
  file_contents << "#{line[:content]}\n"
64
64
  coverage << line[:was_run]
65
65
  end
66
-
66
+
67
67
  begin
68
- line_numbers = LineNumbers.new(file_contents)
68
+ line_numbers = MetricFu::LineNumbers.new(file_contents)
69
69
  rescue StandardError => e
70
70
  raise e unless e.message =~ /you shouldn't be able to get here/
71
71
  puts "ruby_parser blew up while trying to parse #{file_path}. You won't have method level Rcov information for this file."
72
72
  next
73
73
  end
74
-
74
+
75
75
  method_coverage_map = {}
76
76
  coverage.each_with_index do |covered, index|
77
77
  line_number = index + 1
@@ -84,16 +84,16 @@ module MetricFu
84
84
  method_coverage_map[method_name][:uncovered] += 1 if !covered
85
85
  end
86
86
  end
87
-
87
+
88
88
  @rcov[file_path][:methods] = {}
89
-
89
+
90
90
  method_coverage_map.each do |method_name, coverage_data|
91
91
  @rcov[file_path][:methods][method_name] = (coverage_data[:uncovered] / coverage_data[:total].to_f) * 100.0
92
92
  end
93
-
93
+
94
94
  end
95
95
  end
96
-
96
+
97
97
  def assemble_files(output)
98
98
  files = {}
99
99
  output.each_slice(2) {|out| files[out.first.strip] = out.last}
@@ -111,16 +111,16 @@ module MetricFu
111
111
  end
112
112
  files
113
113
  end
114
-
114
+
115
115
  def add_coverage_percentage(files)
116
116
  files.each_pair do |fname, content|
117
117
  lines = content[:lines]
118
118
  @global_total_lines_run += lines_run = lines.find_all {|line| line[:was_run] == true }.length
119
119
  @global_total_lines += total_lines = lines.length
120
120
  percent_run = ((lines_run.to_f / total_lines.to_f) * 100).round
121
- files[fname][:percent_run] = percent_run
121
+ files[fname][:percent_run] = percent_run
122
122
  end
123
123
  end
124
-
124
+
125
125
  end
126
126
  end