metric_fu 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,18 +8,39 @@ module MetricFu
8
8
 
9
9
  def analyze
10
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? }
11
+ lines = remove_noise(output)
12
+
17
13
  @stats = {}
14
+
15
+ set_global_stats(lines.pop)
16
+ set_granular_stats(lines)
17
+
18
+ @stats
19
+ end
20
+
21
+ def to_h
22
+ {:stats => @stats}
23
+ end
24
+
25
+ private
26
+
27
+ def remove_noise(output)
28
+ lines = output.split("\n")
29
+ lines = lines.find_all {|line| line[0].chr != "+" }
30
+ lines = lines.find_all {|line| line[0].chr != "(" }
31
+ lines.shift
32
+ lines
33
+ end
34
+
35
+ def set_global_stats(totals)
36
+ totals = totals.split(" ").find_all {|el| ! el.empty? }
18
37
  @stats[:codeLOC] = totals[0].match(/\d.*/)[0].to_i
19
38
  @stats[:testLOC] = totals[1].match(/\d.*/)[0].to_i
20
39
  @stats[:code_to_test_ratio] = totals[2].match(/1\:(\d.*)/)[1].to_f
21
-
22
- @stats[:lines] = output.map do |line|
40
+ end
41
+
42
+ def set_granular_stats(lines)
43
+ @stats[:lines] = lines.map do |line|
23
44
  elements = line.split("|")
24
45
  elements.map! {|el| el.strip }
25
46
  elements = elements.find_all {|el| ! el.empty? }
@@ -32,11 +53,6 @@ module MetricFu
32
53
  end
33
54
  info_line
34
55
  end
35
- @stats
36
- end
37
-
38
- def to_h
39
- {:stats => @stats}
40
56
  end
41
57
 
42
58
  end
@@ -83,4 +83,31 @@ module MetricFu
83
83
  File.open(File.join(MetricFu.output_directory, 'roodi.js'), 'w') {|f| f << content }
84
84
  end
85
85
  end
