metric_fu 4.8.0 → 4.9.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 (57) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.metrics +1 -66
  5. data/.travis.yml +1 -0
  6. data/CONTRIBUTORS +13 -11
  7. data/DEV.md +21 -18
  8. data/Gemfile +1 -2
  9. data/Gemfile.devtools +0 -1
  10. data/HISTORY.md +7 -1
  11. data/README.md +79 -19
  12. data/Rakefile +0 -1
  13. data/checksum/metric_fu-4.8.0.gem.sha512 +1 -0
  14. data/gem_tasks/usage_test.rake +30 -0
  15. data/lib/metric_fu/configuration.rb +6 -1
  16. data/lib/metric_fu/data_structures/line_numbers.rb +1 -1
  17. data/lib/metric_fu/environment.rb +2 -7
  18. data/lib/metric_fu/logging/mf_debugger.rb +4 -4
  19. data/lib/metric_fu/metric.rb +2 -2
  20. data/lib/metric_fu/metrics/cane/template_awesome/cane.html.erb +74 -85
  21. data/lib/metric_fu/metrics/churn/churn.rb +1 -2
  22. data/lib/metric_fu/metrics/flay/template_awesome/flay.html.erb +2 -1
  23. data/lib/metric_fu/metrics/flog/template_awesome/flog.html.erb +2 -1
  24. data/lib/metric_fu/metrics/rails_best_practices/template_awesome/rails_best_practices.html.erb +2 -1
  25. data/lib/metric_fu/metrics/rcov/external_client.rb +24 -0
  26. data/lib/metric_fu/metrics/rcov/rcov.rb +9 -100
  27. data/lib/metric_fu/metrics/rcov/rcov_format_coverage.rb +140 -0
  28. data/lib/metric_fu/metrics/rcov/simplecov_formatter.rb +64 -0
  29. data/lib/metric_fu/metrics/rcov/template_awesome/rcov.html.erb +5 -1
  30. data/lib/metric_fu/metrics/reek/reek_grapher.rb +7 -1
  31. data/lib/metric_fu/metrics/reek/template_awesome/reek.html.erb +2 -1
  32. data/lib/metric_fu/metrics/roodi/template_awesome/roodi.html.erb +2 -1
  33. data/lib/metric_fu/metrics/stats/template_awesome/stats.html.erb +2 -2
  34. data/lib/metric_fu/reporting/graphs/grapher.rb +5 -20
  35. data/lib/metric_fu/reporting/templates/awesome/awesome_template.rb +1 -1
  36. data/lib/metric_fu/reporting/templates/awesome/layout.html.erb +8 -3
  37. data/lib/metric_fu/reporting/templates/javascripts/bluff_graph.js +15 -0
  38. data/lib/metric_fu/reporting/templates/javascripts/excanvas.js +1 -1
  39. data/lib/metric_fu/reporting/templates/javascripts/highcharts.js +294 -0
  40. data/lib/metric_fu/reporting/templates/javascripts/highcharts_graph.js +38 -0
  41. data/lib/metric_fu/reporting/templates/javascripts/standalone-framework.js +17 -0
  42. data/lib/metric_fu/reporting/templates/javascripts/utils.js +9 -0
  43. data/lib/metric_fu/templates/css/bluff.css +10 -10
  44. data/lib/metric_fu/templates/css/integrity.css +3 -0
  45. data/lib/metric_fu/templates/report.html.erb +3 -4
  46. data/lib/metric_fu/version.rb +1 -1
  47. data/metric_fu.gemspec +1 -0
  48. data/spec/fixtures/coverage.rb +13 -0
  49. data/spec/fixtures/exit0.sh +3 -0
  50. data/spec/fixtures/exit1.sh +3 -0
  51. data/spec/metric_fu/formatter/html_spec.rb +2 -2
  52. data/spec/metric_fu/metrics/rcov/simplecov_formatter_spec.rb +41 -0
  53. data/spec/spec_helper.rb +7 -8
  54. data/spec/support/usage_test.rb +134 -0
  55. data/spec/usage_test_spec.rb +69 -0
  56. metadata +39 -3
  57. metadata.gz.sig +0 -0
data/Rakefile CHANGED
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env rake
2
2
  require 'bundler/setup'
