metric_fu 4.6.0 → 4.7.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 (97) hide show
  1. data.tar.gz.sig +0 -0
  2. data/.metrics +6 -0
  3. data/.travis.yml +6 -5
  4. data/CONTRIBUTING.md +15 -3
  5. data/Gemfile +3 -8
  6. data/Gemfile.devtools +3 -3
  7. data/HISTORY.md +23 -2
  8. data/README.md +42 -38
  9. data/checksum/metric_fu-4.6.0.gem.sha512 +1 -0
  10. data/gemfiles/Gemfile.travis +7 -0
  11. data/lib/metric_fu/cli/helper.rb +2 -1
  12. data/lib/metric_fu/configuration.rb +0 -2
  13. data/lib/metric_fu/formatter/html.rb +2 -0
  14. data/lib/metric_fu/formatter/syntax.rb +47 -0
  15. data/lib/metric_fu/gem_version.rb +44 -11
  16. data/lib/metric_fu/io.rb +5 -5
  17. data/lib/metric_fu/loader.rb +12 -5
  18. data/lib/metric_fu/metric.rb +10 -0
  19. data/lib/metric_fu/metrics/base_template.rb +2 -2
  20. data/lib/metric_fu/metrics/cane/cane.rb +1 -0
  21. data/lib/metric_fu/metrics/churn/churn.rb +16 -42
  22. data/lib/metric_fu/metrics/churn/init.rb +7 -1
  23. data/lib/metric_fu/metrics/hotspots/analysis/groupings.rb +1 -0
  24. data/lib/metric_fu/metrics/hotspots/analysis/problems.rb +1 -0
  25. data/lib/metric_fu/metrics/hotspots/analysis/ranked_problem_location.rb +2 -0
  26. data/lib/metric_fu/metrics/hotspots/analysis/rankings.rb +5 -0
  27. data/lib/metric_fu/metrics/hotspots/hotspot.rb +3 -0
  28. data/lib/metric_fu/metrics/hotspots/hotspot_analyzer.rb +8 -4
  29. data/lib/metric_fu/metrics/hotspots/hotspots.rb +11 -0
  30. data/lib/metric_fu/metrics/reek/reek.rb +1 -1
  31. data/lib/metric_fu/metrics/reek/reek_hotspot.rb +0 -1
  32. data/lib/metric_fu/metrics/saikuro/saikuro.rb +1 -0
  33. data/lib/metric_fu/reporting/graphs/grapher.rb +0 -1
  34. data/lib/metric_fu/reporting/templates/awesome/awesome_template.rb +8 -59
  35. data/lib/metric_fu/run.rb +1 -1
  36. data/lib/metric_fu/{reporting/templates/awesome → templates}/css/bluff.css +0 -0
  37. data/lib/metric_fu/{reporting/templates/awesome → templates}/css/buttons.css +0 -0
  38. data/lib/metric_fu/{reporting/templates/awesome → templates}/css/default.css +0 -0
  39. data/lib/metric_fu/{reporting/templates/awesome → templates}/css/integrity.css +0 -0
  40. data/lib/metric_fu/{reporting/templates/awesome → templates}/css/rcov.css +0 -0
  41. data/lib/metric_fu/{reporting/templates/awesome → templates}/css/reset.css +0 -0
  42. data/lib/metric_fu/{reporting/templates/awesome → templates}/css/syntax.css +0 -0
  43. data/lib/metric_fu/templates/report.html.erb +32 -0
  44. data/lib/metric_fu/templates/report.rb +36 -0
  45. data/lib/metric_fu/utility.rb +4 -0
  46. data/lib/metric_fu/version.rb +1 -1
  47. data/metric_fu.gemspec +11 -2
  48. data/spec/cli/helper_spec.rb +33 -33
  49. data/spec/metric_fu/configuration_spec.rb +20 -20
  50. data/spec/metric_fu/data_structures/line_numbers_spec.rb +12 -11
  51. data/spec/metric_fu/data_structures/location_spec.rb +27 -26
  52. data/spec/metric_fu/formatter/html_spec.rb +18 -8
  53. data/spec/metric_fu/formatter/yaml_spec.rb +14 -3
  54. data/spec/metric_fu/formatter_spec.rb +5 -5
  55. data/spec/metric_fu/loader_spec.rb +1 -1
  56. data/spec/metric_fu/metric_spec.rb +2 -2
  57. data/spec/metric_fu/metrics/base_template_spec.rb +50 -50
  58. data/spec/metric_fu/metrics/cane/cane_spec.rb +29 -28
  59. data/spec/metric_fu/metrics/churn/churn_spec.rb +10 -33
  60. data/spec/metric_fu/metrics/flay/flay_grapher_spec.rb +9 -8
  61. data/spec/metric_fu/metrics/flay/flay_spec.rb +14 -13
  62. data/spec/metric_fu/metrics/flog/flog_grapher_spec.rb +16 -15
  63. data/spec/metric_fu/metrics/flog/flog_spec.rb +10 -9
  64. data/spec/metric_fu/metrics/generator_spec.rb +19 -19
  65. data/spec/metric_fu/metrics/graph_spec.rb +11 -9
  66. data/spec/metric_fu/metrics/hotspots/analysis/analyzed_problems_spec.rb +5 -0
  67. data/spec/metric_fu/metrics/hotspots/analysis/analyzer_tables_spec.rb +11 -6
  68. data/spec/metric_fu/metrics/hotspots/analysis/ranking_spec.rb +4 -3
  69. data/spec/metric_fu/metrics/hotspots/analysis/rankings_spec.rb +16 -10
  70. data/spec/metric_fu/metrics/hotspots/analysis/table_spec.rb +2 -1
  71. data/spec/metric_fu/metrics/hotspots/hotspot_analyzer_spec.rb +2 -1
  72. data/spec/metric_fu/metrics/hotspots/hotspot_spec.rb +8 -2
  73. data/spec/metric_fu/metrics/hotspots/hotspots_spec.rb +4 -8
  74. data/spec/metric_fu/metrics/rails_best_practices/rails_best_practices_grapher_spec.rb +10 -9
  75. data/spec/metric_fu/metrics/rails_best_practices/rails_best_practices_spec.rb +8 -7
  76. data/spec/metric_fu/metrics/rcov/rcov_grapher_spec.rb +8 -8
  77. data/spec/metric_fu/metrics/rcov/rcov_hotspot_spec.rb +7 -4
  78. data/spec/metric_fu/metrics/rcov/rcov_spec.rb +15 -14
  79. data/spec/metric_fu/metrics/reek/reek_grapher_spec.rb +10 -9
  80. data/spec/metric_fu/metrics/reek/reek_spec.rb +17 -15
  81. data/spec/metric_fu/metrics/roodi/roodi_grapher_spec.rb +9 -8
  82. data/spec/metric_fu/metrics/roodi/roodi_spec.rb +4 -3
  83. data/spec/metric_fu/metrics/saikuro/saikuro_spec.rb +13 -10
  84. data/spec/metric_fu/metrics/stats/stats_grapher_spec.rb +13 -12
  85. data/spec/metric_fu/metrics/stats/stats_spec.rb +16 -14
  86. data/spec/metric_fu/reporter_spec.rb +8 -7
  87. data/spec/metric_fu/reporting/graphs/grapher_spec.rb +1 -1
  88. data/spec/metric_fu/reporting/result_spec.rb +12 -11
  89. data/spec/metric_fu/run_spec.rb +24 -8
  90. data/spec/spec_helper.rb +1 -2
  91. data/spec/support/helper_methods.rb +14 -1
  92. metadata +124 -57
  93. metadata.gz.sig +0 -0
  94. checksums.yaml +0 -7
  95. checksums.yaml.gz.sig +0 -0
  96. data/lib/metric_fu/initial_requires.rb +0 -13
  97. data/lib/metric_fu/load_files.rb +0 -34
