metric_fu 4.2.1 → 4.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. data/.metrics +19 -1
  2. data/.travis.yml +2 -2
  3. data/Gemfile +2 -1
  4. data/HISTORY.md +24 -0
  5. data/README.md +60 -1
  6. data/TODO.md +1 -0
  7. data/bin/mf-saikuro +8 -0
  8. data/checksum/metric_fu-4.3.0.gem.sha512 +1 -0
  9. data/gemfiles/Gemfile.travis +9 -0
  10. data/lib/metric_fu.rb +1 -1
  11. data/lib/metric_fu/cli/client.rb +2 -2
  12. data/lib/metric_fu/cli/helper.rb +3 -3
  13. data/lib/metric_fu/cli/parser.rb +37 -16
  14. data/lib/metric_fu/configuration.rb +9 -1
  15. data/lib/metric_fu/constantize.rb +57 -0
  16. data/lib/metric_fu/data_structures/line_numbers.rb +19 -3
  17. data/lib/metric_fu/data_structures/location.rb +8 -3
  18. data/lib/metric_fu/formatter.rb +24 -0
  19. data/lib/metric_fu/formatter/html.rb +91 -0
  20. data/lib/metric_fu/formatter/yaml.rb +18 -0
  21. data/lib/metric_fu/initial_requires.rb +0 -1
  22. data/lib/metric_fu/io.rb +69 -0
  23. data/lib/metric_fu/load_files.rb +5 -2
  24. data/lib/metric_fu/logging/mf_debugger.rb +23 -0
  25. data/lib/metric_fu/metrics/base_template.rb +10 -4
  26. data/lib/metric_fu/metrics/cane/cane.rb +2 -1
  27. data/lib/metric_fu/metrics/cane/cane_bluff_grapher.rb +10 -9
  28. data/lib/metric_fu/metrics/cane/cane_gchart_grapher.rb +25 -0
  29. data/lib/metric_fu/metrics/flay/flay_bluff_grapher.rb +10 -9
  30. data/lib/metric_fu/metrics/flay/flay_gchart_grapher.rb +14 -11
  31. data/lib/metric_fu/metrics/flog/flog_bluff_grapher.rb +11 -10
  32. data/lib/metric_fu/metrics/flog/flog_gchart_grapher.rb +22 -15
  33. data/lib/metric_fu/metrics/generator.rb +10 -10
  34. data/lib/metric_fu/metrics/graph.rb +2 -2
  35. data/lib/metric_fu/metrics/hotspots/analysis/scoring_strategies.rb +3 -3
  36. data/lib/metric_fu/metrics/hotspots/hotspot.rb +4 -3
  37. data/lib/metric_fu/metrics/hotspots/hotspot_analyzer.rb +19 -10
  38. data/lib/metric_fu/metrics/hotspots/hotspots.rb +1 -1
  39. data/lib/metric_fu/metrics/hotspots/template_awesome/hotspots.html.erb +45 -45
  40. data/lib/metric_fu/metrics/rails_best_practices/rails_best_practices_bluff_grapher.rb +10 -9
  41. data/lib/metric_fu/metrics/rails_best_practices/rails_best_practices_gchart_grapher.rb +21 -15
  42. data/lib/metric_fu/metrics/rcov/rcov.rb +1 -1
  43. data/lib/metric_fu/metrics/rcov/rcov_bluff_grapher.rb +10 -9
  44. data/lib/metric_fu/metrics/rcov/rcov_gchart_grapher.rb +13 -8
  45. data/lib/metric_fu/metrics/reek/reek_bluff_grapher.rb +9 -13
  46. data/lib/metric_fu/metrics/reek/reek_gchart_grapher.rb +22 -17
  47. data/lib/metric_fu/metrics/roodi/roodi_bluff_grapher.rb +10 -9
  48. data/lib/metric_fu/metrics/roodi/roodi_gchart_grapher.rb +14 -11
  49. data/lib/metric_fu/metrics/saikuro/saikuro.rb +5 -34
  50. data/lib/metric_fu/metrics/stats/stats.rb +2 -1
  51. data/lib/metric_fu/metrics/stats/stats_bluff_grapher.rb +11 -10
  52. data/lib/metric_fu/metrics/stats/stats_gchart_grapher.rb +21 -14
  53. data/lib/metric_fu/reporter.rb +37 -0
  54. data/lib/metric_fu/reporting/graphs/engines/bluff.rb +20 -0
  55. data/lib/metric_fu/reporting/graphs/engines/gchart.rb +41 -3
  56. data/lib/metric_fu/reporting/graphs/grapher.rb +9 -2
  57. data/lib/metric_fu/reporting/result.rb +51 -0
  58. data/lib/metric_fu/reporting/templates/awesome/awesome_template.rb +8 -8
  59. data/lib/metric_fu/run.rb +34 -39
  60. data/lib/metric_fu/version.rb +1 -1
  61. data/lib/metric_fu_requires.rb +50 -33
  62. data/metric_fu.gemspec +30 -39
  63. data/spec/cli/helper_spec.rb +15 -0
  64. data/spec/metric_fu/configuration_spec.rb +40 -2
  65. data/spec/metric_fu/formatter/html_spec.rb +134 -0
  66. data/spec/metric_fu/formatter/yaml_spec.rb +59 -0
  67. data/spec/metric_fu/formatter_spec.rb +49 -0
  68. data/spec/metric_fu/metrics/base_template_spec.rb +23 -23
  69. data/spec/metric_fu/metrics/cane/cane_spec.rb +2 -2
  70. data/spec/metric_fu/metrics/churn/churn_spec.rb +1 -1
  71. data/spec/metric_fu/metrics/flay/flay_spec.rb +4 -4
  72. data/spec/metric_fu/metrics/flog/flog_spec.rb +7 -7
  73. data/spec/metric_fu/metrics/generator_spec.rb +21 -21
  74. data/spec/metric_fu/metrics/graph_spec.rb +9 -9
  75. data/spec/metric_fu/metrics/hotspots/hotspots_spec.rb +1 -1
  76. data/spec/metric_fu/metrics/rcov/rcov_spec.rb +8 -8
  77. data/spec/metric_fu/metrics/reek/reek_spec.rb +1 -1
  78. data/spec/metric_fu/metrics/saikuro/saikuro_spec.rb +5 -5
  79. data/spec/metric_fu/metrics/stats/stats_spec.rb +4 -4
  80. data/spec/metric_fu/reporter_spec.rb +41 -0
  81. data/spec/metric_fu/reporting/graphs/engines/gchart_spec.rb +7 -7
  82. data/spec/metric_fu/reporting/result_spec.rb +51 -0
  83. data/spec/run_spec.rb +167 -27
  84. data/spec/spec_helper.rb +1 -0
  85. data/spec/support/matcher_create_file.rb +7 -2
  86. data/spec/support/matcher_create_files.rb +41 -0
  87. data/spec/support/suite.rb +32 -0
  88. metadata +27 -6
  89. data/lib/metric_fu/reporting/report.rb +0 -111
  90. data/spec/metric_fu/reporting/report_spec.rb +0 -148
