indirect-metric_fu 0.8.2 → 0.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.
- data/HISTORY +19 -0
- data/MIT-LICENSE +1 -1
- data/Manifest.txt +25 -0
- data/README +74 -21
- data/Rakefile +4 -10
- data/lib/metric_fu/base.rb +116 -9
- data/lib/metric_fu/churn.rb +8 -7
- data/lib/metric_fu/flay.rb +17 -0
- data/lib/metric_fu/flog.rb +129 -0
- data/lib/metric_fu/reek.rb +17 -0
- data/lib/metric_fu/roodi.rb +17 -0
- data/lib/metric_fu.rb +17 -3
- data/lib/tasks/churn.rake +1 -3
- data/lib/tasks/coverage.rake +19 -10
- data/lib/tasks/flay.rake +1 -4
- data/lib/tasks/flog.rake +10 -10
- data/lib/tasks/metric_fu.rake +8 -4
- data/lib/tasks/metric_fu.rb +1 -1
- data/lib/tasks/railroad.rake +39 -0
- data/lib/tasks/reek.rake +6 -0
- data/lib/tasks/roodi.rake +7 -0
- data/lib/tasks/saikuro.rake +1 -1
- data/lib/tasks/stats.rake +2 -3
- data/lib/templates/churn.html.erb +7 -4
- data/lib/templates/default.css +45 -0
- data/lib/templates/flay.html.erb +17 -10
- data/lib/templates/flog.html.erb +27 -15
- data/lib/templates/flog_page.html.erb +15 -3
- data/lib/templates/reek.html.erb +30 -0
- data/lib/templates/roodi.html.erb +26 -0
- data/spec/base_spec.rb +31 -9
- data/spec/churn_spec.rb +11 -4
- data/spec/config_spec.rb +110 -0
- data/spec/{flay_reporter_spec.rb → flay_spec.rb} +10 -3
- data/spec/flog_spec.rb +208 -0
- data/spec/md5_tracker_spec.rb +1 -3
- data/spec/reek_spec.rb +26 -0
- data/spec/spec_helper.rb +7 -3
- metadata +42 -18
- data/lib/metric_fu/flay_reporter.rb +0 -17
- data/lib/metric_fu/flog_reporter/base.rb +0 -49
- data/lib/metric_fu/flog_reporter/generator.rb +0 -39
- data/lib/metric_fu/flog_reporter/operator.rb +0 -10
- data/lib/metric_fu/flog_reporter/page.rb +0 -33
- data/lib/metric_fu/flog_reporter/scanned_method.rb +0 -28
- data/lib/metric_fu/flog_reporter.rb +0 -5
- data/lib/templates/churn.css +0 -38
- data/lib/templates/flay.css +0 -38
- data/lib/templates/flog.css +0 -39
- data/spec/flog_reporter/base_spec.rb +0 -69
@@ -0,0 +1,129 @@
|
|
1
|
+
module MetricFu
|
2
|
+
|
3
|
+
def self.generate_flog_report
|
4
|
+
Flog::Generator.generate_report
|
5
|
+
system("open #{Flog::Generator.metric_dir}/index.html") if open_in_browser?
|
6
|
+
end
|
7
|
+
|
8
|
+
module Flog
|
9
|
+
class Generator < Base::Generator
|
10
|
+
def generate_report
|
11
|
+
@base_dir = self.class.metric_dir
|
12
|
+
pages = []
|
13
|
+
flog_results.each do |filename|
|
14
|
+
page = Base.parse(open(filename, "r") { |f| f.read })
|
15
|
+
if page
|
16
|
+
page.filename = filename
|
17
|
+
pages << page
|
18
|
+
end
|
19
|
+
end
|
20
|
+
generate_pages(pages)
|
21
|
+
end
|
22
|
+
|
23
|
+
def generate_pages(pages)
|
24
|
+
pages.each do |page|
|
25
|
+
unless MetricFu::MD5Tracker.file_already_counted?(page.filename)
|
26
|
+
generate_page(page)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
save_html(ERB.new(File.read(template_file)).result(binding))
|
30
|
+
end
|
31
|
+
|
32
|
+
def generate_page(page)
|
33
|
+
save_html(page.to_html, page.path)
|
34
|
+
end
|
35
|
+
|
36
|
+
def flog_results
|
37
|
+
Dir.glob("#{@base_dir}/**/*.txt")
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.template_name
|
41
|
+
"flog"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
SCORE_FORMAT = "%0.2f"
|
46
|
+
|
47
|
+
class Base
|
48
|
+
METHOD_LINE_REGEX = /([A-Za-z]+#.*):\s\((\d+\.\d+)\)/
|
49
|
+
OPERATOR_LINE_REGEX = /\s+(\d+\.\d+):\s(.*)$/
|
50
|
+
|
51
|
+
class << self
|
52
|
+
|
53
|
+
def parse(text)
|
54
|
+
score = text[/\w+ = (\d+\.\d+)/, 1]
|
55
|
+
return nil unless score
|
56
|
+
page = Page.new(score)
|
57
|
+
|
58
|
+
text.each_line do |method_line|
|
59
|
+
if match = method_line.match(METHOD_LINE_REGEX)
|
60
|
+
page.scanned_methods << ScannedMethod.new(match[1], match[2])
|
61
|
+
end
|
62
|
+
|
63
|
+
if match = method_line.match(OPERATOR_LINE_REGEX)
|
64
|
+
return if page.scanned_methods.empty?
|
65
|
+
page.scanned_methods.last.operators << Operator.new(match[1], match[2])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
page
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class Page < MetricFu::Base::Generator
|
75
|
+
attr_accessor :filename, :score, :scanned_methods
|
76
|
+
|
77
|
+
def initialize(score, scanned_methods = [])
|
78
|
+
@score = score.to_f
|
79
|
+
@scanned_methods = scanned_methods
|
80
|
+
end
|
81
|
+
|
82
|
+
def path
|
83
|
+
@path ||= File.basename(filename, ".txt") + '.html'
|
84
|
+
end
|
85
|
+
|
86
|
+
def to_html
|
87
|
+
ERB.new(File.read(template_file)).result(binding)
|
88
|
+
end
|
89
|
+
|
90
|
+
def average_score
|
91
|
+
return 0 if scanned_methods.length == 0
|
92
|
+
sum = 0
|
93
|
+
scanned_methods.each do |m|
|
94
|
+
sum += m.score
|
95
|
+
end
|
96
|
+
sum / scanned_methods.length
|
97
|
+
end
|
98
|
+
|
99
|
+
def highest_score
|
100
|
+
scanned_methods.inject(0) do |highest, m|
|
101
|
+
m.score > highest ? m.score : highest
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def template_name
|
106
|
+
'flog_page'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class Operator
|
111
|
+
attr_accessor :score, :operator
|
112
|
+
|
113
|
+
def initialize(score, operator)
|
114
|
+
@score = score.to_f
|
115
|
+
@operator = operator
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class ScannedMethod
|
120
|
+
attr_accessor :name, :score, :operators
|
121
|
+
|
122
|
+
def initialize(name, score, operators = [])
|
123
|
+
@name = name
|
124
|
+
@score = score.to_f
|
125
|
+
@operators = operators
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module MetricFu
|
2
|
+
|
3
|
+
def self.generate_reek_report
|
4
|
+
Reek.generate_report
|
5
|
+
system("open #{Reek.metric_dir}/index.html") if open_in_browser?
|
6
|
+
end
|
7
|
+
|
8
|
+
class Reek < Base::Generator
|
9
|
+
|
10
|
+
def analyze
|
11
|
+
files_to_reek = MetricFu.reek[:dirs_to_reek].map{|dir| Dir[File.join(dir, "**/*.rb")] }
|
12
|
+
output = `reek #{files_to_reek.join(" ")}`
|
13
|
+
@matches = output.chomp.split("\n\n").map{|m| m.split("\n") }
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module MetricFu
|
2
|
+
|
3
|
+
def self.generate_roodi_report
|
4
|
+
MetricFu::Roodi.generate_report
|
5
|
+
system("open #{Roodi.metric_dir}/index.html") if open_in_browser?
|
6
|
+
end
|
7
|
+
|
8
|
+
class Roodi < Base::Generator
|
9
|
+
|
10
|
+
def analyze
|
11
|
+
files_to_analyze = MetricFu.roodi[:dirs_to_roodi].map{|dir| Dir[File.join(dir, "**/*.rb")] }
|
12
|
+
output = `roodi #{files_to_analyze.join(" ")}`
|
13
|
+
@matches = output.chomp.split("\n").map{|m| m.split(" - ") }
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
data/lib/metric_fu.rb
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
module MetricFu
|
2
|
+
TEMPLATE_DIR = File.join(File.dirname(__FILE__), '..', 'templates')
|
3
|
+
BASE_DIRECTORY = ENV['CC_BUILD_ARTIFACTS'] || 'tmp/metric_fu'
|
4
|
+
RAILS = File.exist?("config/environment.rb")
|
5
|
+
|
6
|
+
if RAILS
|
7
|
+
CODE_DIRS = ['app', 'lib']
|
8
|
+
else
|
9
|
+
CODE_DIRS = ['lib']
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Load metric_fu base class
|
14
|
+
require File.join(File.dirname(__FILE__), 'metric_fu', 'base')
|
15
|
+
# Load metrics tasks
|
16
|
+
require "rake"
|
17
|
+
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].each { |ext| load ext }
|
data/lib/tasks/churn.rake
CHANGED
@@ -4,8 +4,6 @@ namespace :metrics do
|
|
4
4
|
|
5
5
|
desc "Which files change the most"
|
6
6
|
task :churn do
|
7
|
-
|
8
|
-
MetricFu::Churn.generate_report(churn_dir, defined?(MetricFu::CHURN_OPTIONS) ? MetricFu::CHURN_OPTIONS : {} )
|
9
|
-
system("open #{churn_dir}/index.html") if PLATFORM['darwin']
|
7
|
+
MetricFu.generate_churn_report
|
10
8
|
end
|
11
9
|
end
|
data/lib/tasks/coverage.rake
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'fileutils'
|
2
|
-
|
3
2
|
begin
|
4
3
|
require 'rcov'
|
5
4
|
require 'rcov/rcovtask'
|
@@ -18,20 +17,30 @@ begin
|
|
18
17
|
task(:clean) { rm_f("rcov_tmp", :verbose => false) }
|
19
18
|
|
20
19
|
desc "RCov task to generate report"
|
21
|
-
|
20
|
+
Rcov::RcovTask.new(:do => :clean) do |t|
|
22
21
|
FileUtils.mkdir_p(MetricFu::BASE_DIRECTORY) unless File.directory?(MetricFu::BASE_DIRECTORY)
|
23
|
-
t.
|
24
|
-
t.
|
25
|
-
t.
|
26
|
-
|
27
|
-
t.
|
28
|
-
t.rcov_dir = COVERAGE_DIR
|
22
|
+
t.test_files = FileList[*MetricFu.coverage[:test_files]]
|
23
|
+
t.rcov_opts = MetricFu.coverage[:rcov_opts]
|
24
|
+
t.output_dir = COVERAGE_DIR
|
25
|
+
# this line is a fix for Rails 2.1 relative loading issues
|
26
|
+
t.libs << 'test'
|
29
27
|
end
|
28
|
+
# TODO not sure what this improves but it requires the diff-lcs gem
|
29
|
+
# http://github.com/indirect/metric_fu/commit/b9c1cf75f09d5b531b388cd01661eb16b5126968#diff-1
|
30
|
+
# Spec::Rake::SpecTask.new(:do => :clean) do |t|
|
31
|
+
# FileUtils.mkdir_p(MetricFu::BASE_DIRECTORY) unless File.directory?(MetricFu::BASE_DIRECTORY)
|
32
|
+
# t.ruby_opts = ['-rtest/unit']
|
33
|
+
# t.spec_files = FileList['test/**/*_test.rb', 'spec/**/*spec.rb']
|
34
|
+
# t.spec_opts = ["--format", "html:#{SPEC_HTML_FILE}", "--diff"]
|
35
|
+
# t.rcov = true
|
36
|
+
# t.rcov_opts = ["--sort coverage", "--html", "--rails", "--exclude /gems/,/Library/"]
|
37
|
+
# t.rcov_dir = COVERAGE_DIR
|
38
|
+
# end
|
30
39
|
end
|
31
40
|
|
32
41
|
desc "Generate and open coverage report"
|
33
42
|
task :coverage => ['coverage:do'] do
|
34
|
-
system("open #{COVERAGE_DIR}/index.html") if
|
43
|
+
system("open #{COVERAGE_DIR}/index.html") if MetricFu.open_in_browser?
|
35
44
|
end
|
36
45
|
end
|
37
46
|
rescue LoadError
|
@@ -41,4 +50,4 @@ rescue LoadError
|
|
41
50
|
puts 'sudo gem install rcov # if you want the rcov tasks'
|
42
51
|
|
43
52
|
end
|
44
|
-
end
|
53
|
+
end
|
data/lib/tasks/flay.rake
CHANGED
@@ -1,9 +1,6 @@
|
|
1
|
-
FLAY_DIR = File.join(MetricFu::BASE_DIRECTORY, 'flay')
|
2
|
-
|
3
1
|
namespace :metrics do
|
4
2
|
desc "Generate code duplication report with flay"
|
5
3
|
task :flay do
|
6
|
-
MetricFu
|
7
|
-
system("open #{FLAY_DIR}/index.html") if PLATFORM['darwin']
|
4
|
+
MetricFu.generate_flay_report
|
8
5
|
end
|
9
6
|
end
|
data/lib/tasks/flog.rake
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
begin
|
2
|
-
FLOG_DIR = File.join(MetricFu::BASE_DIRECTORY, 'flog')
|
3
2
|
|
4
3
|
def flog(output, directory)
|
4
|
+
metric_dir = MetricFu::Flog::Generator.metric_dir
|
5
5
|
Dir.glob("#{directory}/**/*.rb").each do |filename|
|
6
|
-
output_dir = "#{
|
6
|
+
output_dir = "#{metric_dir}/#{filename.split("/")[0..-2].join("/")}"
|
7
7
|
mkdir_p(output_dir, :verbose => false) unless File.directory?(output_dir)
|
8
|
-
|
8
|
+
if MetricFu::MD5Tracker.file_changed?(filename, metric_dir)
|
9
|
+
`flog #{filename} > #{metric_dir}/#{filename.split('.')[0]}.txt`
|
10
|
+
end
|
9
11
|
end
|
10
12
|
end
|
11
13
|
|
@@ -16,7 +18,7 @@ begin
|
|
16
18
|
|
17
19
|
namespace :flog do
|
18
20
|
desc "Delete aggregate flog data."
|
19
|
-
task(:clean) { rm_rf(
|
21
|
+
task(:clean) { rm_rf(MetricFu::Flog.metric_dir, :verbose => false) }
|
20
22
|
|
21
23
|
desc "Flog code in app/models"
|
22
24
|
task :models do
|
@@ -40,20 +42,18 @@ begin
|
|
40
42
|
|
41
43
|
desc "Generate a flog report from specified directories"
|
42
44
|
task :custom do
|
43
|
-
MetricFu::
|
44
|
-
MetricFu
|
45
|
+
MetricFu::flog[:dirs_to_flog].each { |directory| flog(directory, directory) }
|
46
|
+
MetricFu.generate_flog_report
|
45
47
|
end
|
46
48
|
|
47
49
|
desc "Generate and open flog report"
|
48
50
|
if MetricFu::RAILS
|
49
51
|
task :all => [:models, :controllers, :helpers, :lib] do
|
50
|
-
MetricFu
|
51
|
-
system("open #{FLOG_DIR}/index.html") if PLATFORM['darwin']
|
52
|
+
MetricFu.generate_flog_report
|
52
53
|
end
|
53
54
|
else
|
54
55
|
task :all => [:custom] do
|
55
|
-
MetricFu
|
56
|
-
system("open #{FLOG_DIR}/index.html") if PLATFORM['darwin']
|
56
|
+
MetricFu.generate_flog_report
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
data/lib/tasks/metric_fu.rake
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
+
# only load configured metrics
|
2
|
+
MetricFu.metrics.each { |task| import "#{File.dirname(__FILE__)}/#{task}.rake" }
|
3
|
+
|
1
4
|
namespace :metrics do
|
2
5
|
if MetricFu::RAILS
|
3
6
|
|
4
|
-
desc "Generate coverage, cyclomatic complexity, flog,
|
5
|
-
task :all =>
|
7
|
+
desc "Generate coverage, cyclomatic complexity, flog, flay, railroad, reek, roodi, stats and churn reports"
|
8
|
+
task :all => MetricFu.metrics
|
6
9
|
|
7
10
|
task :set_testing_env do
|
8
11
|
RAILS_ENV = 'test'
|
@@ -13,8 +16,9 @@ namespace :metrics do
|
|
13
16
|
|
14
17
|
else
|
15
18
|
|
16
|
-
desc "Generate coverage, cyclomatic complexity, flog,
|
17
|
-
task :all =>
|
19
|
+
desc "Generate coverage, cyclomatic complexity, flog, flay, railroad and churn reports"
|
20
|
+
task :all => MetricFu.metrics
|
18
21
|
|
19
22
|
end
|
23
|
+
|
20
24
|
end
|
data/lib/tasks/metric_fu.rb
CHANGED
@@ -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
|
data/lib/tasks/reek.rake
ADDED
data/lib/tasks/saikuro.rake
CHANGED
data/lib/tasks/stats.rake
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
namespace :metrics do
|
2
|
-
|
3
2
|
STATS_DIR = File.join(MetricFu::BASE_DIRECTORY, 'stats')
|
4
3
|
STATS_FILE = File.join(STATS_DIR, 'index.html')
|
5
4
|
|
@@ -9,6 +8,6 @@ namespace :metrics do
|
|
9
8
|
`echo '<pre>' > #{STATS_FILE}`
|
10
9
|
`rake stats >> #{STATS_FILE}`
|
11
10
|
`echo '</pre>' >> #{STATS_FILE}`
|
12
|
-
system("open #{STATS_FILE}") if
|
11
|
+
system("open #{STATS_FILE}") if MetricFu.open_in_browser?
|
13
12
|
end
|
14
|
-
end
|
13
|
+
end if MetricFu::RAILS
|
@@ -2,18 +2,21 @@
|
|
2
2
|
<head>
|
3
3
|
<title>Source Control Churn Results</title>
|
4
4
|
<style>
|
5
|
-
<%=
|
5
|
+
<%= inline_css("default.css") %>
|
6
6
|
</style>
|
7
7
|
</head>
|
8
8
|
|
9
9
|
<body>
|
10
10
|
<h1>Source Control Churn Results</h1>
|
11
|
-
<
|
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>
|
12
15
|
<tr><th>File Path</th><th>Times Changed</th></tr>
|
13
16
|
<% @changes.to_a.sort {|x,y| y[1] <=> x[1]}.each do |change| %>
|
14
17
|
<tr><td><%= change[0] %></td><td class='warning'><%= change[1] %></td></tr>
|
15
18
|
<% end %>
|
16
|
-
|
17
19
|
</table>
|
20
|
+
<p>Generated on <%= Time.now.localtime %></p>
|
18
21
|
</body>
|
19
|
-
</html>
|
22
|
+
</html>
|