data/lib/metric_fu/io.rb CHANGED
@@ -101,21 +101,21 @@ module MetricFu
101
101
  # Given an existing io stream, the stream will not
102
102
  # be automatically closed. Cleanup, if necessary, is
103
103
  # the responsibility of the caller.
104
- def io_for(path_or_io)
104
+ def io_for(path_or_io, &block)
105
105
  raise ArgumentError, "No path or io provided." if path_or_io.nil?
106
106
  raise ArgumentError, "No block given. Cannot yield io stream." unless block_given?
107
107
 
108
108
  if path_or_io.respond_to?(:write)
109
109
  # We have an existing open stream...
110
- yield path_or_io
110
+ block.call(path_or_io)
111
111
  else # Otherwise, we assume its a file path...
112
- file_for(path_or_io) {|io| yield io }
112
+ file_for(path_or_io, &block)
113
113
  end
114
114
  end
115
115
 
116
- def file_for(path)
116
+ def file_for(path, &block)
117
117
  File.open(path_relative_to_base(path), 'w') do |file|
118
- yield file
118
+ block.call(file)
119
119
  end
120
120
  end
121
121
 
@@ -72,13 +72,21 @@ module MetricFu
72
72
  end
73
73
 
74
74
  def setup
75
+ MetricFu.logging_require { 'mf_debugger' }
76
+ Object.send :include, MfDebugger
77
+ MfDebugger::Logger.debug_on = !!(ENV['MF_DEBUG'] =~ /true/i)
78
+
75
79
  MetricFu.lib_require { 'configuration' }