3
3
 
4
-
5
4
  Dir['./gem_tasks/*.rake'].each do |task|
6
5
  import(task)
7
6
  end
@@ -0,0 +1 @@
1
+ d46528ba18e30ce561e3d0dfb4245ee2e4b7ff72a895cff24cb4bb4a86ace4ed3f759eba96b26aa9e512ebd253d78d9697423bf468c09a09fb764597977f26c2
@@ -0,0 +1,30 @@
1
+ require 'redcarpet'
2
+ require 'English'
3
+ require 'tmpdir'
4
+ ROOT_PATH = File.expand_path("..", File.dirname(__FILE__))
5
+ require File.join(ROOT_PATH, 'spec', 'support', 'usage_test')
6
+ LIB_PATH = File.join(ROOT_PATH, 'lib')
7
+ BIN_PATH = File.join(ROOT_PATH, 'bin')
8
+ EXAMPLE_FILES = [
9
+ File.join(ROOT_PATH, 'README.md'),
10
+ File.join(ROOT_PATH, 'DEV.md')
11
+ ]
12
+ task "load_path" do
13
+ $LOAD_PATH.unshift(LIB_PATH)
14
+ $VERBOSE = nil
15
+ ENV['PATH'] = "#{BIN_PATH}:#{ENV['PATH']}"
16
+ ENV['CC_BUILD_ARTIFACTS'] = 'turn_off_browser_opening'
17
+ end
18
+ desc "Test that documentation usage works"
19
+ task "usage_test" => %w[load_path] do
20
+ usage_test = UsageTest.new
21
+ usage_test.test_files(EXAMPLE_FILES)
22
+ end
23
+
24
+ class HTMLRenderAndVerifyCodeBlocks < Redcarpet::Render::HTML
25
+
26
+ def block_code(code, language)
27
+ SnippetRunner.new(code, language).test!
28
+ end
29
+
30
+ end
@@ -78,6 +78,7 @@ module MetricFu
78
78
  @templates_configuration = MetricFu::Templates::Configuration.new
79
79
  MetricFu::Formatter::Templates.templates_configuration = @templates_configuration
80
80
  @formatters = []
81
+ @graph_engine = :bluff
81
82
  end
82
83
 
83
84
  # This allows us to have a nice syntax like:
@@ -128,8 +129,12 @@ module MetricFu
128
129
  MetricFu::Metric.enabled_metrics.select{|metric|metric.has_graph?}.map(&:name)
129
130
  end
130
131
 
132
+ def configure_graph_engine(graph_engine)
133
+ @graph_engine = graph_engine
134
+ end
135
+
131
136
  def graph_engine
132
- :bluff
137
+ @graph_engine
133
138
  end
134
139
 
135
140
  # This allows us to configure the templates with:
@@ -37,7 +37,7 @@ module MetricFu
37
37
  end
38
38
 
39
39
  # @param method [String] the method name being queried
40
- # @erturn [Integer, nil] the line number at which the given method is defined
40
+ # @return [Integer, nil] the line number at which the given method is defined
41
41
  def start_line_for_method(method)
42
42
  return nil unless @locations.has_key?(method)
43
43
  @locations[method].first
@@ -123,14 +123,9 @@ module MetricFu
123
123
  end
124
124
 
125
125
  def ruby_strangely_makes_accessors_private?
126
- ruby192? || jruby?
126
+ @private_accessors ||= ruby192? || jruby?
127
127
  end
128
+ module_function :ruby_strangely_makes_accessors_private?
128
129
 
129
- # HACK: for using this module via inclusion in Configuration
130
- def self.included(host_class)
131
- def host_class.ruby_strangely_makes_accessors_private?
132
- @private_accessors ||= allocate.ruby_strangely_makes_accessors_private?
133
- end
134
- end
135
130
  end
136
131
  end
@@ -23,8 +23,8 @@ module MfDebugger
23
23
  end
24
24
  # From episode 029 of Ruby Tapas by Avdi
25
25
  # https://rubytapas.dpdcart.com/subscriber/post?id=88
26
- def self.capture_output(&block)
27
- old_stdout = STDOUT.clone
26
+ def self.capture_output(stream=STDOUT, &block)
27
+ old_stdout = stream.clone
28
28
  pipe_r, pipe_w = IO.pipe