86
+
87
+ class StatsBluffGrapher < StatsGrapher
88
+ def graph!
89
+ content = <<-EOS
90
+ #{BLUFF_DEFAULT_OPTIONS}
91
+ g.title = 'Stats: LOC & LOT';
92
+ g.data('LOC', [#{@loc_counts.join(',')}]);
93
+ g.data('LOT', [#{@lot_counts.join(',')}])
94
+ g.labels = #{@labels.to_json};
95
+ g.draw();
96
+ EOS
97
+ File.open(File.join(MetricFu.output_directory, 'stats.js'), 'w') {|f| f << content }
98
+ end
99
+ end
100
+
101
+ class RailsBestPracticesBluffGrapher < RailsBestPracticesGrapher
102
+ def graph!
103
+ content = <<-EOS
104
+ #{BLUFF_DEFAULT_OPTIONS}
105
+ g.title = 'Rails Best Practices: design problems';
106
+ g.data('rails_best_practices', [#{@rails_best_practices_count.join(',')}]);
107
+ g.labels = #{@labels.to_json};
108
+ g.draw();
109
+ EOS
110
+ File.open(File.join(MetricFu.output_directory, 'rails_best_practices.js'), 'w') {|f| f << content }
111
+ end
112
+ end
86
113
  end
@@ -1,7 +1,7 @@
1
1
  module MetricFu
2
2
  module GchartGrapher
3
3
  COLORS = %w{009999 FF7400 A60000 008500 E6399B 344AD7 00B860 D5CCB9}
4
- GCHART_GRAPH_SIZE = "1000x300" # maximum permitted image size is 300000 pixels
4
+ GCHART_GRAPH_SIZE = "945x317" # maximum permitted image size is 300000 pixels
5
5
 
6
6
  NUMBER_OF_TICKS = 6
7
7
  def determine_y_axis_scale(values)
@@ -23,7 +23,7 @@ module MetricFu
23
23
  include MetricFu::GchartGrapher
24
24
 
25
25
  def self.require_graphing_gem
26
- require 'gchart'
26
+ require 'gchart' if MetricFu.graph_engine == :gchart
27
27
  rescue LoadError
28
28
  puts "#"*99 + "\n" +
29
29
  "If you want to use google charts for graphing, you'll need to install the googlecharts rubygem." +
@@ -55,7 +55,8 @@ module MetricFu
55
55
  :data => [@flog_average, @top_five_percent_average],
56
56
  :stacked => false,
57
57
  :bar_colors => COLORS[0..1],
58
- :legend => ['average', 'top 5%25 average'],
58
+ :legend => ['average', 'top 5% average'],
59
+ :custom => "chdlp=t",
59
60
  :max_value => @max_value,
60
61
  :axis_with_labels => 'x,y',
61
62
  :axis_labels => [@labels.values, @yaxis],
@@ -93,6 +94,7 @@ module MetricFu
93
94
  :stacked => false,
94
95
  :bar_colors => COLORS,
95
96
  :legend => legend,
97
+ :custom => "chdlp=t",
96
98
  :max_value => @max_value,
97
99
  :axis_with_labels => 'x,y',
98
100
  :axis_labels => [@labels.values, @yaxis],
@@ -115,4 +117,23 @@ module MetricFu
115
117
  :filename => File.join(MetricFu.output_directory, 'roodi.png'))
116
118
  end
117
119
  end
120
+
121
+ class StatsGchartGrapher < StatsGrapher
122
+ def graph!
123
+ determine_y_axis_scale(@loc_counts + @lot_counts)
124
+ url = Gchart.line(
125
+ :size => GCHART_GRAPH_SIZE,
126
+ :title => URI.escape("Stats: LOC & LOT"),
127
+ :data => [@loc_counts, @lot_counts],
128
+ :bar_colors => COLORS[0..1],
129
+ :legend => ['Lines of code', 'Lines of test'],
130
+ :custom => "chdlp=t",
131
+ :max_value => @max_value,
132
+ :axis_with_labels => 'x,y',
133
+ :axis_labels => [@labels.values, @yaxis],
134
+ :format => 'file',
135
+ :filename => File.join(MetricFu.output_directory, 'stats.png'))
136
+ end
137
+ end
138
+
118
139
  end
@@ -0,0 +1,25 @@
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
+ if metrics[:rails_best_practices] && metrics[:rails_best_practices][:problems]
15
+ size = metrics[:rails_best_practices][:problems].size
16
+ else
17
+ size = 0
18
+ end
19
+ @rails_best_practices_count.push(size)
20
+ @labels.update( { @labels.size => date })
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,23 @@
1
+
2
+ module MetricFu
3
+
4
+ class StatsGrapher < Grapher
5
+
6
+ attr_accessor :loc_counts, :lot_counts, :labels
7
+
8
+ def initialize
9
+ super
10
+ self.loc_counts = []
11
+ self.lot_counts = []
12
+ self.labels = {}
13
+ end
14
+
15
+ def get_metrics(metrics, date)
16
+ self.loc_counts.push(metrics[:stats][:codeLOC].to_i)
17
+ self.lot_counts.push(metrics[:stats][:testLOC].to_i)
18
+ self.labels.update( { self.labels.size => date })
19
+ end
20
+
21
+ end
22
+
23
+ end
data/lib/metric_fu.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'rake'
2
2
  require 'yaml'
3
+ require 'activesupport'
3
4
  # Load a few things to make our lives easier elsewhere.
4
5
  module MetricFu
5
6
  LIB_ROOT = File.dirname(__FILE__)
@@ -34,7 +34,7 @@
34
34
  <% @flog[:pages].each do |page| %>
35
35
  <h2 id="<%= page[:path].gsub(/[^a-z]+/, '_') %>"><%= link_to_filename page[:path] %></h2>
36
36
  <% page[:scanned_methods].each do |sm| %>
37
- <p><%= sm[:name] %></p>
37
+ <p><%= link_to_filename(page[:path],sm[:line],sm[:name]) %></p>
38
38
  <p>Total Score: <%=sm[:score]%></p>
39
39
  <table>
40
40
  <tr>
@@ -23,6 +23,9 @@
23
23
  <% end %>
24
24
  <% if @stats %>
25
25
  <li class='even failure'><a href="stats.html">Stats</a></li>
26
+ <% end %>
27
+ <% if @rails_best_practices %>
28
+ <li class='even failure'><a href="rails_best_practices.html">Rails Best Practices report</a></li>
26
29
  <% end %>
27
30
  </ul>
28
31
  <p>Generated on <%= Time.now.localtime %></p>
@@ -0,0 +1,27 @@
1
+ <h3>Rails Best Practices Results</h3>
2
+
3
+ <p><a href="http://github.com/flyerhzm/rails_best_practices">rails_best_practices</a> checks quality of rails app files according to ihower’s presentation from Kungfu RailsConf in Shanghai China.</p>
4
+
5
+ <% graph_name = 'rails_best_practices' %>
6
+ <% if MetricFu.configuration.graph_engine == :gchart %>
7
+ <img src="<%= graph_name %>.png?<%= Time.now.to_i %>" />
8
+ <% else %>
9
+ <canvas id="graph"></canvas>
10
+ <script language="javascript" src="<%= graph_name %>.js?<%= Time.now.to_i %>" type="text/javascript"></script>
11
+ <% end %>
12
+
13
+ <table>
14
+ <tr>
15
+ <th>File Path</th>
16
+ <th>Warning</th>
17
+ </tr>
18
+ <% count = 0 %>
19
+ <% @rails_best_practices[:problems].each do |problem| %>
20
+ <tr class='<%= cycle("light", "dark", count) %>'>
21
+ <td><%= link_to_filename(problem[:file], problem[:line]) %></td>
22
+ <td><%= problem[:problem] %></td>
23
+ </tr>
24
+ <% count += 1 %>
25
+ <% end %>
26
+ </table>
27
+ <p>Generated on <%= Time.now.localtime %></p>
@@ -1,4 +1,14 @@
1
1
  <h3>Rake Stats Results</h3>
2
+
3
+ <% graph_name = 'stats' %>
4
+ <% if MetricFu.configuration.graph_engine == :gchart %>
5
+ <img src="<%= graph_name %>.png?<%= Time.now.to_i %>" />
6
+ <% else %>
7
+ <canvas id="graph"></canvas>
8
+ <script language="javascript" src="<%= graph_name %>.js?<%= Time.now.to_i %>" type="text/javascript"></script>
9
+ <% end %>
10
+
11
+
2
12
  <p>Rails rake stats results.</p>
3
13
  <table>
4
14
  <tr>
@@ -32,6 +32,9 @@
32
32
  <% if @stats %>
33
33
  <p><a href="stats.html">Stats report</a></p>
34
34
  <% end %>
35
+ <% if @rails_best_practices %>
36
+ <p><a href="rails_best_practices.html">Rails Best Practices report</a></p>
37
+ <% end %>
35
38
  <p>Generated on <%= Time.now.localtime %></p>
36
39
  </body>
37
40
  </html>
@@ -0,0 +1,29 @@
1
+ <html>
2
+ <head>
3
+ <title>Rails Best Practices Results</title>
4
+ <style>
5
+ <%= inline_css("default.css") %>
6
+ </style>
7
+ </head>
8
+
9
+ <body>
10
+ <h1>Rails Best Practices Results</h1>
11
+ <a href="index.html">back to menu</a>
12
+ <p><a href="http://github.com/flyerhzm/rails_best_practices">rails_best_practices</a> checks quality of rails app files according to ihower’s presentation from Kungfu RailsConf in Shanghai China.</p>
13
+ <table>
14
+ <tr>
15
+ <th>File Path</th>
16
+ <th>Warning</th>
17
+ </tr>
18
+ <% count = 0 %>
19
+ <% @rails_best_practices[:problems].each do |problem| %>
20
+ <tr class='<%= cycle("light", "dark", count) %>'>
21
+ <td><%= problem[:file] %>:<%= problem[:line] %></td>
22
+ <td><%= problem[:problem] %></td>
23
+ </tr>
24
+ <% count += 1 %>
25
+ <% end %>
26
+ </table>
27
+ <p>Generated on <%= Time.now.localtime %></p>
28
+ </body>
29
+ </html>
@@ -87,7 +87,7 @@ describe MetricFu::Configuration do
87
87
 
88
88
  it 'should set @flay to {:dirs_to_flay => @code_dirs}' do
89
89
  @config.instance_variable_get(:@flay).
90
- should == {:dirs_to_flay => ['lib'], :minimum_score => 100}
90
+ should == {:dirs_to_flay => ['lib'], :filetypes=>["rb"], :minimum_score => 100}
91
91
  end
92
92
 
93
93
  it 'should set @flog to {:dirs_to_flog => @code_dirs}' do
@@ -134,7 +134,8 @@ describe MetricFu::Configuration do
134
134
  "--no-color",
135
135
  "--profile",
136
136
  "--rails",
137
- "--exclude /gems/,/Library/,/usr/,spec"]}
137
+ "--exclude /gems/,/Library/,/usr/,spec"],
138
+ :external => nil}
138
139
  end
139
140
 
140
141
  it 'should set @saikuro to { :output_directory => @scratch_directory + "/saikuro",
@@ -6,7 +6,7 @@ describe Flay do
6
6
  end
7
7
  describe "emit method" do
8
8
  it "should look at the dirs" do
9
- MetricFu::Configuration.run {|config| config.flay = { :dirs_to_flay => ['app', 'lib'] } }
9
+ MetricFu::Configuration.run {|config| config.flay = { :dirs_to_flay => ['app', 'lib'], :filetypes => ['rb'] } }
10
10
  File.stub!(:directory?).and_return(true)
11
11
  @flay = MetricFu::Flay.new('base_dir')
12
12
 
@@ -15,6 +15,17 @@ describe Flay do
15
15
  @flay.should_receive(:`).with("flay path/to/app path/to/lib")
16
16
  output = @flay.emit
17
17
  end
18
+
19
+ it "should look at the filetypes" do
20
+ MetricFu::Configuration.run {|config| config.flay = { :dirs_to_flay => ['lib'], :filetypes => ['rb','haml'] } }
21
+ File.stub!(:directory?).and_return(true)
22
+ @flay = MetricFu::Flay.new('base_dir')
23
+
24
+ Dir.should_receive(:[]).with(File.join("lib", "**/*.rb")).and_return("path/to/lib/stuff.rb")
25
+ Dir.should_receive(:[]).with(File.join("lib", "**/*.haml")).and_return("path/to/lib/view/blah.html.haml")
26
+ @flay.should_receive(:`).with("flay path/to/lib/stuff.rb path/to/lib/view/blah.html.haml")
27
+ output = @flay.emit
28
+ end
18
29
 
19
30
  it "should limit flay scores by the minimum_score" do
20
31
  MetricFu::Configuration.run {|config| config.flay = { :dirs_to_flay => [], :minimum_score => 99 } }
@@ -170,6 +170,30 @@ describe Flog do
170
170
  flog_page.scanned_methods.first.name.should == "SomeNamespace::UsersController#create"
171
171
  end
172
172
 
173
+ it "should find the line number for the method" do
174
+ text = <<-HERE
175
+ 157.9: flog total
176
+ 11.3: flog/method average
177
+
178
+ 34.8: SomeNamespace::UsersController#create lib/blah/file.rb:37
179
+ HERE
180
+ flog = MetricFu::Flog.new('base_dir')
181
+ flog_page = flog.parse(text)
182
+ flog_page.scanned_methods.first.line.should == "37"
183
+ end
184
+
185
+ it "should not add a line number, when none is found" do
186
+ text = <<-HERE
187
+ 157.9: flog total
188
+ 11.3: flog/method average
189
+
190
+ 34.8: UsersController#none lib/blah/file.rb
191
+ HERE
192
+ flog = MetricFu::Flog.new('base_dir')
193
+ flog_page = flog.parse(text)
194
+ flog_page.scanned_methods.first.line.should == nil
195
+ end
196
+
173
197
  it "should parse empty flog files" do
174
198
  text = ""
175
199
  flog = MetricFu::Flog.new('base_dir')
@@ -4,8 +4,11 @@ describe "Bluff graphers responding to #graph!" do
4
4
  it "should write chart file" do
5
5
  MetricFu.configuration
6
6
  graphs = {}
7
- MetricFu::AVAILABLE_GRAPHS.each do |graph|
8
- graphs[graph] = MetricFu.const_get("#{graph.to_s.capitalize}BluffGrapher").new
7
+ available_graphs = MetricFu::AVAILABLE_GRAPHS + [:stats]
8
+ available_graphs.each do |graph|
9
+ grapher_name = graph.to_s.gsub("MetricFu::",'').gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
10
+ grapher_name = grapher_name+"BluffGrapher"
11
+ graphs[graph] = MetricFu.const_get(grapher_name).new
9
12
  end
10
13
  graphs.each do |key, val|
11
14
  val.graph!
@@ -13,4 +16,4 @@ describe "Bluff graphers responding to #graph!" do
13
16
  lambda{ File.open(File.join(output_dir, "#{key.to_s.downcase}.js")) }.should_not raise_error
14
17
  end
15
18
  end
16
- end
19
+ end
@@ -1,4 +1,15 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
2
+ describe MetricFu::Grapher do
3
+ describe "require_graphing_gem" do
4
+ it "should give a warning if trying to use gchart but gem is not installed" do
5
+ MetricFu::Configuration.run {|config| config.graph_engine = :gchart}
6
+ MetricFu::Grapher.should_receive(:require).with('gchart').and_raise(LoadError)
7
+ MetricFu::Grapher.should_receive(:puts).with(/If you want to use google charts/)
8
+ MetricFu::Grapher.require_graphing_gem
9
+ end
10
+ end
11
+ end
12
+
2
13
  describe MetricFu::GchartGrapher do
3
14
  describe "determine_y_axis_scale" do
4
15
  it "should set defaults when empty array" do
@@ -21,7 +32,7 @@ end
21
32
 
22
33
  describe "Gchart graphers" do
23
34
  before :each do
24
- MetricFu.configuration
35
+ MetricFu::Configuration.run {|config| config.graph_engine = :gchart}
25
36
  end
26
37
 
27
38
  describe "FlayGchartGrapher graph! method" do
@@ -47,7 +58,8 @@ describe "Gchart graphers" do
47
58
  :title => URI.escape("Flog: code complexity"),
48
59
  :stacked => false,
49
60
  :bar_colors => MetricFu::GchartGrapher::COLORS[0..1],
50
- :legend => ['average', 'top 5%25 average'],
61
+ :legend => ['average', 'top 5% average'],
62
+ :custom => "chdlp=t",
51
63
  :axis_with_labels => 'x,y',
52
64
  :format => 'file',
53
65
  :filename => File.join(MetricFu.output_directory, 'flog.png'),
@@ -105,4 +117,23 @@ describe "Gchart graphers" do
105
117
  grapher.graph!
106
118
  end
107
119
  end
120
+
121
+ describe "StatsGchartGrapher graph! method" do
122
+ it "should set static values for graph" do
123
+ grapher = StatsGchartGrapher.new
124
+ expected = {
125
+ :size => MetricFu::GchartGrapher::GCHART_GRAPH_SIZE,
126
+ :title => URI.escape("Stats: LOC & LOT"),
127
+ :bar_colors => MetricFu::GchartGrapher::COLORS[0..1],
128
+ :legend => ['Lines of code', 'Lines of test'],
129
+ :custom => "chdlp=t",
130
+ :axis_with_labels => 'x,y',
131
+ :format => 'file',
132
+ :filename => File.join(MetricFu.output_directory, 'stats.png'),
133
+ }
134
+ Gchart.should_receive(:line).with(hash_including(expected))
135
+ grapher.graph!
136
+ end
137
+ end
138
+
108
139
  end