76
80
  MetricFu.lib_require { 'metric' }
77
- # TODO: consolidate these setup files
78
- MetricFu.lib_require { 'initial_requires' }
79
- # Load a few things to make our lives easier elsewhere.
80
- MetricFu.lib_require { 'load_files' }
81
+
82
+ Dir.glob(File.join(MetricFu.metrics_dir, '**/init.rb')).each{|init_file|require(init_file)}
83
+
81
84
  load_user_configuration
85
+
86
+ MetricFu.lib_require { 'reporter' }
87
+ MetricFu.reporting_require { 'result' }
88
+
89
+ MetricFu.load_tasks('metric_fu.rake', task_name: 'metrics:all')
82
90
  end
83
91
 
84
92
  def load_user_configuration
@@ -88,4 +96,3 @@ module MetricFu
88
96
 
89
97
  end
90
98
  end
91
-
@@ -1,5 +1,6 @@
1
1
  require 'set'
2
2
  MetricFu.lib_require { 'gem_run' }
3
+ MetricFu.metrics_require { 'generator' }
3
4
  # Encapsulates the configuration options for each metric
4
5
  module MetricFu
5
6
  class Metric
@@ -18,6 +19,7 @@ module MetricFu
18
19
 
19
20
  # TODO: Confirm this catches load errors from requires in subclasses, such as for flog
20
21
  def activate
22
+ MetricFu.metrics_require { default_metric_library_paths }
21
23
  @libraries.each {|library| require(library) }
22
24
  self.activated = true
23
25
  rescue LoadError => e
@@ -120,5 +122,13 @@ module MetricFu
120
122
  @libraries << file.strip
121
123
  end
122
124
 
125
+ def default_metric_library_paths
126
+ paths = []
127
+ paths << generator_path = "#{name}/#{name}"
128
+ if has_graph?
129
+ paths << grapher_path = "#{name}/#{name}_grapher"
130
+ end
131
+ paths
132
+ end
123
133
  end
124
134
  end
@@ -120,8 +120,8 @@ module MetricFu
120
120
  # @return String
121
121
  # The contents of the css file
122
122
  def inline_css(css)
123
- css_file = File.join(template_directory, css)
124
- open(css_file) {|f| f.read }
123
+ css_file = File.join(MetricFu.lib_dir,'templates', css)
124
+ MetricFu::Utility.binread(css_file)
125
125
  end
126
126
 
127
127
  # Provides a link to open a file through the textmate protocol
@@ -1,3 +1,4 @@
1
+ MetricFu.metrics_require { 'cane/violations' }
1
2
  module MetricFu
2
3
  class CaneGenerator < Generator
3
4
  attr_reader :violations, :total_violations
@@ -6,61 +6,35 @@ module MetricFu
6
6
  :churn
7
7
  end
8
8
 