29
29
  pipe_r.sync = true
30
30
  output = ""
@@ -36,10 +36,10 @@ module MfDebugger
36
36
  rescue EOFError
37
37
  end
38
38
  end
39
- STDOUT.reopen(pipe_w)
39
+ stream.reopen(pipe_w)
40
40
  yield
41
41
  ensure
42
- STDOUT.reopen(old_stdout)
42
+ stream.reopen(old_stdout)
43
43
  pipe_w.close
44
44
  reader.join
45
45
  pipe_r.close
@@ -62,12 +62,12 @@ module MetricFu
62
62
  @configured_run_options
63
63
  end
64
64
 
65
- # @return default metric run options [Hash]
65
+ # @return [Hash] default metric run options
66
66
  def default_run_options
67
67
  not_implemented
68
68
  end
69
69
 
70
- # @return metric_options [Hash]
70
+ # @return [Hash] metric_options
71
71
  def has_graph?
72
72
  not_implemented
73
73
  end
@@ -1,100 +1,89 @@
1
- <html>
2
- <head>
3
- <title>Cane Results</title>
4
- <style>
5
- <%= inline_css("css/default.css") %>
6
- </style>
7
- </head>
1
+ <h3>Cane Results</h3>
8
2
 
9
- <body>
10
- <h1>Cane Results</h1>
11
- <a href="index.html">back to menu</a>
12
- <h2>Total Violations: <%= @cane[:total_violations] %></h2>
13
- <p><a href='https://github.com/square/cane'>Cane</a> reports code quality threshold violations.</p>
3
+ <p><a href='https://github.com/square/cane'>Cane</a> reports code quality threshold violations.</p>
14
4
 
15
- <% graph_name = 'cane' %>
16
- <canvas id="graph"></canvas>
17
- <script language="javascript" src="<%= graph_name %>.js?<%= Time.now.to_i %>" type="text/javascript"></script>
5
+ <div id="graph_container"></div>
18
6
 
19
- <% if @cane[:violations][:abc_complexity] && @cane[:violations][:abc_complexity].size > 0 %>
20
- <h3>Methods exceeding allowed Abc complexity (<%= @cane[:violations][:abc_complexity].size %>)</h3>
21
- <table>
22
- <tr>
23
- <th>File</th>
24
- <th>Method</th>
25
- <th>Complexity</th>
7
+ <% graph_name = 'cane' %>
8
+ <script language="javascript" src="<%= graph_name %>.js?<%= Time.now.to_i %>" type="text/javascript"></script>
9
+
10
+ <% if @cane[:violations][:abc_complexity] && @cane[:violations][:abc_complexity].size > 0 %>
11
+ <h3>Methods exceeding allowed Abc complexity (<%= @cane[:violations][:abc_complexity].size %>)</h3>
12
+ <table>
13
+ <tr>
14
+ <th>File</th>
15
+ <th>Method</th>
16
+ <th>Complexity</th>
17
+ </tr>
18
+ <% count = 0 %>
19
+ <% @cane[:violations][:abc_complexity].each do |violation| %>
20
+ <tr class='<%= cycle("light", "dark", count) %>'>
21
+ <td><%=violation[:file]%></td>
22
+ <td><%=violation[:method]%></td>
23
+ <td><%=violation[:complexity]%></td>
26
24
  </tr>
27
- <% count = 0 %>
28
- <% @cane[:violations][:abc_complexity].each do |violation| %>
29
- <tr class='<%= cycle("light", "dark", count) %>'>
30
- <td><%=violation[:file]%></td>
31
- <td><%=violation[:method]%></td>
32
- <td><%=violation[:complexity]%></td>
33
- </tr>
34
- <% count += 1 %>
35
- <% end %>
36
- </table>
25
+ <% count += 1 %>
37
26
  <% end %>