@@ -1,17 +1,22 @@
1
1
  MetricFu.metrics_require { 'rcov/rcov_grapher' }
2
2
  module MetricFu
3
3
  class RcovGchartGrapher < RcovGrapher
4
- def graph!
5
- url = Gchart.line(
6
- :size => GCHART_GRAPH_SIZE,
7
- :title => URI.escape("Rcov: code coverage"),
8
- :data => self.rcov_percent,
4
+ def title
5
+ "Rcov: code coverage"
6
+ end
7
+ def data
8
+ self.rcov_percent
9
+ end
10
+ def output_filename
11
+ 'rcov.png'
12
+ end
13
+ # overrides method
14
+ def y_axis_scale_options
15
+ {
9
16
  :max_value => 101,
10
17
  :axis_with_labels => 'x,y',
11
18
  :axis_labels => [self.labels.values, [0,20,40,60,80,100]],
12
- :format => 'file',
13
- :filename => File.join(MetricFu.output_directory, 'rcov.png')
14
- )
19
+ }
15
20
  end
16
21
  end
17
22
  end
@@ -1,20 +1,16 @@
1
1
  MetricFu.metrics_require { 'reek/reek_grapher' }
2
2
  module MetricFu
3
3
  class ReekBluffGrapher < ReekGrapher