9
+ ###
10
+ # options available are what can be passed to churn_calculator
11
+ # https://github.com/danmayer/churn#library-options
12
+ ###
9
13
  def emit
10
- @output = generate_churn_metrics
14
+ @output = run(options)
11
15
  end
12
16
 
13
17
  def analyze
14
- if @output.nil? || @output.match(/Churning requires.*git/)
18
+ if @output.nil? || @output.size.zero?
15
19
  @churn = {:churn => {}}
16
20
  else
17
- @churn = YAML::load(@output)
21
+ @churn = @output
18
22
  end
23
+ @churn
19
24
  end
20
25
 
21
26
  # ensure hash only has the :churn key
22
27
  def to_h
23
28
  {:churn => @churn[:churn]}
24
29
  end
25
-
26
- private
27
-
28
- def generate_churn_metrics
29
- output = churn_code
30
- ensure_output_is_valid_yaml(output)
31
- end
32
-
33
- def churn_code
34
- run!(build_churn_options)
35
- end
36
-
37
- def ensure_output_is_valid_yaml(output)
38
- yaml_start = output.index("---")
39
- if yaml_start
40
- output[yaml_start...output.length]
41
- else
42
- nil
43
- end
44
- end
45
-
46
- def build_churn_options
47
- opts = ["--yaml"]
48
- churn_options.each do |churn_option, command_flag|
49
- if has_option?(churn_option)
50
- opts << "#{command_flag}=#{options[churn_option]}"
51
- end
52
- end
53
- opts.join(" ")
54
- end
55
-
56
- def has_option?(churn_option)
57
- options.include?(churn_option)
58
- end
59
- def churn_options
60
- {
61
- :minimum_churn_count => '--minimum_churn_count',
62
- :start_date => '--start_date'
63
- }
30
+
31
+ # @param args [Hash] churn metric run options
32
+ # @return [Hash] churn results
33
+ # @example {something}
34
+ def run(args)
35
+ # @note passing in false to report will return a hash
36
+ # instead of the default String
37
+ ::Churn::ChurnCalculator.new(args).report(false)
64
38
  end
65
39
 
66
40
  end
@@ -6,7 +6,12 @@ module MetricFu
6
6
  end
7
7
 
8
8
  def default_run_options
9
- { :start_date => %q("1 year ago"), :minimum_churn_count => 10}
9
+ {
10
+ :start_date => %q("1 year ago"),
11
+ :minimum_churn_count => 10,
12
+ :ignore_files => [],
13
+ :data_directory => MetricFu::Io::FileSystem.scratch_directory(name)
14
+ }
10
15
  end
11
16
 
12
17
  def has_graph?
@@ -18,6 +23,7 @@ module MetricFu
18
23
  end
19
24
 
20
25
  def activate
26
+ activate_library('churn/churn_calculator')
21
27
  super
22
28
  end
23
29
 
@@ -1,3 +1,4 @@
1
+ MetricFu.metrics_require { 'hotspots/analysis/grouping' }
1
2
  module MetricFu
2
3
  class HotspotGroupings
3
4
 
@@ -1,3 +1,4 @@
1
+ MetricFu.metrics_require { 'hotspots/analysis/groupings' }
1
2
  module MetricFu
2
3
  class HotspotProblems
3
4
 
@@ -1,3 +1,5 @@
1
+ MetricFu.lib_require { 'errors/analysis_error' }
2
+ MetricFu.metrics_require { 'hotspots/analysis/problems' }
1
3
  module MetricFu
2
4
  class HotspotRankedProblemLocation
3
5
  MetricFu.data_structures_require { 'location' }
@@ -1,3 +1,8 @@
1
+ MetricFu.metrics_require do
2
+ [
3
+ 'hotspots/analysis/ranking'
4
+ ]
5
+ end
1
6
  module MetricFu
2
7
  class HotspotRankings
3
8
 
@@ -1,3 +1,6 @@
1
+ MetricFu.lib_require { 'errors/analysis_error' }
2
+ MetricFu.metrics_require { 'hotspots/analysis/scoring_strategies' }
3
+
1
4
  module MetricFu
