metric_fu 1.3.0 → 1.4.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 +13 -1
- data/README +26 -0
- data/Rakefile +2 -1
- data/lib/base/configuration.rb +13 -6
- data/lib/base/graph.rb +1 -1
- data/lib/base/report.rb +1 -1
- data/lib/generators/flay.rb +5 -1
- data/lib/generators/flog.rb +8 -3
- data/lib/generators/rails_best_practices.rb +31 -0
- data/lib/generators/rcov.rb +28 -31
- data/lib/generators/roodi.rb +2 -1
- data/lib/generators/saikuro.rb +67 -47
- data/lib/generators/stats.rb +29 -13
- data/lib/graphs/engines/bluff.rb +27 -0
- data/lib/graphs/engines/gchart.rb +24 -3
- data/lib/graphs/rails_best_practices_grapher.rb +25 -0
- data/lib/graphs/stats_grapher.rb +23 -0
- data/lib/metric_fu.rb +1 -0
- data/lib/templates/awesome/flog.html.erb +1 -1
- data/lib/templates/awesome/index.html.erb +3 -0
- data/lib/templates/awesome/rails_best_practices.html.erb +27 -0
- data/lib/templates/awesome/stats.html.erb +10 -0
- data/lib/templates/standard/index.html.erb +3 -0
- data/lib/templates/standard/rails_best_practices.html.erb +29 -0
- data/spec/base/configuration_spec.rb +3 -2
- data/spec/generators/flay_spec.rb +12 -1
- data/spec/generators/flog_spec.rb +24 -0
- data/spec/graphs/engines/bluff_spec.rb +6 -3
- data/spec/graphs/engines/gchart_spec.rb +33 -2
- data/spec/graphs/reek_grapher_spec.rb +1 -0
- data/spec/resources/yml/20090630.yml +69 -0
- metadata +210 -52
- data/Manifest.txt +0 -25
data/HISTORY
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
=== MetricFu 1.4.0 / 2010-06-19
|
2
|
+
|
3
|
+
* Added support for rails_best_practices gem - Richard Huang
|
4
|
+
* Added rails stats graphing -- Josh Cronemeyer
|
5
|
+
* Parameterize the filetypes for flay. By default flay supports haml as well as rb and has a plugin ability for other filetypes. - bfabry
|
6
|
+
* Support for Flog 2.4.0 line numbers - Dan Mayer
|
7
|
+
* Saikuro multi input directory patch - Spencer Dillard and Dan Mayer
|
8
|
+
* Can now parse rcov analysis file coming from multiple sources with an rcov :external option in the config. - Tarsoly András
|
9
|
+
* Fixed open file handles problem in the Saikuro analyzer - aselder, erebor
|
10
|
+
* Fix some problems with the google charts - Chris Griego
|
11
|
+
* Stop showing the googlecharts warning if you are not using google charts.
|
12
|
+
|
1
13
|
=== MetricFu 1.3.0 / 2010-01-26
|
2
14
|
|
3
15
|
* Flay can be configured to ignore scores below a threshold (by default it ignores scores less than 100)
|
@@ -171,7 +183,7 @@
|
|
171
183
|
|
172
184
|
=== MetricFu 0.5.1 / 2008-04-25
|
173
185
|
|
174
|
-
* Fixed bug with Saikuro report generation
|
186
|
+
* Fixed bug with Saikuro report generation - thanks Toby Tripp
|
175
187
|
|
176
188
|
=== MetricFu 0.5.0 / 2008-04-25
|
177
189
|
|
data/README
CHANGED
@@ -1 +1,27 @@
|
|
1
1
|
See http://metric-fu.rubyforge.org/ for documentation, or the HISTORY file for a change log.
|
2
|
+
|
3
|
+
How to contribute:
|
4
|
+
|
5
|
+
1. Fork metric_fu on github.
|
6
|
+
2. 'gem install metric_fu --development' #to get development dependencies
|
7
|
+
3. Run the tests ('rake')
|
8
|
+
4. Run metric_fu on itself ('rake metrics:all')
|
9
|
+
5. Make the changes you want and back them up with tests.
|
10
|
+
6. Make sure two important rake tests still run ('rake' and 'rake metrics:all')
|
11
|
+
7. Commit and send me a pull request with details as to what has been changed.
|
12
|
+
|
13
|
+
Extra Credit:
|
14
|
+
1. Make sure your changes work in 1.8.7, Ruby Enterprise Edition, and 1.9.1 (Hint use 'rvm' to help install multiple rubies)
|
15
|
+
2. Post to the Google group explaining what you did and why you did it (I don't merge things in immediately so others might want to use what you've done).
|
16
|
+
3. Update the documentation (web page inside the 'home_page' folder)
|
17
|
+
4. Update the History and give yourself credit.
|
18
|
+
|
19
|
+
|
20
|
+
The more of the above steps you do the easier it will be for me to merge in which will greatly increase you chances of getting your changes accepted.
|
21
|
+
If you want to do something really radical (which will touch over %30 of all the files) you might what to ask the Google group first to see if anyone is interested in your change before you spend a lot of time on it.
|
22
|
+
|
23
|
+
Resources:
|
24
|
+
Homepage: http://metric-fu.rubyforge.org/
|
25
|
+
Github: http://github.com/jscruggs/metric_fu
|
26
|
+
Google Group: http://groups.google.com/group/metric_fu
|
27
|
+
My Blog: http://jakescruggs.blogspot.com/
|
data/Rakefile
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
$LOAD_PATH << '.'
|
1
2
|
require 'rake'
|
2
3
|
require 'rake/rdoctask'
|
3
4
|
require 'spec/rake/spectask'
|
@@ -12,4 +13,4 @@ MetricFu::Configuration.run do |config|
|
|
12
13
|
config.template_class = AwesomeTemplate
|
13
14
|
end
|
14
15
|
|
15
|
-
task :default =>
|
16
|
+
task :default => :spec
|
data/lib/base/configuration.rb
CHANGED
@@ -8,7 +8,7 @@ module MetricFu
|
|
8
8
|
AVAILABLE_METRICS = [:churn, :flog, :flay, :reek,
|
9
9
|
:roodi, :saikuro, :rcov]
|
10
10
|
|
11
|
-
AVAILABLE_GRAPHS = [:flog, :flay, :reek, :roodi, :rcov]
|
11
|
+
AVAILABLE_GRAPHS = [:flog, :flay, :reek, :roodi, :rcov, :rails_best_practices]
|
12
12
|
AVAILABLE_GRAPH_ENGINES = [:gchart, :bluff]
|
13
13
|
|
14
14
|
# The @@configuration class variable holds a global type configuration
|
@@ -110,7 +110,8 @@ module MetricFu
|
|
110
110
|
set_graphs
|
111
111
|
set_code_dirs
|
112
112
|
@flay = { :dirs_to_flay => @code_dirs,
|
113
|
-
:minimum_score => 100
|
113
|
+
:minimum_score => 100,
|
114
|
+
:filetypes => ['rb'] }
|
114
115
|
@flog = { :dirs_to_flog => @code_dirs }
|
115
116
|
@reek = { :dirs_to_reek => @code_dirs }
|
116
117
|
@roodi = { :dirs_to_roodi => @code_dirs }
|
@@ -132,8 +133,10 @@ module MetricFu
|
|
132
133
|
"--no-color",
|
133
134
|
"--profile",
|
134
135
|
"--rails",
|
135
|
-
"--exclude /gems/,/Library/,/usr/,spec"]
|
136
|
-
|
136
|
+
"--exclude /gems/,/Library/,/usr/,spec"],
|
137
|
+
:external => nil
|
138
|
+
}
|
139
|
+
@rails_best_practices = {}
|
137
140
|
@file_globs_to_ignore = []
|
138
141
|
|
139
142
|
@graph_engine = :bluff # can be :bluff or :gchart
|
@@ -151,14 +154,18 @@ module MetricFu
|
|
151
154
|
# running within rails.
|
152
155
|
def set_metrics
|
153
156
|
if rails?
|
154
|
-
@metrics = MetricFu::AVAILABLE_METRICS + [:stats]
|
157
|
+
@metrics = MetricFu::AVAILABLE_METRICS + [:stats, :rails_best_practices]
|
155
158
|
else
|
156
159
|
@metrics = MetricFu::AVAILABLE_METRICS
|
157
160
|
end
|
158
161
|
end
|
159
162
|
|
160
163
|
def set_graphs
|
161
|
-
|
164
|
+
if rails?
|
165
|
+
@graphs = MetricFu::AVAILABLE_GRAPHS + [:stats]
|
166
|
+
else
|
167
|
+
@graphs = MetricFu::AVAILABLE_GRAPHS
|
168
|
+
end
|
162
169
|
end
|
163
170
|
|
164
171
|
# Add the 'app' directory if we're running within rails.
|
data/lib/base/graph.rb
CHANGED
@@ -13,7 +13,7 @@ module MetricFu
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def add(graph_type, graph_engine)
|
16
|
-
grapher_name = graph_type.to_s.
|
16
|
+
grapher_name = graph_type.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase } + graph_engine.to_s.capitalize + "Grapher"
|
17
17
|
self.clazz.push MetricFu.const_get(grapher_name).new
|
18
18
|
end
|
19
19
|
|
data/lib/base/report.rb
CHANGED
@@ -50,7 +50,7 @@ module MetricFu
|
|
50
50
|
# @param report_type Hash
|
51
51
|
# The hash to add to the aggregate report_hash
|
52
52
|
def add(report_type)
|
53
|
-
clazz = MetricFu.const_get(report_type.to_s.
|
53
|
+
clazz = MetricFu.const_get(report_type.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase })
|
54
54
|
report_hash.merge!(clazz.generate_report)
|
55
55
|
end
|
56
56
|
|
data/lib/generators/flay.rb
CHANGED
@@ -8,7 +8,11 @@ module MetricFu
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def emit
|
11
|
-
files_to_flay = MetricFu.flay[:dirs_to_flay].map{|dir|
|
11
|
+
files_to_flay = MetricFu.flay[:dirs_to_flay].map{|dir|
|
12
|
+
MetricFu.flay[:filetypes].map {|type|
|
13
|
+
Dir[ File.join(dir, "**/*.#{type}") ]
|
14
|
+
}.flatten
|
15
|
+
}
|
12
16
|
files = remove_excluded_files(files_to_flay.flatten)
|
13
17
|
mimimum_score_parameter = MetricFu.flay[:minimum_score] ? "--mass #{MetricFu.flay[:minimum_score]} " : ""
|
14
18
|
@output = `flay #{mimimum_score_parameter}#{files.join(" ")}`
|
data/lib/generators/flog.rb
CHANGED
@@ -120,9 +120,13 @@ module MetricFu
|
|
120
120
|
end
|
121
121
|
|
122
122
|
class ScannedMethod
|
123
|
-
attr_accessor :name, :score, :operators
|
123
|
+
attr_accessor :name, :score, :operators, :line
|
124
124
|
|
125
125
|
def initialize(name, score, operators = [])
|
126
|
+
if name.match(/\.rb:\d*/)
|
127
|
+
@line = name.match(/\.rb:\d*/).to_s.sub('.rb:','')
|
128
|
+
name = name.match(/\S*/)
|
129
|
+
end
|
126
130
|
@name = name
|
127
131
|
@score = score.to_f
|
128
132
|
@operators = operators
|
@@ -130,8 +134,9 @@ module MetricFu
|
|
130
134
|
|
131
135
|
def to_h
|
132
136
|
{:name => @name,
|
133
|
-
|
134
|
-
|
137
|
+
:score => @score,
|
138
|
+
:operators => @operators.map {|o| o.to_h},
|
139
|
+
:line => @line}
|
135
140
|
end
|
136
141
|
end
|
137
142
|
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module MetricFu
|
2
|
+
class RailsBestPractices < Generator
|
3
|
+
|
4
|
+
def self.verify_dependencies!
|
5
|
+
`rails_best_practices --help`
|
6
|
+
raise 'sudo gem install rails_best_practices # if you want the rails_best_practices tasks' unless $?.success?
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
def emit
|
11
|
+
@output = `rails_best_practices .`
|
12
|
+
end
|
13
|
+
|
14
|
+
def analyze
|
15
|
+
@matches = @output.chomp.split("\n").map{|m| m.split(" - ") }
|
16
|
+
total = @matches.pop
|
17
|
+
2.times { @matches.pop } # ignore wiki link
|
18
|
+
@matches.reject! {|array| array.empty? }
|
19
|
+
@matches.map! do |match|
|
20
|
+
file, line = match[0].split(':')
|
21
|
+
problem = match[1]
|
22
|
+
{:file => file, :line => line, :problem => problem}
|
23
|
+
end
|
24
|
+
@rails_best_practices_results = {:total => total, :problems => @matches}
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_h
|
28
|
+
{:rails_best_practices => @rails_best_practices_results}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/generators/rcov.rb
CHANGED
@@ -5,17 +5,6 @@ module MetricFu
|
|
5
5
|
class Rcov < Generator
|
6
6
|
NEW_FILE_MARKER = ("=" * 80) + "\n"
|
7
7
|
|
8
|
-
def self.verify_dependencies!
|
9
|
-
`rcov --help`
|
10
|
-
unless $?.success?
|
11
|
-
if RUBY_PLATFORM =~ /java/
|
12
|
-
raise 'running in jruby - rcov tasks not available'
|
13
|
-
else
|
14
|
-
raise 'sudo gem install rcov # if you want the rcov tasks'
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
8
|
class Line
|
20
9
|
attr_accessor :content, :was_run
|
21
10
|
|
@@ -30,7 +19,7 @@ module MetricFu
|
|
30
19
|
end
|
31
20
|
|
32
21
|
def emit
|
33
|
-
|
22
|
+
unless MetricFu.rcov[:external]
|
34
23
|
FileUtils.rm_rf(MetricFu::Rcov.metric_directory, :verbose => false)
|
35
24
|
Dir.mkdir(MetricFu::Rcov.metric_directory)
|
36
25
|
test_files = FileList[*MetricFu.rcov[:test_files]].join(' ')
|
@@ -38,21 +27,33 @@ module MetricFu
|
|
38
27
|
output = ">> #{MetricFu::Rcov.metric_directory}/rcov.txt"
|
39
28
|
puts "** Running the specs/tests in the [#{MetricFu.rcov[:environment]}] environment"
|
40
29
|
`RAILS_ENV=#{MetricFu.rcov[:environment]} rcov #{test_files} #{rcov_opts} #{output}`
|
41
|
-
rescue LoadError
|
42
|
-
if RUBY_PLATFORM =~ /java/
|
43
|
-
puts 'running in jruby - rcov tasks not available'
|
44
|
-
else
|
45
|
-
puts 'sudo gem install rcov # if you want the rcov tasks'
|
46
|
-
end
|
47
30
|
end
|
48
31
|
end
|
49
32
|
|
50
33
|
|
51
34
|
def analyze
|
52
|
-
|
35
|
+
output_file = MetricFu.rcov[:external] ? MetricFu.rcov[:external] : MetricFu::Rcov.metric_directory + '/rcov.txt'
|
36
|
+
output = File.open(output_file).read
|
53
37
|
output = output.split(NEW_FILE_MARKER)
|
54
|
-
|
55
|
-
output.shift
|
38
|
+
|
39
|
+
output.shift # Throw away the first entry - it's the execution time etc.
|
40
|
+
|
41
|
+
files = assemble_files(output)
|
42
|
+
|
43
|
+
@global_total_lines = 0
|
44
|
+
@global_total_lines_run = 0
|
45
|
+
|
46
|
+
@rcov = add_coverage_percentage(files)
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_h
|
50
|
+
global_percent_run = ((@global_total_lines_run.to_f / @global_total_lines.to_f) * 100)
|
51
|
+
{:rcov => @rcov.merge({:global_percent_run => round_to_tenths(global_percent_run) })}
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def assemble_files(output)
|
56
57
|
files = {}
|
57
58
|
output.each_slice(2) {|out| files[out.first.strip] = out.last}
|
58
59
|
files.each_pair {|fname, content| files[fname] = content.split("\n") }
|
@@ -64,12 +65,13 @@ module MetricFu
|
|
64
65
|
line = Line.new(raw_line, true).to_h
|
65
66
|
end
|
66
67
|
end
|
68
|
+
content.reject! {|line| line[:content].blank? }
|
67
69
|
files[fname] = {:lines => content}
|
68
70
|
end
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
71
|
+
files
|
72
|
+
end
|
73
|
+
|
74
|
+
def add_coverage_percentage(files)
|
73
75
|
files.each_pair do |fname, content|
|
74
76
|
lines = content[:lines]
|
75
77
|
@global_total_lines_run += lines_run = lines.find_all {|line| line[:was_run] == true }.length
|
@@ -77,12 +79,7 @@ module MetricFu
|
|
77
79
|
percent_run = ((lines_run.to_f / total_lines.to_f) * 100).round
|
78
80
|
files[fname][:percent_run] = percent_run
|
79
81
|
end
|
80
|
-
@rcov = files
|
81
|
-
end
|
82
|
-
|
83
|
-
def to_h
|
84
|
-
global_percent_run = ((@global_total_lines_run.to_f / @global_total_lines.to_f) * 100)
|
85
|
-
{:rcov => @rcov.merge({:global_percent_run => round_to_tenths(global_percent_run) })}
|
86
82
|
end
|
83
|
+
|
87
84
|
end
|
88
85
|
end
|
data/lib/generators/roodi.rb
CHANGED
@@ -10,7 +10,8 @@ module MetricFu
|
|
10
10
|
def emit
|
11
11
|
files_to_analyze = MetricFu.roodi[:dirs_to_roodi].map{|dir| Dir[File.join(dir, "**/*.rb")] }
|
12
12
|
files = remove_excluded_files(files_to_analyze.flatten)
|
13
|
-
|
13
|
+
config = MetricFu.roodi[:roodi_config] ? "-config=#{MetricFu.roodi[:roodi_config]}" : ""
|
14
|
+
@output = `roodi #{config} #{files.join(" ")}`
|
14
15
|
end
|
15
16
|
|
16
17
|
def analyze
|
data/lib/generators/saikuro.rb
CHANGED
@@ -2,20 +2,21 @@ module MetricFu
|
|
2
2
|
|
3
3
|
class Saikuro < Generator
|
4
4
|
|
5
|
-
def emit
|
6
|
-
MetricFu.saikuro
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
exit
|
16
|
-
|
17
|
-
|
18
|
-
|
5
|
+
def emit
|
6
|
+
options_string = MetricFu.saikuro.inject("") do |options, option|
|
7
|
+
options + "--#{option.join(' ')} " unless option == :input_directory
|
8
|
+
end
|
9
|
+
|
10
|
+
MetricFu.saikuro[:input_directory].each do |input_dir|
|
11
|
+
options_string += "--input_directory #{input_dir} "
|
12
|
+
end
|
13
|
+
sh %{saikuro #{options_string}} do |ok, response|
|
14
|
+
unless ok
|
15
|
+
puts "Saikuro failed with exit status: #{response.exitstatus}"
|
16
|
+
exit 1
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
19
20
|
|
20
21
|
def format_directories
|
21
22
|
dirs = MetricFu.saikuro[:input_directory].join(" | ")
|
@@ -23,36 +24,9 @@ module MetricFu
|
|
23
24
|
end
|
24
25
|
|
25
26
|
def analyze
|
26
|
-
@files =
|
27
|
-
|
28
|
-
|
29
|
-
file = Saikuro::SFile.new(path)
|
30
|
-
if file
|
31
|
-
@files << file
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
@files = @files.sort_by do |file|
|
36
|
-
file.elements.
|
37
|
-
max {|a,b| a.complexity.to_i <=> b.complexity.to_i}.
|
38
|
-
complexity.to_i
|
39
|
-
end
|
40
|
-
@files.reverse!
|
41
|
-
klasses = []
|
42
|
-
@files.each {|f| klasses << f.elements}
|
43
|
-
klasses.flatten!
|
44
|
-
@classes = klasses.sort_by {|k| k.complexity.to_i}
|
45
|
-
@classes.reverse!
|
46
|
-
meths = []
|
47
|
-
@files.each {|f|
|
48
|
-
f.elements.each {|el|
|
49
|
-
el.defs.each {|defn|
|
50
|
-
defn.name = "#{el.name}##{defn.name}"
|
51
|
-
meths << defn}
|
52
|
-
}
|
53
|
-
}
|
54
|
-
meths = meths.sort_by {|meth| meth.complexity.to_i}
|
55
|
-
@meths = meths.reverse
|
27
|
+
@files = sort_files(assemble_files)
|
28
|
+
@classes = sort_classes(assemble_classes(@files))
|
29
|
+
@meths = sort_methods(assemble_methods(@files))
|
56
30
|
end
|
57
31
|
|
58
32
|
def to_h
|
@@ -67,10 +41,54 @@ module MetricFu
|
|
67
41
|
}
|
68
42
|
}
|
69
43
|
end
|
70
|
-
|
71
|
-
|
72
|
-
|
44
|
+
|
45
|
+
private
|
46
|
+
def sort_methods(methods)
|
47
|
+
methods.sort_by {|method| method.complexity.to_i}.reverse
|
48
|
+
end
|
49
|
+
|
50
|
+
def assemble_methods(files)
|
51
|
+
methods = []
|
52
|
+
files.each do |file|
|
53
|
+
file.elements.each do |element|
|
54
|
+
element.defs.each do |defn|
|
55
|
+
defn.name = "#{element.name}##{defn.name}"
|
56
|
+
methods << defn
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
methods
|
61
|
+
end
|
62
|
+
|
63
|
+
def sort_classes(classes)
|
64
|
+
classes.sort_by {|k| k.complexity.to_i}.reverse
|
65
|
+
end
|
66
|
+
|
67
|
+
def assemble_classes(files)
|
68
|
+
files.map {|f| f.elements}.flatten
|
69
|
+
end
|
70
|
+
|
71
|
+
def sort_files(files)
|
72
|
+
files.sort_by do |file|
|
73
|
+
file.elements.
|
74
|
+
max {|a,b| a.complexity.to_i <=> b.complexity.to_i}.
|
75
|
+
complexity.to_i
|
76
|
+
end.reverse
|
77
|
+
end
|
78
|
+
|
79
|
+
def assemble_files
|
80
|
+
files = []
|
81
|
+
Dir.glob("#{metric_directory}/**/*.html").each do |path|
|
82
|
+
if Saikuro::SFile.is_valid_text_file?(path)
|
83
|
+
file = Saikuro::SFile.new(path)
|
84
|
+
if file
|
85
|
+
files << file
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
files
|
73
90
|
end
|
91
|
+
|
74
92
|
end
|
75
93
|
|
76
94
|
class Saikuro::SFile
|
@@ -82,6 +100,8 @@ module MetricFu
|
|
82
100
|
@file_handle = File.open(@path, "r")
|
83
101
|
@elements = []
|
84
102
|
get_elements
|
103
|
+
ensure
|
104
|
+
@file_handle.close if @file_handle
|
85
105
|
end
|
86
106
|
|
87
107
|
def self.is_valid_text_file?(path)
|