4
- def graph!
5
- legend = @reek_count.keys.sort
6
- data = ""
7
- legend.each do |name|
8
- data += "g.data('#{name}', [#{@reek_count[name].join(',')}])\n"
4
+ def title
5
+ 'Reek: code smells'
6
+ end
7
+ def data
8
+ @reek_count.map do |name, count|
9
+ [name, count.join(',')]
9
10
  end
10
- content = <<-EOS
11
- #{BLUFF_DEFAULT_OPTIONS}
12
- g.title = 'Reek: code smells';
13
- #{data}
14
- g.labels = #{MultiJson.dump(@labels)};
15
- g.draw();
16
- EOS
17
- File.open(File.join(MetricFu.output_directory, 'reek.js'), 'w') {|f| f << content }
11
+ end
12
+ def output_filename
13
+ 'reek.js'
18
14
  end
19
15
  end
20
16
  end
@@ -1,25 +1,30 @@
1
1
  MetricFu.metrics_require { 'reek/reek_grapher' }
2
2
  module MetricFu
3
3
  class ReekGchartGrapher < ReekGrapher
4
- def graph!
5
- determine_y_axis_scale(@reek_count.values.flatten.uniq)
4
+ def title
5
+ "Reek: code smells"
6
+ end
7
+ def legend
8
+ @legend ||= @reek_count.keys.sort
9
+ end
10
+ def data
6
11
  values = []
7
- legend = @reek_count.keys.sort
8
12
  legend.collect {|k| values << @reek_count[k]}
9
-
10
- url = Gchart.line(
11
- :size => GCHART_GRAPH_SIZE,
12
- :title => URI.escape("Reek: code smells"),
13
- :data => values,
14
- :stacked => false,
15
- :bar_colors => COLORS,
16
- :legend => legend,
17
- :custom => "chdlp=t",
18
- :max_value => @max_value,
19
- :axis_with_labels => 'x,y',
20
- :axis_labels => [@labels.values, @yaxis],
21
- :format => 'file',
22
- :filename => File.join(MetricFu.output_directory, 'reek.png'))
13
+ values
14
+ end
15
+ def output_filename
16
+ 'reek.png'
17
+ end
18
+ def gchart_line_options
19
+ super.merge({
20
+ :bar_colors => COLORS,
21
+ :stacked => false,
22
+ :legend => legend,
23
+ :custom => 'chdlp=t',
24
+ })
25
+ end
26
+ def y_axis_scale_argument
27
+ @reek_count.values.flatten.uniq
23
28
  end
24
29
  end
25
30
  end
@@ -1,15 +1,16 @@
1
1
  MetricFu.metrics_require { 'roodi/roodi_grapher' }
2
2
  module MetricFu
3
3
  class RoodiBluffGrapher < RoodiGrapher