2
5
  class Hotspot
3
6
  def self.metric
@@ -1,8 +1,12 @@
1
- require File.expand_path('analysis_error', MetricFu.errors_dir)
2
- %w(table record grouping ranking problems).each do |path|
3
- MetricFu.metrics_require { "hotspots/analysis/#{path}" }
1
+ require 'yaml'
2
+ MetricFu.metrics_require do
3
+ [
4
+ 'hotspots/hotspot',
5
+ 'hotspots/analysis/analyzer_tables',
6
+ 'hotspots/analysis/analyzed_problems',
7
+ 'hotspots/analysis/rankings'
8
+ ]
4
9
  end
5
- MetricFu.metrics_require { 'hotspots/hotspot' }
6
10
 
7
11
  module MetricFu
8
12
  class HotspotAnalyzer
@@ -8,6 +8,9 @@ module MetricFu
8
8
  end
9
9
 
10
10
  def initialize(options={})
11
+ MetricFu::Metric.enabled_metrics.each do |metric|
12
+ require_hotspot(metric.name)
13
+ end
11
14
  super
12
15
  end
13
16
 
@@ -27,6 +30,14 @@ module MetricFu
27
30
  end
28
31
  result
29
32
  end
33
+
34
+ private
35
+
36
+ def require_hotspot(metric_name)
37
+ require "metric_fu/metrics/#{metric_name}/#{metric_name}_hotspot"
38
+ rescue LoadError
39
+ mf_debug "*** No hotspot for #{metric_name}"
40
+ end
30
41
  end
31
42
 
32
43
  end
@@ -23,7 +23,7 @@ module MetricFu
23
23
  def analyze
24
24
  @matches = @output.chomp.split("\n\n").map{|m| m.split("\n") }
25
25
  @matches = @matches.map do |match|
26
- file_path = match.shift.split('--').first
26
+ file_path = match.shift.split(' -- ').first
27
27
  file_path = file_path.gsub('"', ' ').strip
28
28
  code_smells = match.map do |smell|
29
29
  match_object = smell.match(REEK_REGEX)
@@ -1,5 +1,4 @@
1
1
  # coding: utf-8
2
-
3
2
  class ReekHotspot < MetricFu::Hotspot
4
3
 
5
4
  # Note that in practice, the prefix reek__ is appended to each one
@@ -2,6 +2,7 @@
2
2
  MetricFu.lib_require { 'utility' }
3
3
  MetricFu.metrics_require { 'saikuro/scratch_file' }
4
4
  MetricFu.metrics_require { 'saikuro/parsing_element' }
5
+ MetricFu.data_structures_require { 'line_numbers' }
5
6
  module MetricFu
6
7
 
7
8
  class SaikuroGenerator < MetricFu::Generator
@@ -1,4 +1,3 @@
1
- # Class opened and modified by requiring a graph engine
2
1
  require 'multi_json'
3
2
  module MetricFu
4
3
  class Grapher
@@ -1,7 +1,6 @@
1
1
  require 'fileutils'
2
- require 'coderay'
3
2
  MetricFu.metrics_require { 'base_template' }
4
- MetricFu.lib_require { 'utility' }
3
+ MetricFu.lib_require { 'templates/report' }
5
4
 
6
5
  class AwesomeTemplate < MetricFu::Template
7
6
 
@@ -44,70 +43,20 @@ class AwesomeTemplate < MetricFu::Template
44
43
  write_file_data
45
44
  end
46
45
 
47
- def convert_ruby_to_html(ruby_text, line_number)
48
- tokens = CodeRay.scan(MetricFu::Utility.clean_ascii_text(ruby_text), :ruby)
49
- options = { :css => :class, :style => :alpha }
50
- if line_number.to_i > 0
51
- options = options.merge({:line_numbers => :inline, :line_number_start => line_number.to_i })
52
- end
53
- tokens.div(options)
54
- # CodeRay options
55
- # used to analyze source code, because object Tokens is a list of tokens with specified types.
56
- # :tab_width – tabulation width in spaces. Default: 8
57
- # :css – how to include the styles (:class и :style). Default: :class)
58
- #
59
- # :wrap – wrap result in html tag :page, :div, :span or not to wrap (nil)
60
- #
61
- # :line_numbers – how render line numbers (:table, :inline, :list or nil)
62
- #
63
- # :line_number_start – first line number
64
- #
65
- # :bold_every – make every n-th line number bold. Default: 10
66
- end
67
46
  def write_file_data