38
- <% if @cane[:violations][:line_style] && @cane[:violations][:line_style].size > 0 %>
39
- <h3>Lines violating style requirements (<%= @cane[:violations][:line_style].size %>)</h3>
40
- <table>
41
- <tr>
42
- <th>File</th>
43
- <th>Description</th>
27
+ </table>
28
+ <% end %>
29
+ <% if @cane[:violations][:line_style] && @cane[:violations][:line_style].size > 0 %>
30
+ <h3>Lines violating style requirements (<%= @cane[:violations][:line_style].size %>)</h3>
31
+ <table>
32
+ <tr>
33
+ <th>File</th>
34
+ <th>Description</th>
35
+ </tr>
36
+ <% count = 0 %>
37
+ <% @cane[:violations][:line_style].each do |violation| %>
38
+ <tr class='<%= cycle("light", "dark", count) %>'>
39
+ <td><%=violation[:line]%></td>
40
+ <td><%=violation[:description]%></td>
44
41
  </tr>
45
- <% count = 0 %>
46
- <% @cane[:violations][:line_style].each do |violation| %>
47
- <tr class='<%= cycle("light", "dark", count) %>'>
48
- <td><%=violation[:line]%></td>
49
- <td><%=violation[:description]%></td>
50
- </tr>
51
- <% count += 1 %>
52
- <% end %>
53
- </table>
42
+ <% count += 1 %>
54
43
  <% end %>
55
- <% if @cane[:violations][:documentation] && @cane[:violations][:documentation].size > 0 %>
56
- <h3>Missing documentation (<%= @cane[:violations][:documentation].size %>)</h3>
57
- <table>
44
+ </table>
45
+ <% end %>
46
+ <% if @cane[:violations][:documentation] && @cane[:violations][:documentation].size > 0 %>
47
+ <h3>Missing documentation (<%= @cane[:violations][:documentation].size %>)</h3>
48
+ <table>
49
+ <tr>
50
+ <th>Description</th>
51
+ </tr>
52
+ <% @cane[:violations][:documentation].each do |violation| %>
58
53
  <tr>
59
- <th>Description</th>
54
+ <td><%=violation[:description]%></td>
60
55
  </tr>
61
- <% @cane[:violations][:documentation].each do |violation| %>
62
- <tr>
63
- <td><%=violation[:description]%></td>
64
- </tr>
65
- <% end %>
66
- </table>
67
56
  <% end %>
68
- <% if @cane[:violations][:comment] && @cane[:violations][:comment].size > 0 %>
69
- <h3>Class definitions requiring comments (<%= @cane[:violations][:comment].size %>)</h3>
70
- <table>
71
- <tr>
72
- <th>File</th>
73
- <th>Class</th>
57
+ </table>
58
+ <% end %>
59
+ <% if @cane[:violations][:comment] && @cane[:violations][:comment].size > 0 %>
60
+ <h3>Class definitions requiring comments (<%= @cane[:violations][:comment].size %>)</h3>
61
+ <table>
62
+ <tr>
63
+ <th>File</th>
64
+ <th>Class</th>
65
+ </tr>
66
+ <% count = 0 %>
67
+ <% @cane[:violations][:comment].each do |violation| %>
68
+ <tr class='<%= cycle("light", "dark", count) %>'>
69
+ <td><%=violation[:line]%></td>
70
+ <td><%=violation[:class_name]%></td>
74
71
  </tr>
75
- <% count = 0 %>
76
- <% @cane[:violations][:comment].each do |violation| %>
77
- <tr class='<%= cycle("light", "dark", count) %>'>
78
- <td><%=violation[:line]%></td>
79
- <td><%=violation[:class_name]%></td>
80
- </tr>
81
- <% count += 1 %>
82
- <% end %>
83
- </table>
72
+ <% count += 1 %>
84
73
  <% end %>
85
- <% if @cane[:violations][:others] && @cane[:violations][:others].size > 0 %>
86
- <h3>Others (<%= @cane[:violations][:others].size %>)</h3>
87
- <table>
74
+ </table>
75
+ <% end %>
76
+ <% if @cane[:violations][:others] && @cane[:violations][:others].size > 0 %>
77
+ <h3>Others (<%= @cane[:violations][:others].size %>)</h3>
78
+ <table>
79
+ <tr>
80
+ <th>Description</th>
81
+ </tr>
82
+ <% @cane[:violations][:others].each do |violation| %>
88
83
  <tr>
89
- <th>Description</th>
84
+ <td><%=violation[:description]%></td>
90
85
  </tr>