4
- def graph!
5
- content = <<-EOS
6
- #{BLUFF_DEFAULT_OPTIONS}
7
- g.title = 'Roodi: design problems';
8
- g.data('roodi', [#{@roodi_count.join(',')}]);
9
- g.labels = #{MultiJson.dump(@labels)};
10
- g.draw();
11
- EOS
12
- File.open(File.join(MetricFu.output_directory, 'roodi.js'), 'w') {|f| f << content }
4
+ def title
5
+ 'Roodi: design problems'
6
+ end
7
+ def data
8
+ [
9
+ ['roodi', @roodi_count.join(',')]
10
+ ]
11
+ end
12
+ def output_filename
13
+ 'roodi.js'
13
14
  end
14
15
  end
15
16
  end
@@ -1,17 +1,20 @@
1
1
  MetricFu.metrics_require { 'roodi/roodi_grapher' }
2
2
  module MetricFu
3
3
  class RoodiGchartGrapher < RoodiGrapher
4
- def graph!
5
- determine_y_axis_scale(@roodi_count)
6
- url = Gchart.line(
7
- :size => GCHART_GRAPH_SIZE,
8
- :title => URI.escape("Roodi: potential design problems"),
9
- :data => @roodi_count,
10
- :max_value => @max_value,
11
- :axis_with_labels => 'x,y',
12
- :axis_labels => [@labels.values, @yaxis],
13
- :format => 'file',
14
- :filename => File.join(MetricFu.output_directory, 'roodi.png'))
4
+ def title
5
+ "Roodi: potential design problems"
6
+ end
7
+ def data
8
+ @roodi_count
9
+ end
10
+ def output_filename
11
+ 'roodi.png'
12
+ end
13
+ def gchart_line_options
14
+ super
15
+ end
16
+ def y_axis_scale_argument
17
+ @roodi_count
15
18
  end
16
19
  end
17
20
  end
@@ -1,8 +1,6 @@
1
1
  module MetricFu
2
2
 
3
3
  class Saikuro < Generator
4
- include Rake::DSL if defined?(Rake::DSL) # rake 0.8.7 and 0.9.2 compatible
5
-
6
4
  def emit
7
5
  options_string = MetricFu.saikuro.inject("") do |options, option|
8
6
  option[0] == :input_directory ? options : options + "--#{option.join(' ')} "
@@ -12,36 +10,9 @@ module MetricFu
12
10
  options_string += "--input_directory #{input_dir} "
13
11
  end
14
12
 
15
- saikuro_bin= $:.map{|d| d+'/../bin/saikuro'}.select{|f| File.exists? f}.first || 'saikuro'
16
- mf_debug(capture_output do
17
- sh %{#{saikuro_bin} #{options_string}} do |ok, response|
18
- unless ok
19
- mf_log "Saikuro failed with exit status: #{response.exitstatus}"
20
- end
21
- end
22
- end)
23
- end
24
-
25
- def capture_output(&block)
26
- old_stdout = STDOUT.clone
27
- pipe_r, pipe_w = IO.pipe
28
- pipe_r.sync = true
29
- output = ""
30
- reader = Thread.new do
31
- begin
32
- loop do
33
- output << pipe_r.readpartial(1024)
34
- end
35
- rescue EOFError
36
- end
37
- end
38
- STDOUT.reopen(pipe_w)
39
- yield
40
- ensure
41
- STDOUT.reopen(old_stdout)
42
- pipe_w.close
43
- reader.join
44
- return output
13
+ command = %Q(mf-saikuro #{options_string})
14
+ mf_debug "** #{command}"
15
+ `#{command}`
45
16
  end
46
17
 
47
18
  def format_directories
@@ -186,14 +157,14 @@ module MetricFu
186
157
  while (line = @file_handle.readline) do
187
158
  return [] if line.nil? || line !~ /\S/
188
159
  element ||= nil
189
- if line.match /START/
160
+ if line.match(/START/)
190
161
  unless element.nil?
191
162
  @elements << element
192
163
  element = nil
193
164
  end
194
165
  line = @file_handle.readline
195
166
  element = Saikuro::ParsingElement.new(line)
196
- elsif line.match /END/
167
+ elsif line.match(/END/)
197
168
  @elements << element if element
198
169
  element = nil
199
170
  else
@@ -10,7 +10,7 @@ module MetricFu
10
10
 
11
11
  def analyze
12
12
  output = File.open(metric_directory + '/stats.txt').read
13
- lines = remove_noise(output)
13
+ lines = remove_noise(output).compact
14
14
 
15
15
  @stats = {}
16
16
 
@@ -34,6 +34,7 @@ module MetricFu
34
34
  end
35
35
 
36
36
  def set_global_stats(totals)
37
+ return if totals.nil?
37
38
  totals = totals.split(" ").find_all {|el| ! el.empty? }
38
39
  @stats[:codeLOC] = totals[0].match(/\d.*/)[0].to_i
39
40
  @stats[:testLOC] = totals[1].match(/\d.*/)[0].to_i
@@ -1,16 +1,17 @@
1
1
  MetricFu.metrics_require { 'stats/stats_grapher' }
2
2
  module MetricFu
3
3
  class StatsBluffGrapher < StatsGrapher
4
- def graph!
5
- content = <<-EOS
6
- #{BLUFF_DEFAULT_OPTIONS}
7
- g.title = 'Stats: LOC & LOT';
8
- g.data('LOC', [#{@loc_counts.join(',')}]);
9
- g.data('LOT', [#{@lot_counts.join(',')}])
10
- g.labels = #{MultiJson.dump(@labels)};
11
- g.draw();
12
- EOS
13
- File.open(File.join(MetricFu.output_directory, 'stats.js'), 'w') {|f| f << content }
4
+ def title
5
+ 'Stats: LOC & LOT'
6
+ end
7
+ def data
8
+ [
9
+ ['LOC', @loc_counts.join(',')],
10
+ ['LOT', @lot_counts.join(',')],
11
+ ]
12
+ end
13
+ def output_filename
14
+ 'stats.js'
14
15
  end
15
16
  end
16
17
  end
@@ -1,20 +1,27 @@
1
1
  MetricFu.metrics_require { 'stats/stats_grapher' }
2
2
  module MetricFu
3
3
  class StatsGchartGrapher < StatsGrapher
4
- def graph!
5
- determine_y_axis_scale(@loc_counts + @lot_counts)
6
- url = Gchart.line(
7
- :size => GCHART_GRAPH_SIZE,
8
- :title => URI.escape("Stats: LOC & LOT"),
9
- :data => [@loc_counts, @lot_counts],
10
- :bar_colors => COLORS[0..1],
11
- :legend => ['Lines of code', 'Lines of test'],
12
- :custom => "chdlp=t",
13
- :max_value => @max_value,
14
- :axis_with_labels => 'x,y',
15
- :axis_labels => [@labels.values, @yaxis],
16
- :format => 'file',
17
- :filename => File.join(MetricFu.output_directory, 'stats.png'))
4
+ def title
5
+ "Stats: LOC & LOT"
6
+ end
7
+ def data
8
+ [@loc_counts, @lot_counts]
9
+ end
10
+ def output_filename
11
+ 'stats.png'
12
+ end
13
+ def legend
14
+ ['Lines of code', 'Lines of test']
15
+ end
16
+ def gchart_line_options
17
+ super.merge({
18
+ :bar_colors => COLORS[0..1],
19
+ :legend => legend,
20
+ :custom => 'chdlp=t',
21
+ })
22
+ end
23
+ def y_axis_scale_argument
24
+ @loc_counts + @lot_counts
18
25
  end
19
26
  end
20
27
  end
@@ -0,0 +1,37 @@
1
+ module MetricFu
2
+ class Reporter
3
+ def initialize(formatters=nil)
4
+ @formatters = Array(formatters)
5
+ end
6
+
7
+ def start
8
+ notify :start
9
+ end
10
+
11
+ def finish
12
+ notify :finish
13
+ end
14
+
15
+ def start_metric(metric)
16
+ mf_log "** STARTING METRIC #{metric}"
17
+ notify :start_metric, metric
18
+ end
19
+
20
+ def finish_metric(metric)
21
+ mf_log "** ENDING METRIC #{metric}"
22
+ notify :finish_metric, metric
23
+ end
24
+
25
+ def display_results
26
+ notify :display_results
27
+ end
28
+
29
+ protected
30
+
31
+ def notify(event, *args)
32
+ @formatters.each do |formatter|
33
+ formatter.send(event, *args) if formatter.respond_to?(event)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -9,5 +9,25 @@ module MetricFu
9
9
  g.legend_font_size = "12px"
10
10
  g.marker_font_size = "10px"
11
11
  EOS
12
+ def graph!
13
+ title = send(:title)
14
+ data = send(:data)
15
+ labels = MultiJson.dump(@labels)
16
+ output_filename = send(:output_filename)
17
+ content = <<-EOS
18
+ #{BLUFF_DEFAULT_OPTIONS}
19
+ g.title = '#{title}';
20
+ #{build_data(data)}
21
+ g.labels = #{labels};
22
+ g.draw();
23
+ EOS
24
+ File.open(File.join(self.output_directory, output_filename), 'w') {|f| f << content }
25
+ end
26
+ private
27
+ def build_data(data)
28
+ Array(data).map do |label, datum|
29
+ "g.data('#{label}', [#{datum}]);"
30
+ end.join("\n")
31
+ end
12
32
  end
13
33
  end
@@ -2,9 +2,42 @@ module MetricFu
2
2
  module GchartGrapher
3
3
  COLORS = %w{009999 FF7400 A60000 008500 E6399B 344AD7 00B860 D5CCB9}
4
4
  GCHART_GRAPH_SIZE = "945x317" # maximum permitted image size is 300000 pixels
5
-
6
5
  NUMBER_OF_TICKS = 6
6
+
7
+ # @see gchart_line_options
8
+ def graph!
9
+ options = gchart_line_options.reject{|_,v|v.nil?}
10
+ Gchart.line(options)
11
+ end
12
+ # @note Some values are initialized as nil to maintain consistent
13
+ # key ordering for the tests. Any keys with nil values are removed
14
+ # before graphing
15
+ def gchart_line_options
16
+ {
17
+ :size => GCHART_GRAPH_SIZE,
18
+ :title => URI.encode(title),
19
+ :data => data,
20
+ :stacked => nil,
21
+ :bar_colors => nil,
22
+ :legend => nil,
23
+ :custom => nil,
24
+ :max_value => nil,
25
+ :axis_with_labels => nil,
26
+ :axis_labels => nil,
27
+ :format => 'file',
28
+ :filename => File.join(self.output_directory, output_filename),
29
+ }.merge(y_axis_scale_options)
30
+ end
31
+ def y_axis_scale_options
32
+ determine_y_axis_scale(y_axis_scale_argument)
33
+ {
34
+ :max_value => @max_value,
35
+ :axis_with_labels => 'x,y',
36
+ :axis_labels => [@labels.values, @yaxis],
37
+ }
38
+ end
7
39
  def determine_y_axis_scale(values)
40
+ values = Array(values)
8
41
  values.collect! {|val| val || 0.0 }
9
42
  if values.empty?
10
43
  @max_value = 10
@@ -17,13 +50,18 @@ module MetricFu
17
50
  @max_value = @yaxis.last
18
51
  end
19
52
  end
53
+ def y_axis_scale_argument
54
+ raise "#{__LINE__} in #{__FILE__} from #{caller.join('\n')}"
55
+ end
20
56
  end
21
57
 
22
58
  class Grapher
23
- include MetricFu::GchartGrapher
24
59
 
25
60
  def self.require_graphing_gem
26
- require 'gchart' if MetricFu.graph_engine == :gchart
61
+ if MetricFu.graph_engine == :gchart
62
+ require 'gchart'
63
+ include MetricFu::GchartGrapher
64
+ end
27
65
  rescue LoadError
28
66
  mf_log "#"*99 + "\n" +
29
67
  "If you want to use google charts for graphing, you'll need to install the googlecharts rubygem." +