68
-
69
47
  per_file_data.each_pair do |file, lines|
70
48
  next if file.to_s.empty?
71
49
  next unless File.file?(file)
50
+ report = MetricFu::Templates::Report.new(file, lines).render(@metrics)
72
51
 
73
- data = File.readlines(file)
74
- fn = "#{file.gsub(%r{/}, '_')}.html"
75
-
76
- out = <<-HTML
77
- <html><head><style>
78
- #{inline_css('css/syntax.css')}
79
- #{inline_css('css/bluff.css') if MetricFu.configuration.graph_engine == :bluff}
80
- #{inline_css('css/rcov.css') if @metrics.has_key?(:rcov)}
81
- </style></head><body>
82
- HTML
83
- out << "<table cellpadding='0' cellspacing='0' class='ruby'>"
84
- data.each_with_index do |line, idx|
85
- line_number = (idx + 1).to_s
86
- out << "<tr>"
87
- out << "<td valign='top'>"
88
- if lines.has_key?(line_number)
89
- out << "<ul>"
90
- lines[line_number].each do |problem|
91
- out << "<li>#{problem[:description]} &raquo; #{problem[:type]}</li>"
92
- end
93
- out << "</ul>"
94
- else
95
- out << "&nbsp;"
96
- end
97
- out << "</td>"
98
- if MetricFu::Formatter::Templates.option('syntax_highlighting')
99
- line_for_display = convert_ruby_to_html(line, line_number)
100
- else
101
- line_for_display = "<a name='n#{line_number}' href='n#{line_number}'>#{line_number}</a>#{line}"
102
- end
103
- out << "<td valign='top'>#{line_for_display}</td>"
104
- out << "</tr>"
105
- end
106
- out << "<table></body></html>"
107
-
108
- formatter.write_template(out, fn)
52
+ formatter.write_template(report, html_filename(file))
109
53
  end
110
54
  end
55
+
56
+ def html_filename(file)
57
+ "#{file.gsub(%r{/}, '_')}.html"
58
+ end
59
+
111
60
  def template_directory
112
61
  File.dirname(__FILE__)
113
62
  end
data/lib/metric_fu/run.rb CHANGED
@@ -70,7 +70,7 @@ module MetricFu
70
70
  end
71
71
  end
72
72
  def reporter
73
- Reporter.new(MetricFu.configuration.formatters)
73
+ MetricFu::Reporter.new(MetricFu.configuration.formatters)
74
74
  end
75
75
  end
76
76
  end
@@ -0,0 +1,32 @@
1
+ <html>
2
+ <head>
3
+ <style>
4
+ <%= inline_css('css/syntax.css') %>
5
+ <%= inline_css('css/bluff.css') if MetricFu.configuration.graph_engine == :bluff %>
6
+ <%= inline_css('css/rcov.css') if @metrics.has_key?(:rcov) %>
7
+ </style>
8
+ </head>
9
+ <body>
10
+ <table cellpadding='0' cellspacing='0' class='ruby'>
11
+ <% @data.each_with_index do |line, idx| %>
12
+ <% line_number = (idx + 1).to_s %>
13
+ <tr>
14
+ <td valign='top'>
15
+ <% if @lines.has_key?(line_number) %>
16
+ <ul>
17
+ <% @lines[line_number].each do |problem| %>
18
+ <li><%= "#{problem[:description]} &raquo; #{problem[:type]}" %></li>
19
+ <% end %>
20
+ </ul>
21
+ <% else %>
22
+ &nbsp;
23
+ <% end %>
24
+ </td>
25
+ <td valign='top'>
26
+ <%= line_for_display(line, line_number) %>
27
+ </td>
28
+ </tr>
29
+ <% end %>
30
+ <table>
31
+ </body>
32
+ </html>