91
- <% @cane[:violations][:others].each do |violation| %>
92
- <tr>
93
- <td><%=violation[:description]%></td>
94
- </tr>
95
- <% end %>
96
- </table>
97
86
  <% end %>
98
- <p>Generated on <%= Time.now.localtime %></p>
99
- </body>
100
- </html>
87
+ </table>
88
+ <% end %>
89
+ <p>Generated on <%= Time.now.localtime %></p>
@@ -27,10 +27,9 @@ module MetricFu
27
27
  def to_h
28
28
  {:churn => @churn[:churn]}
29
29
  end
30
-
30
+
31
31
  # @param args [Hash] churn metric run options
32
32
  # @return [Hash] churn results
33
- # @example {something}
34
33
  def run(args)
35
34
  # @note passing in false to report will return a hash
36
35
  # instead of the default String
@@ -2,8 +2,9 @@
2
2
 
3
3
  <p><a href='http://ruby.sadi.st/Flay.html'>Flay</a> analyzes ruby code for structural similarities.</p>
4
4
 
5
+ <div id="graph_container"></div>
6
+
5
7
  <% graph_name = 'flay' %>
6
- <canvas id="graph"></canvas>
7
8
  <script language="javascript" src="<%= graph_name %>.js?<%= Time.now.to_i %>" type="text/javascript"></script>
8
9
 
9
10
  <h4>Total Score (lower is better): <%= @flay[:total_score] %></h4>
@@ -1,8 +1,9 @@
1
1
  <h3>Flog Results</h3>
2
2
  <p><a href='http://ruby.sadi.st/Flog.html'>Flog</a> measures code complexity.</p>
3
3
 
4
+ <div id="graph_container"></div>
5
+
4
6
  <% graph_name = 'flog' %>
5
- <canvas id="graph"></canvas>
6
7
  <script language="javascript" src="<%= graph_name %>.js?<%= Time.now.to_i %>" type="text/javascript"></script>
7
8
 
8
9
  <h2>Total Flog score for all methods: <%= round_to_tenths @flog[:total]%></h2>
@@ -2,8 +2,9 @@
2
2
 
3
3
  <p><a href="http://github.com/railsbp/rails_best_practices">rails_best_practices</a> is a code metric tool for rails projects.</p>
4
4
 
5
+ <div id="graph_container"></div>
6
+
5
7
  <% graph_name = 'rails_best_practices' %>
6
- <canvas id="graph"></canvas>
7
8
  <script language="javascript" src="<%= graph_name %>.js?<%= Time.now.to_i %>" type="text/javascript"></script>
8
9
 
9
10
  <table>
@@ -0,0 +1,24 @@
1
+ # Reads and writes external coverage files as BINARY
2
+ module MetricFu
3
+ class RCovTestCoverageClient
4
+
5
+ def initialize(coverage_file)
6
+ @file_path = Pathname(coverage_file)
7
+ @file_path.dirname.mkpath
8
+ end
9
+
10
+ def post_results(payload)
11
+ mf_log "Saving coverage payload to #{@file_path}"
12
+ dump(payload)
13
+ end
14
+
15
+ def load
16
+ File.binread(@file_path)
17
+ end
18
+
19
+ def dump(payload)
20
+ File.open(@file_path, 'wb') {|file| file.write(payload) }
21
+ end
22
+
23
+ end
24
+ end
@@ -1,29 +1,17 @@
1
1
  MetricFu.lib_require { 'utility' }
2
2
  MetricFu.lib_require { 'calculate' }
3
3
  MetricFu.data_structures_require { 'line_numbers' }
4
+ require_relative 'rcov_format_coverage'
5
+ require_relative 'external_client'
4
6
 
5
7
  module MetricFu
6
8
 
7
9
  class RcovGenerator < MetricFu::Generator
8
- NEW_FILE_MARKER = /^={80}$/.freeze
9
10
 
10
11
  def self.metric
11
12
  :rcov
12
13
  end
13
14
 
14
- class Line
15
- attr_accessor :content, :was_run
16
-
17
- def initialize(content, was_run)
18
- @content = content
19
- @was_run = was_run
20
- end
21
-
22
- def to_h
23
- {:content => @content, :was_run => @was_run}
24
- end
25
- end
26
-
27
15
  def emit
