nielsm-metric_fu 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,9 @@
1
+ require File.join(File.dirname(__FILE__), '../metric_fu/churn')
2
+
3
+ namespace :metrics do
4
+
5
+ desc "Which files change the most"
6
+ task :churn do
7
+ MetricFu.generate_churn_report
8
+ end
9
+ end
@@ -0,0 +1,54 @@
1
+ require 'fileutils'
2
+
3
+ begin
4
+ require 'rcov'
5
+ require 'rcov/rcovtask'
6
+ require 'spec/rake/spectask'
7
+
8
+ namespace :metrics do
9
+
10
+ COVERAGE_DIR = File.join(MetricFu::BASE_DIRECTORY, 'coverage')
11
+ COVERAGE_DATA_FILE = File.join(MetricFu::BASE_DIRECTORY, 'coverage.data')
12
+ SPEC_HTML_FILE = File.join(MetricFu::BASE_DIRECTORY, 'specs.html')
13
+
14
+ namespace :coverage do
15
+ rcov_output = COVERAGE_DIR
16
+
17
+ desc "Delete aggregate coverage data."
18
+ task(:clean) { rm_f("rcov_tmp", :verbose => false) }
19
+
20
+ desc "RCov task to generate report"
21
+ Rcov::RcovTask.new(:do => :clean) do |t|
22
+ FileUtils.mkdir_p(MetricFu::BASE_DIRECTORY) unless File.directory?(MetricFu::BASE_DIRECTORY)
23
+ t.test_files = FileList[*MetricFu.coverage[:test_files]]
24
+ t.rcov_opts = MetricFu.coverage[:rcov_opts]
25
+ t.output_dir = COVERAGE_DIR
26
+ # this line is a fix for Rails 2.1 relative loading issues
27
+ t.libs << 'test'
28
+ end
29
+ # TODO not sure what this improves but it requires the diff-lcs gem
30
+ # http://github.com/indirect/metric_fu/commit/b9c1cf75f09d5b531b388cd01661eb16b5126968#diff-1
31
+ # Spec::Rake::SpecTask.new(:do => :clean) do |t|
32
+ # FileUtils.mkdir_p(MetricFu::BASE_DIRECTORY) unless File.directory?(MetricFu::BASE_DIRECTORY)
33
+ # t.ruby_opts = ['-rtest/unit']
34
+ # t.spec_files = FileList['test/**/*_test.rb', 'spec/**/*spec.rb']
35
+ # t.spec_opts = ["--format", "html:#{SPEC_HTML_FILE}", "--diff"]
36
+ # t.rcov = true
37
+ # t.rcov_opts = ["--sort coverage", "--html", "--rails", "--exclude /gems/,/Library/"]
38
+ # t.rcov_dir = COVERAGE_DIR
39
+ # end
40
+ end
41
+
42
+ desc "Generate and open coverage report"
43
+ task :coverage => ['coverage:do'] do
44
+ system("open #{COVERAGE_DIR}/index.html") if MetricFu.open_in_browser?
45
+ end
46
+ end
47
+ rescue LoadError
48
+ if RUBY_PLATFORM =~ /java/
49
+ puts 'running in jruby - rcov tasks not available'
50
+ else
51
+ puts 'sudo gem install rcov # if you want the rcov tasks'
52
+
53
+ end
54
+ end
@@ -0,0 +1,6 @@
1
+ namespace :metrics do
2
+ desc "Generate code duplication report with flay"
3
+ task :flay do
4
+ MetricFu.generate_flay_report
5
+ end
6
+ end
@@ -0,0 +1,69 @@
1
+ begin
2
+
3
+ def flog(output, directory)
4
+ metric_dir = MetricFu::Flog::Generator.metric_dir
5
+ Dir.glob("#{directory}/**/*.rb").each do |filename|
6
+ output_dir = "#{metric_dir}/#{filename.split("/")[0..-2].join("/")}"
7
+ mkdir_p(output_dir, :verbose => false) unless File.directory?(output_dir)
8
+ if MetricFu::MD5Tracker.file_changed?(filename, metric_dir)
9
+ `flog #{filename} > #{metric_dir}/#{filename.split('.')[0]}.txt`
10
+ end
11
+ end
12
+ end
13
+
14
+ namespace :metrics do
15
+
16
+ task :flog => ['flog:all'] do
17
+ end
18
+
19
+ namespace :flog do
20
+ desc "Delete aggregate flog data."
21
+ task(:clean) { rm_rf(MetricFu::Flog.metric_dir, :verbose => false) }
22
+
23
+ desc "Flog code in app/models"
24
+ task :models do
25
+ flog "models", "app/models"
26
+ end
27
+
28
+ desc "Flog code in app/controllers"
29
+ task :controllers do
30
+ flog "controllers", "app/controllers"
31
+ end
32
+
33
+ desc "Flog code in app/helpers"
34
+ task :helpers do
35
+ flog "helpers", "app/helpers"
36
+ end
37
+
38
+ desc "Flog code in lib"
39
+ task :lib do
40
+ flog "lib", "lib"
41
+ end
42
+
43
+ desc "Generate a flog report from specified directories"
44
+ task :custom do
45
+ MetricFu::flog[:dirs_to_flog].each { |directory| flog(directory, directory) }
46
+ MetricFu.generate_flog_report
47
+ end
48
+
49
+ desc "Generate and open flog report"
50
+ if MetricFu::RAILS
51
+ task :all => [:models, :controllers, :helpers, :lib] do
52
+ MetricFu.generate_flog_report
53
+ end
54
+ else
55
+ task :all => [:custom] do
56
+ MetricFu.generate_flog_report
57
+ end
58
+ end
59
+
60
+ end
61
+
62
+ end
63
+ rescue LoadError
64
+ if RUBY_PLATFORM =~ /java/
65
+ puts 'running in jruby - flog tasks not available'
66
+ else
67
+ puts 'sudo gem install flog # if you want the flog tasks'
68
+ end
69
+ end
@@ -0,0 +1,24 @@
1
+ # only load configured metrics
2
+ MetricFu.metrics.each { |task| import "#{File.dirname(__FILE__)}/#{task}.rake" }
3
+
4
+ namespace :metrics do
5
+ if MetricFu::RAILS
6
+
7
+ desc "Generate coverage, cyclomatic complexity, flog, flay, railroad, reek, roodi, stats and churn reports"
8
+ task :all => MetricFu.metrics
9
+
10
+ task :set_testing_env do
11
+ RAILS_ENV = 'test'
12
+ end
13
+
14
+ desc "Generate metrics after migrating (for continuous integration)"
15
+ task :all_with_migrate => [:set_testing_env, "db:migrate", :all]
16
+
17
+ else
18
+
19
+ desc "Generate coverage, cyclomatic complexity, flog, flay, railroad and churn reports"
20
+ task :all => MetricFu.metrics
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,6 @@
1
+ require 'fileutils'
2
+ require 'rubygems'
3
+ require 'rake'
4
+
5
+ # Load rake files
6
+ import "#{File.dirname(__FILE__)}/metric_fu.rake"
@@ -0,0 +1,39 @@
1
+ namespace :metrics do
2
+
3
+ RAILROAD_DIR = File.join(MetricFu::BASE_DIRECTORY, 'railroad')
4
+ RAILROAD_FILE = File.join(RAILROAD_DIR, 'index.html')
5
+
6
+ task :railroad => ['railroad:all'] do
7
+ end
8
+
9
+ namespace :railroad do
10
+
11
+ desc "Create all railroad reports"
12
+ task :all => [:models, :controllers, :aasm] do
13
+ #system("open #{RAILROAD_INDEX}") if PLATFORM['darwin']
14
+ end
15
+
16
+ desc "Create a railroad models report"
17
+ task :models do
18
+ #mkdir_p(RAILROAD_DIR) unless File.directory?(RAILROAD_DIR)
19
+ `railroad -M -a -m -l -v | neato -Tpng > #{File.join(MetricFu::BASE_DIRECTORY,'model-diagram.png')}`
20
+ #`echo "<a href=\"railroad/models.png\">Model diagram</a><br />" >> #{RAILROAD_FILE}`
21
+ end
22
+
23
+ desc "Create a railroad controllers report"
24
+ task :controllers do
25
+ #mkdir_p(RAILROAD_DIR) unless File.directory?(RAILROAD_DIR)
26
+ `railroad -C -l -v | neato -Tpng > #{File.join(MetricFu::BASE_DIRECTORY,'controller-diagram.png')}`
27
+ #`echo "<a href=\"railroad/controllers.png\">Controller diagram</a><br />" >> #{RAILROAD_FILE}`
28
+ end
29
+
30
+ desc "Create a railroad acts_as_state_machine report"
31
+ task :aasm do
32
+ #mkdir_p(RAILROAD_DIR) unless File.directory?(RAILROAD_DIR)
33
+ `railroad -A -l -v | neato -Tpng > #{File.join(MetricFu::BASE_DIRECTORY,'aasm-diagram.png')}`
34
+ #`echo "<a href=\"railroad/aasm.png\">State machine diagram</a><br />" >> #{RAILROAD_FILE}`
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,6 @@
1
+ namespace :metrics do
2
+ desc "A code smell report using Reek"
3
+ task :reek do
4
+ MetricFu.generate_reek_report
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ namespace :metrics do
2
+
3
+ desc "A Ruby coding standards report using Roodi"
4
+ task :roodi do
5
+ MetricFu.generate_roodi_report
6
+ end
7
+ end
@@ -0,0 +1,35 @@
1
+ require 'fileutils'
2
+
3
+ namespace :metrics do
4
+
5
+ desc "A cyclomatic complexity report using Saikuro"
6
+ task :saikuro do
7
+ SAIKURO_DIR = File.join(MetricFu::BASE_DIRECTORY, 'saikuro')
8
+ SAIKURO = File.expand_path(File.join(File.dirname(__FILE__), '..', 'metric_fu', 'saikuro', 'saikuro.rb'))
9
+
10
+ raise "SAIKURO_OPTIONS is now MetricFu::SAIKURO_OPTIONS" if defined?(SAIKURO_OPTIONS)
11
+ options = { :output_directory => SAIKURO_DIR,
12
+ :input_directory => MetricFu::CODE_DIRS,
13
+ :cyclo => "",
14
+ :filter_cyclo => "0",
15
+ :warn_cyclo => "5",
16
+ :error_cyclo => "7"}
17
+
18
+ options.merge!(MetricFu::SAIKURO_OPTIONS) if defined?(MetricFu::SAIKURO_OPTIONS)
19
+ options_string = options.inject(""){ |o, h| o + "--#{h.join(' ')} " }
20
+
21
+ sh %{ruby "#{SAIKURO}" #{options_string}} do |ok, response|
22
+ unless ok
23
+ puts "Saikuro failed with exit status: #{response.exitstatus}"
24
+ exit 1
25
+ end
26
+ end
27
+
28
+ if File.exist? "#{SAIKURO_DIR}/index_cyclo.html"
29
+ mv "#{SAIKURO_DIR}/index_cyclo.html",
30
+ "#{SAIKURO_DIR}/index.html"
31
+ end
32
+
33
+ system("open #{SAIKURO_DIR}/index.html") if MetricFu.open_in_browser?
34
+ end
35
+ end
@@ -0,0 +1,14 @@
1
+ namespace :metrics do
2
+
3
+ STATS_DIR = File.join(MetricFu::BASE_DIRECTORY, 'stats')
4
+ STATS_FILE = File.join(STATS_DIR, 'index.html')
5
+
6
+ desc "A stats report"
7
+ task :stats do
8
+ mkdir_p(STATS_DIR) unless File.directory?(STATS_DIR)
9
+ `echo '<pre>' > #{STATS_FILE}`
10
+ `rake stats >> #{STATS_FILE}`
11
+ `echo '</pre>' >> #{STATS_FILE}`
12
+ system("open #{STATS_FILE}") if MetricFu.open_in_browser?
13
+ end
14
+ end
@@ -0,0 +1,22 @@
1
+ <html>
2
+ <head>
3
+ <title>Source Control Churn Results</title>
4
+ <style>
5
+ <%= inline_css("default.css") %>
6
+ </style>
7
+ </head>
8
+
9
+ <body>
10
+ <h1>Source Control Churn Results</h1>
11
+ <p>Files that change a lot in your project may be bad a sign.
12
+ This task uses your source control log to identify those files.
13
+ </p>
14
+ <table>
15
+ <tr><th>File Path</th><th>Times Changed</th></tr>
16
+ <% @changes.to_a.sort {|x,y| y[1] <=> x[1]}.each do |change| %>
17
+ <tr><td><%= change[0] %></td><td class='warning'><%= change[1] %></td></tr>
18
+ <% end %>
19
+ </table>
20
+ <p>Generated on <%= Time.now.localtime %></p>
21
+ </body>
22
+ </html>
@@ -0,0 +1,45 @@
1
+ body {
2
+ background-color: #efefef;
3
+ margin: 20px;
4
+ padding: 0;
5
+ font: 12px verdana, arial, helvetica;
6
+ }
7
+
8
+ table {
9
+ border-collapse: collapse;
10
+ border: 1px solid #666;
11
+ background: #fff;
12
+ margin-bottom: 20px;
13
+ }
14
+
15
+ table tr.light {
16
+ background: #fff;
17
+ }
18
+
19
+ table tr.dark {
20
+ background: #f9f9f9;
21
+ }
22
+
23
+ table td, table th {
24
+ padding: 4px 10px;
25
+ font-size: 13px;
26
+ }
27
+ table th {
28
+ text-align: center;
29
+ color: #fc0;
30
+ background: #336;
31
+ font-weight: bold;
32
+ border: #d0d0d0 1px solid;
33
+ }
34
+
35
+ table td {
36
+ border: #d0d0d0 1px solid;
37
+ }
38
+
39
+ table td.score {
40
+ text-align: right;
41
+ }
42
+
43
+ .warning {
44
+ background: yellow;
45
+ }
@@ -0,0 +1,30 @@
1
+ <html>
2
+ <head>
3
+ <title>Flay Results</title>
4
+ <style>
5
+ <%= inline_css("default.css") %>
6
+ </style>
7
+ </head>
8
+
9
+ <body>
10
+ <h1>Flay Results</h1>
11
+ <p><a href='http://ruby.sadi.st/Flay.html'>Flay</a> analyzes ruby code for structural similarities.</p>
12
+ <table>
13
+ <tr>
14
+ <th>Files</th>
15
+ <th>Matches</th>
16
+ </tr>
17
+ <% @matches.each_with_index do |match, count| %>
18
+ <tr class='<%= cycle("light", "dark", count) %>'>
19
+ <td>
20
+ <% match[1..-1].each do |filename| %>
21
+ <%= link_to_filename(*filename.split(":")) %><br>
22
+ <% end %>
23
+ </td>
24
+ <td><%= match.first %></td>
25
+ </tr>
26
+ <% end %>
27
+ </table>
28
+ <p>Generated on <%= Time.now.localtime %></p>
29
+ </body>
30
+ </html>
@@ -0,0 +1,38 @@
1
+ <html>
2
+ <head>
3
+ <title>Flog Reporter</title>
4
+ <style>
5
+ <%= inline_css("default.css") %>
6
+ </style>
7
+ </head>
8
+ <body>
9
+ <h1>Flog Results</h1>
10
+ <p><a href='http://ruby.sadi.st/Flog.html'>Flog</a> measures code complexity.</p>
11
+ <table>
12
+ <tr>
13
+ <th>File</th>
14
+ <th>Total score</th>
15
+ <th>Methods</th>
16
+ <th>Average score</th>
17
+ <th>Highest score</th>
18
+ </tr>
19
+ <% pages.sort {|x,y| y.highest_score <=> x.highest_score }.each_with_index do |page, count| %>
20
+ <tr class='<%= cycle("light", "dark", count) %>'>
21
+ <td><a href='<%= page.path %>'><%= page.path.sub('.html', '.rb') %></a></td>
22
+ <td class='score'><%= sprintf(SCORE_FORMAT, page.score) %></td>
23
+ <td class='score'><%= page.scanned_methods.length %></td>
24
+ <td class='score'><%= sprintf(SCORE_FORMAT, page.average_score) %></td>
25
+ <td class='score'><%= sprintf(SCORE_FORMAT, page.highest_score) %></td>
26
+ </tr>
27
+ <% end %>
28
+ <tr>
29
+ <td><strong>Totals</strong></td>
30
+ <td class='score'><strong><%= sprintf(SCORE_FORMAT, pages.inject(0){|sum, page| sum + page.score }) %></strong></td>
31
+ <td class='score'><strong><%= pages.inject(0){|sum, page| sum + page.scanned_methods.length } %></strong></td>
32
+ <td class='score'></td>
33
+ <td class='score'></td>
34
+ </tr>
35
+ </table>
36
+ <p>Generated on <%= Time.now.localtime %></p>
37
+ </body>
38
+ </html>
@@ -0,0 +1,25 @@
1
+ <html>
2
+ <head>
3
+ <style>
4
+ <%= inline_css("default.css") %>
5
+ </style>
6
+ </head>
7
+ <body>
8
+ <p>Score: <%= score %></p>
9
+ <% scanned_methods.each do |sm| %>
10
+ <p><strong><%= sm.name %> (<%= sm.score %>)</strong></p>
11
+ <table>
12
+ <tr>
13
+ <th>Score</th>
14
+ <th>Operator</th>
15
+ </tr>
16
+ <% sm.operators.each_with_index do |operator, count| %>
17
+ <tr class='<%= cycle("light", "dark", count) %>'>
18
+ <td class='score'><%= sprintf(SCORE_FORMAT, operator.score) %></td>
19
+ <td class='score'><%= operator.operator %></td>
20
+ </tr>
21
+ <% end %>
22
+ </table>
23
+ <% end %>
24
+ </body>
25
+ </html>