cdd-metric_fu 1.3.1
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/.document +7 -0
- data/.gitignore +24 -0
- data/HISTORY +182 -0
- data/MIT-LICENSE +22 -0
- data/Manifest.txt +25 -0
- data/README.rdoc +20 -0
- data/Rakefile +59 -0
- data/TODO +9 -0
- data/VERSION +1 -0
- data/cdd-metric_fu.gemspec +200 -0
- data/home_page/back_all.jpg +0 -0
- data/home_page/churn.gif +0 -0
- data/home_page/flay.gif +0 -0
- data/home_page/flog.gif +0 -0
- data/home_page/footer.gif +0 -0
- data/home_page/header.jpg +0 -0
- data/home_page/img09.gif +0 -0
- data/home_page/index.html +277 -0
- data/home_page/rcov.gif +0 -0
- data/home_page/reek.gif +0 -0
- data/home_page/roodi.gif +0 -0
- data/home_page/saikuro.gif +0 -0
- data/home_page/stats.gif +0 -0
- data/home_page/styles.css +245 -0
- data/home_page/title.gif +0 -0
- data/home_page/title_back.gif +0 -0
- data/lib/base/base_template.rb +145 -0
- data/lib/base/configuration.rb +188 -0
- data/lib/base/generator.rb +167 -0
- data/lib/base/graph.rb +47 -0
- data/lib/base/md5_tracker.rb +52 -0
- data/lib/base/report.rb +100 -0
- data/lib/generators/churn.rb +34 -0
- data/lib/generators/flay.rb +35 -0
- data/lib/generators/flog.rb +172 -0
- data/lib/generators/rcov.rb +82 -0
- data/lib/generators/reek.rb +64 -0
- data/lib/generators/roodi.rb +33 -0
- data/lib/generators/saikuro.rb +221 -0
- data/lib/generators/stats.rb +59 -0
- data/lib/graphs/engines/bluff.rb +101 -0
- data/lib/graphs/engines/gchart.rb +120 -0
- data/lib/graphs/flay_grapher.rb +19 -0
- data/lib/graphs/flog_grapher.rb +39 -0
- data/lib/graphs/grapher.rb +18 -0
- data/lib/graphs/rails_best_practices_grapher.rb +24 -0
- data/lib/graphs/rcov_grapher.rb +19 -0
- data/lib/graphs/reek_grapher.rb +31 -0
- data/lib/graphs/roodi_grapher.rb +19 -0
- data/lib/graphs/stats_grapher.rb +22 -0
- data/lib/metric_fu.rb +32 -0
- data/lib/metric_fu/railtie.rb +10 -0
- data/lib/tasks/metric_fu.rake +22 -0
- data/lib/templates/awesome/awesome_template.rb +37 -0
- data/lib/templates/awesome/churn.html.erb +58 -0
- data/lib/templates/awesome/css/buttons.css +82 -0
- data/lib/templates/awesome/css/default.css +91 -0
- data/lib/templates/awesome/css/integrity.css +335 -0
- data/lib/templates/awesome/css/reset.css +7 -0
- data/lib/templates/awesome/flay.html.erb +34 -0
- data/lib/templates/awesome/flog.html.erb +53 -0
- data/lib/templates/awesome/index.html.erb +31 -0
- data/lib/templates/awesome/layout.html.erb +30 -0
- data/lib/templates/awesome/rcov.html.erb +42 -0
- data/lib/templates/awesome/reek.html.erb +40 -0
- data/lib/templates/awesome/roodi.html.erb +27 -0
- data/lib/templates/awesome/saikuro.html.erb +71 -0
- data/lib/templates/awesome/stats.html.erb +51 -0
- data/lib/templates/javascripts/bluff-min.js +1 -0
- data/lib/templates/javascripts/excanvas.js +35 -0
- data/lib/templates/javascripts/js-class.js +1 -0
- data/lib/templates/standard/churn.html.erb +31 -0
- data/lib/templates/standard/default.css +64 -0
- data/lib/templates/standard/flay.html.erb +34 -0
- data/lib/templates/standard/flog.html.erb +53 -0
- data/lib/templates/standard/index.html.erb +38 -0
- data/lib/templates/standard/rcov.html.erb +43 -0
- data/lib/templates/standard/reek.html.erb +42 -0
- data/lib/templates/standard/roodi.html.erb +29 -0
- data/lib/templates/standard/saikuro.html.erb +84 -0
- data/lib/templates/standard/standard_template.rb +26 -0
- data/lib/templates/standard/stats.html.erb +55 -0
- data/spec/base/base_template_spec.rb +161 -0
- data/spec/base/configuration_spec.rb +274 -0
- data/spec/base/generator_spec.rb +244 -0
- data/spec/base/graph_spec.rb +32 -0
- data/spec/base/md5_tracker_spec.rb +57 -0
- data/spec/base/report_spec.rb +139 -0
- data/spec/generators/churn_spec.rb +43 -0
- data/spec/generators/flay_spec.rb +110 -0
- data/spec/generators/flog_spec.rb +262 -0
- data/spec/generators/rcov_spec.rb +159 -0
- data/spec/generators/reek_spec.rb +125 -0
- data/spec/generators/saikuro_spec.rb +58 -0
- data/spec/generators/stats_spec.rb +74 -0
- data/spec/graphs/engines/bluff_spec.rb +17 -0
- data/spec/graphs/engines/gchart_spec.rb +109 -0
- data/spec/graphs/flay_grapher_spec.rb +37 -0
- data/spec/graphs/flog_grapher_spec.rb +45 -0
- data/spec/graphs/grapher_spec.rb +29 -0
- data/spec/graphs/rcov_grapher_spec.rb +37 -0
- data/spec/graphs/reek_grapher_spec.rb +47 -0
- data/spec/graphs/roodi_grapher_spec.rb +37 -0
- data/spec/graphs/stats_grapher_spec.rb +44 -0
- data/spec/resources/saikuro/app/controllers/sessions_controller.rb_cyclo.html +10 -0
- data/spec/resources/saikuro/app/controllers/users_controller.rb_cyclo.html +16 -0
- data/spec/resources/saikuro/index_cyclo.html +155 -0
- data/spec/resources/saikuro_sfiles/thing.rb_cyclo.html +11 -0
- data/spec/resources/yml/20090630.yml +7913 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +7 -0
- metadata +285 -0
data/lib/base/graph.rb
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module MetricFu
|
|
2
|
+
|
|
3
|
+
def self.graph
|
|
4
|
+
@graph ||= Graph.new
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
class Graph
|
|
8
|
+
|
|
9
|
+
attr_accessor :clazz
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
self.clazz = []
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def add(graph_type, graph_engine)
|
|
16
|
+
grapher_name = graph_type.to_s.capitalize + graph_engine.to_s.capitalize + "Grapher"
|
|
17
|
+
self.clazz.push MetricFu.const_get(grapher_name).new
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def generate
|
|
22
|
+
return if self.clazz.empty?
|
|
23
|
+
puts "Generating graphs"
|
|
24
|
+
graphed_reports.each { |report| process_report(report) }
|
|
25
|
+
self.clazz.each { |grapher| grapher.graph! }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
def process_report(report)
|
|
30
|
+
puts "Processing #{report}"
|
|
31
|
+
date = date_for(report)
|
|
32
|
+
metrics = YAML::load(File.open(report))
|
|
33
|
+
self.clazz.each { |grapher| grapher.get_metrics(metrics, date) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def date_for(report)
|
|
37
|
+
date = File.basename(report, ".yml") # yyyy/mm/dd
|
|
38
|
+
"#{date[4..5].to_i}/#{date[6..7].to_i}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def graphed_reports
|
|
42
|
+
reports = Dir[File.join(MetricFu.data_directory, '*.yml')].sort
|
|
43
|
+
from = reports.size-MetricFu.graph_period < 0 ? 0 : reports.size-MetricFu.graph_period
|
|
44
|
+
reports[from..-1]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require 'digest/md5'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
|
|
4
|
+
module MetricFu
|
|
5
|
+
class MD5Tracker
|
|
6
|
+
|
|
7
|
+
@@unchanged_md5s = []
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
def md5_dir(path_to_file, base_dir)
|
|
11
|
+
File.join(base_dir,
|
|
12
|
+
path_to_file.split('/')[0..-2].join('/'))
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def md5_file(path_to_file, base_dir)
|
|
16
|
+
File.join(md5_dir(path_to_file, base_dir),
|
|
17
|
+
path_to_file.split('/').last.sub(/\.[a-z]+/, '.md5'))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def track(path_to_file, base_dir)
|
|
21
|
+
md5 = Digest::MD5.hexdigest(File.read(path_to_file))
|
|
22
|
+
FileUtils.mkdir_p(md5_dir(path_to_file, base_dir), :verbose => false)
|
|
23
|
+
f = File.new(md5_file(path_to_file, base_dir), "w")
|
|
24
|
+
f.puts(md5)
|
|
25
|
+
f.close
|
|
26
|
+
md5
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def file_changed?(path_to_file, base_dir)
|
|
30
|
+
orig_md5_file = md5_file(path_to_file, base_dir)
|
|
31
|
+
return !!track(path_to_file, base_dir) unless File.exist?(orig_md5_file)
|
|
32
|
+
|
|
33
|
+
current_md5 = ""
|
|
34
|
+
file = File.open(orig_md5_file, 'r')
|
|
35
|
+
file.each_line { |line| current_md5 << line }
|
|
36
|
+
file.close
|
|
37
|
+
current_md5.chomp!
|
|
38
|
+
|
|
39
|
+
new_md5 = Digest::MD5.hexdigest(File.read(path_to_file))
|
|
40
|
+
new_md5.chomp!
|
|
41
|
+
|
|
42
|
+
@@unchanged_md5s << path_to_file if new_md5 == current_md5
|
|
43
|
+
|
|
44
|
+
return new_md5 != current_md5
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def file_already_counted?(path_to_file)
|
|
48
|
+
return @@unchanged_md5s.include?(path_to_file)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
data/lib/base/report.rb
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
module MetricFu
|
|
2
|
+
|
|
3
|
+
# MetricFu.report memoizes access to a Report object, that will be
|
|
4
|
+
# used throughout the lifecycle of the MetricFu app.
|
|
5
|
+
def self.report
|
|
6
|
+
@report ||= Report.new
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# = Report
|
|
10
|
+
#
|
|
11
|
+
# The Report class is responsible two things:
|
|
12
|
+
#
|
|
13
|
+
# It adds information to the yaml report, produced by the system
|
|
14
|
+
# as a whole, for each of the generators used in this test run.
|
|
15
|
+
#
|
|
16
|
+
# It also handles passing the information from each generator used
|
|
17
|
+
# in this test run out to the template class set in
|
|
18
|
+
# MetricFu::Configuration.
|
|
19
|
+
class Report
|
|
20
|
+
|
|
21
|
+
# Renders the result of the report_hash into a yaml serialization
|
|
22
|
+
# ready for writing out to a file.
|
|
23
|
+
#
|
|
24
|
+
# @return YAML
|
|
25
|
+
# A YAML object containing the results of the report generation
|
|
26
|
+
# process
|
|
27
|
+
def to_yaml
|
|
28
|
+
report_hash.to_yaml
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def report_hash #:nodoc:
|
|
33
|
+
@report_hash ||= {}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Instantiates a new template class based on the configuration set
|
|
37
|
+
# in MetricFu::Configuration, or through the MetricFu.config block
|
|
38
|
+
# in your rake file (defaults to the included AwesomeTemplate),
|
|
39
|
+
# assigns the report_hash to the report_hash in the template, and
|
|
40
|
+
# tells the template to to write itself out.
|
|
41
|
+
def save_templatized_report
|
|
42
|
+
@template = MetricFu.template_class.new
|
|
43
|
+
@template.report = report_hash
|
|
44
|
+
@template.write
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Adds a hash from a passed report, produced by one of the Generator
|
|
48
|
+
# classes to the aggregate report_hash managed by this hash.
|
|
49
|
+
#
|
|
50
|
+
# @param report_type Hash
|
|
51
|
+
# The hash to add to the aggregate report_hash
|
|
52
|
+
def add(report_type)
|
|
53
|
+
clazz = MetricFu.const_get(report_type.to_s.capitalize)
|
|
54
|
+
report_hash.merge!(clazz.generate_report)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Saves the passed in content to the passed in directory. If
|
|
58
|
+
# a filename is passed in it will be used as the name of the
|
|
59
|
+
# file, otherwise it will default to 'index.html'
|
|
60
|
+
#
|
|
61
|
+
# @param content String
|
|
62
|
+
# A string containing the content (usually html) to be written
|
|
63
|
+
# to the file.
|
|
64
|
+
#
|
|
65
|
+
# @param dir String
|
|
66
|
+
# A dir containing the path to the directory to write the file in.
|
|
67
|
+
#
|
|
68
|
+
# @param file String
|
|
69
|
+
# A filename to save the path as. Defaults to 'index.html'.
|
|
70
|
+
#
|
|
71
|
+
def save_output(content, dir, file='index.html')
|
|
72
|
+
open("#{dir}/#{file}", "w") do |f|
|
|
73
|
+
f.puts content
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Checks to discover whether we should try and open the results
|
|
78
|
+
# of the report in the browser on this system. We only try and open
|
|
79
|
+
# in the browser if we're on OS X and we're not running in a
|
|
80
|
+
# CruiseControl.rb environment. See MetricFu.configuration for more
|
|
81
|
+
# details about how we make those guesses.
|
|
82
|
+
#
|
|
83
|
+
# @return Boolean
|
|
84
|
+
# Should we open in the browser or not?
|
|
85
|
+
def open_in_browser?
|
|
86
|
+
MetricFu.configuration.platform.include?('darwin') &&
|
|
87
|
+
! MetricFu.configuration.is_cruise_control_rb?
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Shows 'index.html' from the passed directory in the browser
|
|
91
|
+
# if we're able to open the browser on this platform.
|
|
92
|
+
#
|
|
93
|
+
# @param dir String
|
|
94
|
+
# The directory path where the 'index.html' we want to open is
|
|
95
|
+
# stored
|
|
96
|
+
def show_in_browser(dir)
|
|
97
|
+
system("open #{dir}/index.html") if open_in_browser?
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module MetricFu
|
|
2
|
+
|
|
3
|
+
class Churn < Generator
|
|
4
|
+
|
|
5
|
+
def initialize(options={})
|
|
6
|
+
super
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.verify_dependencies!
|
|
10
|
+
result = `churn --help`
|
|
11
|
+
raise 'sudo gem install churn # if you want the churn tasks' unless result.match(/churn/)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def emit
|
|
16
|
+
@output = `churn --yaml`
|
|
17
|
+
yaml_start = @output.index("---")
|
|
18
|
+
@output = @output[yaml_start...@output.length] if yaml_start
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def analyze
|
|
22
|
+
if @output.match(/fatal: Not a git repository/)
|
|
23
|
+
@churn = [:churn => {}]
|
|
24
|
+
else
|
|
25
|
+
@churn = YAML::load(@output)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_h
|
|
30
|
+
{:churn => @churn[:churn]}
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module MetricFu
|
|
2
|
+
|
|
3
|
+
class Flay < Generator
|
|
4
|
+
|
|
5
|
+
def self.verify_dependencies!
|
|
6
|
+
`flay --help`
|
|
7
|
+
raise 'sudo gem install flay # if you want the flay tasks' unless $?.success?
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def emit
|
|
11
|
+
files_to_flay = MetricFu.flay[:dirs_to_flay].map{|dir| Dir[File.join(dir, "**/*.rb")] }
|
|
12
|
+
files = remove_excluded_files(files_to_flay.flatten)
|
|
13
|
+
mimimum_score_parameter = MetricFu.flay[:minimum_score] ? "--mass #{MetricFu.flay[:minimum_score]} " : ""
|
|
14
|
+
@output = `flay #{mimimum_score_parameter}#{files.join(" ")}`
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def analyze
|
|
18
|
+
@matches = @output.chomp.split("\n\n").map{|m| m.split("\n ") }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_h
|
|
22
|
+
target = []
|
|
23
|
+
total_score = @matches.shift.first.split('=').last.strip
|
|
24
|
+
@matches.each do |problem|
|
|
25
|
+
reason = problem.shift.strip
|
|
26
|
+
lines_info = problem.map do |full_line|
|
|
27
|
+
name, line = full_line.split(":")
|
|
28
|
+
{:name => name.strip, :line => line.strip}
|
|
29
|
+
end
|
|
30
|
+
target << [:reason => reason, :matches => lines_info]
|
|
31
|
+
end
|
|
32
|
+
{:flay => {:total_score => total_score, :matches => target.flatten}}
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
|
|
3
|
+
module MetricFu
|
|
4
|
+
|
|
5
|
+
class Flog < Generator
|
|
6
|
+
attr_reader :pages
|
|
7
|
+
|
|
8
|
+
def self.verify_dependencies!
|
|
9
|
+
`flog --help`
|
|
10
|
+
raise 'sudo gem install flog # if you want the flog tasks' unless $?.success?
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
SCORE_FORMAT = "%0.2f"
|
|
14
|
+
METHOD_LINE_REGEX = /(\d+\.\d+):\s+([A-Za-z:]+#.*)/
|
|
15
|
+
OPERATOR_LINE_REGEX = /\s*(\d+\.\d+):\s(.*)$/
|
|
16
|
+
|
|
17
|
+
def emit
|
|
18
|
+
metric_dir = MetricFu::Flog.metric_directory
|
|
19
|
+
MetricFu.flog[:dirs_to_flog].each do |directory|
|
|
20
|
+
directory = "." if directory=='./'
|
|
21
|
+
files = Dir.glob("#{directory}/**/*.rb")
|
|
22
|
+
files = remove_excluded_files(files)
|
|
23
|
+
files.each do |filename|
|
|
24
|
+
output_dir = "#{metric_dir}/#{filename.split("/")[0..-2].join("/")}"
|
|
25
|
+
mkdir_p(output_dir, :verbose => false) unless File.directory?(output_dir)
|
|
26
|
+
pathname = Pathname.new(filename)
|
|
27
|
+
if MetricFu::MD5Tracker.file_changed?(filename, metric_dir)
|
|
28
|
+
base_name = pathname.basename.to_s.gsub(/\..*$/,'.txt')
|
|
29
|
+
editted_filename = File.join(pathname.dirname.to_s, base_name)
|
|
30
|
+
`flog -ad #{filename} > #{metric_dir}/#{editted_filename}`
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
rescue LoadError
|
|
35
|
+
if RUBY_PLATFORM =~ /java/
|
|
36
|
+
puts 'running in jruby - flog tasks not available'
|
|
37
|
+
else
|
|
38
|
+
puts 'sudo gem install flog # if you want the flog tasks'
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def parse(text)
|
|
43
|
+
summary, methods_summary = text.split "\n\n"
|
|
44
|
+
return unless summary
|
|
45
|
+
score, average = summary.split("\n").map {|line| line[OPERATOR_LINE_REGEX, 1]}
|
|
46
|
+
return nil unless score && methods_summary
|
|
47
|
+
page = Flog::Page.new(score, average)
|
|
48
|
+
methods_summary.each_line do |method_line|
|
|
49
|
+
if match = method_line.match(METHOD_LINE_REGEX)
|
|
50
|
+
page.scanned_methods << ScannedMethod.new(match[2], match[1])
|
|
51
|
+
elsif match = method_line.match(OPERATOR_LINE_REGEX)
|
|
52
|
+
return if page.scanned_methods.empty?
|
|
53
|
+
page.scanned_methods.last.operators << Operator.new(match[1], match[2])
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
page
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def analyze
|
|
60
|
+
@pages = []
|
|
61
|
+
flog_results.each do |path|
|
|
62
|
+
page = parse(open(path, "r") { |f| f.read })
|
|
63
|
+
if page
|
|
64
|
+
page.path = path.sub(metric_directory, "").sub(".txt", ".rb")
|
|
65
|
+
#don't include old cached flog results for files that no longer exist.
|
|
66
|
+
if is_file_current?(page.path.to_s)
|
|
67
|
+
@pages << page
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def to_h
|
|
74
|
+
number_of_methods = @pages.inject(0) {|count, page| count += page.scanned_methods.size}
|
|
75
|
+
total_flog_score = @pages.inject(0) {|total, page| total += page.score}
|
|
76
|
+
sorted_pages = @pages.sort_by {|page| page.highest_score }.reverse
|
|
77
|
+
{:flog => { :total => total_flog_score,
|
|
78
|
+
:average => average_score(total_flog_score, number_of_methods),
|
|
79
|
+
:pages => sorted_pages.map {|page| page.to_h}}}
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
def is_file_current?(pathname)
|
|
85
|
+
pathname = pathname.gsub(/^\//,'')
|
|
86
|
+
local_pathname = "./#{pathname}"
|
|
87
|
+
exists = false
|
|
88
|
+
|
|
89
|
+
MetricFu.flog[:dirs_to_flog].each do |directory|
|
|
90
|
+
directory = "." if directory=='./'
|
|
91
|
+
files = Dir.glob("#{directory}/**/*.rb")
|
|
92
|
+
if files.include?(pathname) || files.include?(local_pathname)
|
|
93
|
+
exists = true
|
|
94
|
+
break
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
exists
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def average_score(total_flog_score, number_of_methods)
|
|
101
|
+
return 0 if total_flog_score == 0
|
|
102
|
+
round_to_tenths(total_flog_score/number_of_methods)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def flog_results
|
|
106
|
+
Dir.glob("#{metric_directory}/**/*.txt")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
class Operator
|
|
110
|
+
attr_accessor :score, :operator
|
|
111
|
+
|
|
112
|
+
def initialize(score, operator)
|
|
113
|
+
@score = score.to_f
|
|
114
|
+
@operator = operator
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def to_h
|
|
118
|
+
{:score => @score, :operator => @operator}
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
class ScannedMethod
|
|
123
|
+
attr_accessor :name, :score, :operators, :line
|
|
124
|
+
|
|
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
|
|
130
|
+
@name = name
|
|
131
|
+
@score = score.to_f
|
|
132
|
+
@operators = operators
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def to_h
|
|
136
|
+
{:name => @name,
|
|
137
|
+
:score => @score,
|
|
138
|
+
:operators => @operators.map {|o| o.to_h},
|
|
139
|
+
:line => @line}
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
class Flog::Page < MetricFu::Generator
|
|
146
|
+
attr_accessor :path, :score, :scanned_methods, :average_score
|
|
147
|
+
|
|
148
|
+
def initialize(score, average_score, scanned_methods = [])
|
|
149
|
+
@score = score.to_f
|
|
150
|
+
@scanned_methods = scanned_methods
|
|
151
|
+
@average_score = average_score.to_f
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def filename
|
|
155
|
+
File.basename(path, ".txt")
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def to_h
|
|
159
|
+
{:score => @score,
|
|
160
|
+
:scanned_methods => @scanned_methods.map {|sm| sm.to_h},
|
|
161
|
+
:highest_score => highest_score,
|
|
162
|
+
:average_score => average_score,
|
|
163
|
+
:path => path}
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def highest_score
|
|
167
|
+
scanned_methods.inject(0) do |highest, m|
|
|
168
|
+
m.score > highest ? m.score : highest
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require 'enumerator'
|
|
2
|
+
|
|
3
|
+
module MetricFu
|
|
4
|
+
|
|
5
|
+
class Rcov < Generator
|
|
6
|
+
NEW_FILE_MARKER = ("=" * 80) + "\n"
|
|
7
|
+
|
|
8
|
+
class Line
|
|
9
|
+
attr_accessor :content, :was_run
|
|
10
|
+
|
|
11
|
+
def initialize(content, was_run)
|
|
12
|
+
@content = content
|
|
13
|
+
@was_run = was_run
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def to_h
|
|
17
|
+
{:content => @content, :was_run => @was_run}
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def emit
|
|
22
|
+
FileUtils.rm_rf(MetricFu::Rcov.metric_directory, :verbose => false)
|
|
23
|
+
Dir.mkdir(MetricFu::Rcov.metric_directory)
|
|
24
|
+
test_files = FileList[*MetricFu.rcov[:test_files]].join(' ')
|
|
25
|
+
rcov_opts = MetricFu.rcov[:rcov_opts].join(' ')
|
|
26
|
+
output = ">> #{MetricFu::Rcov.metric_directory}/rcov.txt"
|
|
27
|
+
puts "** Running the specs/tests in the [#{MetricFu.rcov[:environment]}] environment"
|
|
28
|
+
`RAILS_ENV=#{MetricFu.rcov[:environment]} rcov #{test_files} #{rcov_opts} #{output}`
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def analyze
|
|
33
|
+
output = File.open(MetricFu::Rcov.metric_directory + '/rcov.txt').read
|
|
34
|
+
output = output.split(NEW_FILE_MARKER)
|
|
35
|
+
|
|
36
|
+
output.shift # Throw away the first entry - it's the execution time etc.
|
|
37
|
+
|
|
38
|
+
files = assemble_files(output)
|
|
39
|
+
|
|
40
|
+
@global_total_lines = 0
|
|
41
|
+
@global_total_lines_run = 0
|
|
42
|
+
|
|
43
|
+
@rcov = add_coverage_percentage(files)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def to_h
|
|
47
|
+
global_percent_run = ((@global_total_lines_run.to_f / @global_total_lines.to_f) * 100)
|
|
48
|
+
{:rcov => @rcov.merge({:global_percent_run => round_to_tenths(global_percent_run) })}
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def assemble_files(output)
|
|
54
|
+
files = {}
|
|
55
|
+
output.each_slice(2) {|out| files[out.first.strip] = out.last}
|
|
56
|
+
files.each_pair {|fname, content| files[fname] = content.split("\n") }
|
|
57
|
+
files.each_pair do |fname, content|
|
|
58
|
+
content.map! do |raw_line|
|
|
59
|
+
if raw_line.match(/^!!/)
|
|
60
|
+
line = Line.new(raw_line.gsub('!!', ' '), false).to_h
|
|
61
|
+
else
|
|
62
|
+
line = Line.new(raw_line, true).to_h
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
content.reject! {|line| line[:content].blank? }
|
|
66
|
+
files[fname] = {:lines => content}
|
|
67
|
+
end
|
|
68
|
+
files
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def add_coverage_percentage(files)
|
|
72
|
+
files.each_pair do |fname, content|
|
|
73
|
+
lines = content[:lines]
|
|
74
|
+
@global_total_lines_run += lines_run = lines.find_all {|line| line[:was_run] == true }.length
|
|
75
|
+
@global_total_lines += total_lines = lines.length
|
|
76
|
+
percent_run = ((lines_run.to_f / total_lines.to_f) * 100).round
|
|
77
|
+
files[fname][:percent_run] = percent_run
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
end
|
|
82
|
+
end
|