28
16
  if run_rcov?
29
17
  mf_debug "** Running the specs/tests in the [#{options[:environment]}] environment"
@@ -54,105 +42,26 @@ module MetricFu
54
42
  end
55
43
 
56
44
  def analyze
57
- output = load_output
58
- output = output.split(NEW_FILE_MARKER)
59
-
60
- output.shift # Throw away the first entry - it's the execution time etc.
61
-
62
- files = assemble_files(output)
63
-
64
- @global_total_lines = 0
65
- @global_total_lines_run = 0
66
-
67
- @rcov = add_coverage_percentage(files)
45
+ rcov_text = load_output
46
+ formatter = MetricFu::RCovFormatCoverage.new(rcov_text)
47
+ @rcov = formatter.to_h
68
48
  end
69
49
 
70
50
  def to_h
71
- global_percent_run = ((@global_total_lines_run.to_f / @global_total_lines.to_f) * 100)
72
- add_method_data
73
- {:rcov => @rcov.merge({:global_percent_run => round_to_tenths(global_percent_run) })}
51
+ {
52
+ :rcov => @rcov
53
+ }
74
54
  end
75
55
 
76
56
  private
77
57
 
78
- def add_method_data
79
- @rcov.each_pair do |file_path, info|
80
- file_contents = ""
81
- coverage = []
82
-
83
- info[:lines].each_with_index do |line, index|
84
- file_contents << "#{line[:content]}\n"
85
- coverage << line[:was_run]
86
- end
87
-
88
- begin
89
- line_numbers = MetricFu::LineNumbers.new(file_contents)
90
- rescue StandardError => e
91
- raise e unless e.message =~ /you shouldn't be able to get here/
92
- mf_log "ruby_parser blew up while trying to parse #{file_path}. You won't have method level Rcov information for this file."
93
- next
94
- end
95
-
96
- method_coverage_map = {}
97
- coverage.each_with_index do |covered, index|
98
- line_number = index + 1
99
- if line_numbers.in_method?(line_number)
100
- method_name = line_numbers.method_at_line(line_number)
101
- method_coverage_map[method_name] ||= {}
102
- method_coverage_map[method_name][:total] ||= 0
103
- method_coverage_map[method_name][:total] += 1
104
- method_coverage_map[method_name][:uncovered] ||= 0
105
- method_coverage_map[method_name][:uncovered] += 1 if !covered
106
- end
107
- end
108
-
109
- @rcov[file_path][:methods] = {}
110
-
111
- method_coverage_map.each do |method_name, coverage_data|
112
- @rcov[file_path][:methods][method_name] = (coverage_data[:uncovered] / coverage_data[:total].to_f) * 100.0
113
- end
114
-
115
- end
116
- end
117
-
118
- def assemble_files(output)
119
- files = {}
120
- output.each_slice(2) {|out| files[out.first.strip] = out.last}
121
- files.each_pair {|fname, content| files[fname] = content.split("\n") }
122
- files.each_pair do |fname, content|
123
- content.map! do |raw_line|
124
- covered_line = raw_line.match(/^!!/).nil?
125
- Line.new(raw_line[3..-1], covered_line).to_h
126
- end
127
- content.reject! {|line| line[:content].to_s == '' }
128
- files[fname] = {:lines => content}
129
- end
130
- files
131
- end
132
-
133
- # TODO: remove multiple side effects
134
- # sets global ivars and
135
- # modifies the param passed in
136
- def add_coverage_percentage(files)
137
- files.each_pair do |fname, content|
138
- lines = content[:lines]
139
- lines_run = lines.count {|line| line[:was_run] }
140
- total_lines = lines.length
141
- integer_percent = Calculate.integer_percent(lines_run, total_lines)
142
-
143
- files[fname][:percent_run] = integer_percent
144
- @global_total_lines_run += lines_run
145
- @global_total_lines += total_lines
146
- end
147
- end
148
-
149
58
  # We never run rcov anymore
150
59
  def run_rcov?
151
60
  false
152
61
  end
153
62
 
154
63
  def load_output
155
- File.read(output_file)
64
+ MetricFu::RCovTestCoverageClient.new(output_file).load
156
65
  end
157
66
 
158
67
  def output_file