rubycrap 0.1.0 → 0.1.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.
- checksums.yaml +4 -4
- data/bin/rubycrap +3 -3
- data/lib/application.rb +170 -0
- data/lib/rubycrap.rb +2 -166
- data/lib/rubycrap/application.rb +42 -0
- data/lib/rubycrap/coverage.rb +88 -0
- data/lib/rubycrap/crap.rb +55 -0
- data/lib/rubycrap/logging.rb +1 -1
- data/lib/rubycrap/reporter.rb +83 -0
- data/lib/rubycrap/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 301a7ae4c8bff65e8a2924231b19633102557f6b
|
4
|
+
data.tar.gz: 59f381684ce7de0f62079b2a09e4de708b98a2be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b866fdf70f7b39e647b17776c6173a1e7df631a60ad6ba64398b3c9fc847306a1ab69ed43c476baf226636ff4e2550f7905c963277c7461d13f6b86e800d5832
|
7
|
+
data.tar.gz: ee7c2f599cab9ced0627b0bad58fd715a2433d6fd32ccb26c02956719787e9350f2fee4280a6f42d4fe4f823c53bece0b8ba7b306de869e15c5531ed4bf9b02e
|
data/bin/rubycrap
CHANGED
@@ -6,8 +6,8 @@ require 'logger'
|
|
6
6
|
read_filename = ARGV[0]
|
7
7
|
|
8
8
|
if read_filename == nil
|
9
|
-
|
10
|
-
|
9
|
+
puts "Please specify an Simplecov coverage.json file as argument"
|
10
|
+
exit 1
|
11
11
|
end
|
12
12
|
|
13
13
|
case ARGV[1]
|
@@ -17,4 +17,4 @@ else
|
|
17
17
|
mode = Logger::WARN
|
18
18
|
end
|
19
19
|
|
20
|
-
Rubycrap.
|
20
|
+
Rubycrap::Application.new(read_filename,mode).execute
|
data/lib/application.rb
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
require 'parser/current'
|
2
|
+
require 'flog_cli'
|
3
|
+
require 'json'
|
4
|
+
require 'builder'
|
5
|
+
require 'rubycrap/logging'
|
6
|
+
|
7
|
+
module Rubycrap
|
8
|
+
class Application
|
9
|
+
@simplecov_information=[]
|
10
|
+
@crap_methods=[]
|
11
|
+
def self.minfo(object)
|
12
|
+
puts ">supports: #{(object.methods - Object.methods).inspect}\n"
|
13
|
+
end
|
14
|
+
|
15
|
+
# def self.logger
|
16
|
+
# @logger ||= Logger.new(STDOUT)
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# => reads the sourcefile with an ast parser to get all the methods, then calculate the method coverage
|
20
|
+
#
|
21
|
+
def self.process_simplecov_file(file)
|
22
|
+
#get filename with its coverage information
|
23
|
+
filename = file["filename"]
|
24
|
+
logger.debug(filename)
|
25
|
+
parse_method_coverage(file,filename)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.parse_method_coverage(file,filename)
|
29
|
+
ast = Parser::CurrentRuby.parse(File.open(filename, "r").read)
|
30
|
+
recursive_search_ast(file,ast)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.calculate_method_coverage(file,startline,lastline)
|
34
|
+
# first we get the coverage lines from simplecov
|
35
|
+
# start position -1 and number of total lines (-1 if you dont want the end)
|
36
|
+
total_lines = lastline-startline
|
37
|
+
logger.debug("startline #{startline}")
|
38
|
+
logger.debug("latline #{lastline}")
|
39
|
+
coveragelinestotal = file["coverage"]
|
40
|
+
|
41
|
+
logger.debug( "total_lines #{total_lines}")
|
42
|
+
coveragelines = coveragelinestotal.slice(startline-1,total_lines)
|
43
|
+
logger.debug("coveragelines: #{coveragelines}")
|
44
|
+
covered_lines = 0
|
45
|
+
coveragelines.each do |line|
|
46
|
+
if !(line.to_s.eql? "0" or line.to_s.eql? "")
|
47
|
+
covered_lines = covered_lines + 1
|
48
|
+
end
|
49
|
+
end
|
50
|
+
method_coverage = covered_lines.to_f / total_lines.to_f
|
51
|
+
logger.debug("method_coverage: #{method_coverage}")
|
52
|
+
return method_coverage
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.recursive_search_ast(file,ast)
|
56
|
+
begin
|
57
|
+
ast.children.each do |child|
|
58
|
+
if child.class.to_s == "Parser::AST::Node"
|
59
|
+
if (child.type.to_s == "def" or child.type.to_s == "defs")
|
60
|
+
|
61
|
+
methodname = child.children[0].to_s
|
62
|
+
startline = child.loc.line
|
63
|
+
lastline = child.loc.last_line
|
64
|
+
logger.debug("\nmethodname: #{methodname}")
|
65
|
+
method_coverage = calculate_method_coverage(file,startline,lastline)
|
66
|
+
|
67
|
+
@simplecov_information << {:name => methodname, :coverage => method_coverage , :startline => startline, :lastline => lastline}
|
68
|
+
else
|
69
|
+
recursive_search_ast(file,child)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
rescue
|
74
|
+
# maybe empty source code
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.crap(score,coverage)
|
79
|
+
# CRAP(m) = comp(m)^2 * (1 � cov(m)/100)^3 + comp(m)
|
80
|
+
comp = score
|
81
|
+
cov = coverage
|
82
|
+
comp ** 2 * (1 - cov) ** 3 + comp
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.calcualte_crap_from_flog(file)
|
86
|
+
begin
|
87
|
+
FlogCLI.load_plugins
|
88
|
+
options = FlogCLI.parse_options "-qma"
|
89
|
+
flogger = FlogCLI.new options
|
90
|
+
|
91
|
+
flogger.flog file["filename"]
|
92
|
+
logger.debug("flogger absolute_filename: #{file["filename"]}")
|
93
|
+
flogger.each_by_score nil do |class_method, score, call_list|
|
94
|
+
startline = flogger.method_locations[class_method].split(":")[1]
|
95
|
+
absolute_filename = flogger.method_locations[class_method].split(":")[0]
|
96
|
+
logger.debug("flogger startline: #{startline}")
|
97
|
+
#match simplecov line with startine form floc
|
98
|
+
element = @simplecov_information.detect {|f| f[:startline] == startline.to_i }
|
99
|
+
if element.to_s == ""
|
100
|
+
logger.debug("no match with simplecov for logger class_method: #{class_method} startline: #{startline} ")
|
101
|
+
else
|
102
|
+
logger.debug("flogger class_method: #{class_method} simplecov: #{element}")
|
103
|
+
test_coverage = element[:coverage]
|
104
|
+
@crap_methods << {:methodname => class_method, :flog_score => score , :filepath => absolute_filename, :startline => startline, :method_coverage => test_coverage, :crap_score => crap(score,test_coverage)}
|
105
|
+
end
|
106
|
+
end
|
107
|
+
rescue
|
108
|
+
# something went wrong with flog
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.hasharray_to_html( hashArray )
|
113
|
+
# collect all hash keys, even if they don't appear in each hash:
|
114
|
+
headers = hashArray.inject([]){|a,x| a |= x.keys ; a} # use array union to find all unique headers/keys
|
115
|
+
|
116
|
+
html = Builder::XmlMarkup.new(:indent => 2)
|
117
|
+
html.table {
|
118
|
+
html.tr { headers.each{|h| html.th(h)} }
|
119
|
+
hashArray.each do |row|
|
120
|
+
html.tr { row.values.each { |value| html.td(value) }}
|
121
|
+
end
|
122
|
+
}
|
123
|
+
return html
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.run(coveragefile,mode)
|
127
|
+
logger.level = mode
|
128
|
+
coverage = JSON.parse(File.open(coveragefile, "r").read)
|
129
|
+
|
130
|
+
# file = coverage["files"].first
|
131
|
+
#
|
132
|
+
# => get all the source filenames from the coverage file
|
133
|
+
#
|
134
|
+
puts "total files: #{coverage["files"].count}"
|
135
|
+
coverage["files"].each.with_index(1) do |file, index|
|
136
|
+
logger.debug("file nr. #{index}")
|
137
|
+
process_simplecov_file(file)
|
138
|
+
calcualte_crap_from_flog(file)
|
139
|
+
end
|
140
|
+
|
141
|
+
@sorted = @crap_methods.sort_by { |k| -k[:crap_score] }
|
142
|
+
|
143
|
+
puts "\nRESULTS"
|
144
|
+
@sorted.each do |element|
|
145
|
+
puts "#{element[:crap_score].round}".ljust(6) + "#{element[:methodname]} ---> #{element[:filepath]}:#{element[:startline]}"
|
146
|
+
end
|
147
|
+
|
148
|
+
#
|
149
|
+
# buidler sucks
|
150
|
+
# it doesnt do thead and tbody
|
151
|
+
# and th: isnt accepted in datatables
|
152
|
+
|
153
|
+
|
154
|
+
# <script src="https://code.jquery.com/jquery-2.2.2.min.js" integrity="sha256-36cp2Co+/62rEAAYHLmRCPIych47CvdM+uTBJwSzWjI=" crossorigin="anonymous"></script>
|
155
|
+
# <script src="https://cdn.datatables.net/1.10.11/js/jquery.dataTables.js" crossorigin="anonymous"></script>
|
156
|
+
# <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.11/css/jquery.dataTables.min.css">
|
157
|
+
# <script type="text/javascript">
|
158
|
+
# $(document).ready(function(){
|
159
|
+
# $('#myTable').DataTable();
|
160
|
+
# });
|
161
|
+
# </script>
|
162
|
+
|
163
|
+
# <table id="myTable">
|
164
|
+
|
165
|
+
File.open("CRAP.html", 'w') { |file| file.write(hasharray_to_html(@sorted)) }
|
166
|
+
puts "THE END"
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
end
|
data/lib/rubycrap.rb
CHANGED
@@ -1,169 +1,5 @@
|
|
1
|
-
require '
|
2
|
-
require 'flog_cli'
|
3
|
-
require 'json'
|
4
|
-
require 'builder'
|
5
|
-
require 'logger'
|
1
|
+
require 'rubycrap/application'
|
6
2
|
|
7
|
-
|
8
|
-
@simplecov_information=[]
|
9
|
-
@crap_methods=[]
|
10
|
-
def self.minfo(object)
|
11
|
-
puts ">supports: #{(object.methods - Object.methods).inspect}\n"
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.logger
|
15
|
-
@logger ||= Logger.new(STDOUT)
|
16
|
-
end
|
17
|
-
#
|
18
|
-
# => reads the sourcefile with an ast parser to get all the methods, then calculate the method coverage
|
19
|
-
#
|
20
|
-
def self.process_simplecov_file(file)
|
21
|
-
#get filename with its coverage information
|
22
|
-
filename = file["filename"]
|
23
|
-
logger.debug(filename)
|
24
|
-
parse_method_coverage(file,filename)
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.parse_method_coverage(file,filename)
|
28
|
-
ast = Parser::CurrentRuby.parse(File.open(filename, "r").read)
|
29
|
-
recursive_search_ast(file,ast)
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.calculate_method_coverage(file,startline,lastline)
|
33
|
-
# first we get the coverage lines from simplecov
|
34
|
-
# start position -1 and number of total lines (-1 if you dont want the end)
|
35
|
-
total_lines = lastline-startline
|
36
|
-
logger.debug("startline #{startline}")
|
37
|
-
logger.debug("latline #{lastline}")
|
38
|
-
coveragelinestotal = file["coverage"]
|
39
|
-
|
40
|
-
logger.debug( "total_lines #{total_lines}")
|
41
|
-
coveragelines = coveragelinestotal.slice(startline-1,total_lines)
|
42
|
-
logger.debug("coveragelines: #{coveragelines}")
|
43
|
-
covered_lines = 0
|
44
|
-
coveragelines.each do |line|
|
45
|
-
if !(line.to_s.eql? "0" or line.to_s.eql? "")
|
46
|
-
covered_lines = covered_lines + 1
|
47
|
-
end
|
48
|
-
end
|
49
|
-
method_coverage = covered_lines.to_f / total_lines.to_f
|
50
|
-
logger.debug("method_coverage: #{method_coverage}")
|
51
|
-
return method_coverage
|
52
|
-
end
|
53
|
-
|
54
|
-
def self.recursive_search_ast(file,ast)
|
55
|
-
begin
|
56
|
-
ast.children.each do |child|
|
57
|
-
if child.class.to_s == "Parser::AST::Node"
|
58
|
-
if (child.type.to_s == "def" or child.type.to_s == "defs")
|
59
|
-
|
60
|
-
methodname = child.children[0].to_s
|
61
|
-
startline = child.loc.line
|
62
|
-
lastline = child.loc.last_line
|
63
|
-
logger.debug("\nmethodname: #{methodname}")
|
64
|
-
method_coverage = calculate_method_coverage(file,startline,lastline)
|
65
|
-
|
66
|
-
@simplecov_information << {:name => methodname, :coverage => method_coverage , :startline => startline, :lastline => lastline}
|
67
|
-
else
|
68
|
-
recursive_search_ast(file,child)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
rescue
|
73
|
-
# maybe empty source code
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def self.crap(score,coverage)
|
78
|
-
# CRAP(m) = comp(m)^2 * (1 � cov(m)/100)^3 + comp(m)
|
79
|
-
comp = score
|
80
|
-
cov = coverage
|
81
|
-
comp ** 2 * (1 - cov) ** 3 + comp
|
82
|
-
end
|
83
|
-
|
84
|
-
def self.calcualte_crap_from_flog(file)
|
85
|
-
begin
|
86
|
-
FlogCLI.load_plugins
|
87
|
-
options = FlogCLI.parse_options "-qma"
|
88
|
-
flogger = FlogCLI.new options
|
89
|
-
|
90
|
-
flogger.flog file["filename"]
|
91
|
-
logger.debug("flogger absolute_filename: #{file["filename"]}")
|
92
|
-
flogger.each_by_score nil do |class_method, score, call_list|
|
93
|
-
startline = flogger.method_locations[class_method].split(":")[1]
|
94
|
-
absolute_filename = flogger.method_locations[class_method].split(":")[0]
|
95
|
-
logger.debug("flogger startline: #{startline}")
|
96
|
-
#match simplecov line with startine form floc
|
97
|
-
element = @simplecov_information.detect {|f| f[:startline] == startline.to_i }
|
98
|
-
if element.to_s == ""
|
99
|
-
logger.debug("no match with simplecov for logger class_method: #{class_method} startline: #{startline} ")
|
100
|
-
else
|
101
|
-
logger.debug("flogger class_method: #{class_method} simplecov: #{element}")
|
102
|
-
test_coverage = element[:coverage]
|
103
|
-
@crap_methods << {:methodname => class_method, :flog_score => score , :filepath => absolute_filename, :startline => startline, :method_coverage => test_coverage, :crap_score => crap(score,test_coverage)}
|
104
|
-
end
|
105
|
-
end
|
106
|
-
rescue
|
107
|
-
# something went wrong with flog
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def self.hasharray_to_html( hashArray )
|
112
|
-
# collect all hash keys, even if they don't appear in each hash:
|
113
|
-
headers = hashArray.inject([]){|a,x| a |= x.keys ; a} # use array union to find all unique headers/keys
|
114
|
-
|
115
|
-
html = Builder::XmlMarkup.new(:indent => 2)
|
116
|
-
html.table {
|
117
|
-
html.tr { headers.each{|h| html.th(h)} }
|
118
|
-
hashArray.each do |row|
|
119
|
-
html.tr { row.values.each { |value| html.td(value) }}
|
120
|
-
end
|
121
|
-
}
|
122
|
-
return html
|
123
|
-
end
|
124
|
-
|
125
|
-
def self.run(coveragefile,mode)
|
126
|
-
logger.level = mode
|
127
|
-
coverage = JSON.parse(File.open(coveragefile, "r").read)
|
128
|
-
|
129
|
-
# file = coverage["files"].first
|
130
|
-
#
|
131
|
-
# => get all the source filenames from the coverage file
|
132
|
-
#
|
133
|
-
puts "total files: #{coverage["files"].count}"
|
134
|
-
coverage["files"].each.with_index(1) do |file, index|
|
135
|
-
logger.debug("file nr. #{index}")
|
136
|
-
process_simplecov_file(file)
|
137
|
-
calcualte_crap_from_flog(file)
|
138
|
-
end
|
139
|
-
|
140
|
-
@sorted = @crap_methods.sort_by { |k| -k[:crap_score] }
|
141
|
-
|
142
|
-
puts "\nRESULTS"
|
143
|
-
@sorted.each do |element|
|
144
|
-
puts "#{element[:crap_score].round}".ljust(6) + "#{element[:methodname]} ---> #{element[:filepath]}:#{element[:startline]}"
|
145
|
-
end
|
146
|
-
|
147
|
-
#
|
148
|
-
# buidler sucks
|
149
|
-
# it doesnt do thead and tbody
|
150
|
-
# and th: isnt accepted in datatables
|
151
|
-
|
152
|
-
|
153
|
-
# <script src="https://code.jquery.com/jquery-2.2.2.min.js" integrity="sha256-36cp2Co+/62rEAAYHLmRCPIych47CvdM+uTBJwSzWjI=" crossorigin="anonymous"></script>
|
154
|
-
# <script src="https://cdn.datatables.net/1.10.11/js/jquery.dataTables.js" crossorigin="anonymous"></script>
|
155
|
-
# <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.11/css/jquery.dataTables.min.css">
|
156
|
-
# <script type="text/javascript">
|
157
|
-
# $(document).ready(function(){
|
158
|
-
# $('#myTable').DataTable();
|
159
|
-
# });
|
160
|
-
# </script>
|
161
|
-
|
162
|
-
# <table id="myTable">
|
163
|
-
|
164
|
-
File.open("CRAP.html", 'w') { |file| file.write(hasharray_to_html(@sorted)) }
|
165
|
-
puts "THE END"
|
166
|
-
end
|
3
|
+
module Rubycrap
|
167
4
|
|
168
5
|
end
|
169
|
-
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'parser/current'
|
2
|
+
require 'flog_cli'
|
3
|
+
require 'json'
|
4
|
+
require 'builder'
|
5
|
+
require 'rubycrap/logging'
|
6
|
+
require 'rubycrap/coverage'
|
7
|
+
require 'rubycrap/crap'
|
8
|
+
require 'rubycrap/reporter'
|
9
|
+
|
10
|
+
module Rubycrap
|
11
|
+
class Application
|
12
|
+
|
13
|
+
def initialize(filename,mode)
|
14
|
+
Rubycrap::logger.level = mode
|
15
|
+
coverage = JSON.parse(File.open(filename, "r").read)
|
16
|
+
@coverage_files = coverage["files"]
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.minfo(object)
|
20
|
+
puts ">supports: #{(object.methods - Object.methods).inspect}\n"
|
21
|
+
end
|
22
|
+
|
23
|
+
def execute
|
24
|
+
@crap_methods = []
|
25
|
+
puts "total files: #{@coverage_files.count}"
|
26
|
+
@coverage_files.each.with_index(1) do |file, index|
|
27
|
+
Rubycrap::logger.debug("file nr. #{index}")
|
28
|
+
simplecov_information = Rubycrap::Coverage.new(file).process_simplecov_file
|
29
|
+
@crap_methods.concat(Rubycrap::Crap.new(simplecov_information,file).calculate_with_flog)
|
30
|
+
end
|
31
|
+
show_results
|
32
|
+
end
|
33
|
+
|
34
|
+
def show_results
|
35
|
+
reporter = Rubycrap::Reporter.new(@crap_methods)
|
36
|
+
puts "\nTOP 10 RESULTS"
|
37
|
+
reporter.console
|
38
|
+
reporter.html
|
39
|
+
puts "THE END"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'rubycrap/logging'
|
2
|
+
|
3
|
+
module Rubycrap
|
4
|
+
class Coverage
|
5
|
+
|
6
|
+
attr_reader :file,:filename, :coverage
|
7
|
+
|
8
|
+
def initialize(file)
|
9
|
+
@file = file
|
10
|
+
@filename = file["filename"]
|
11
|
+
@coverage = file["coverage"]
|
12
|
+
@simplecov_information = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def process_simplecov_file
|
16
|
+
Rubycrap::logger.debug(filename)
|
17
|
+
ast = parse_method_coverage
|
18
|
+
search_methods(ast)
|
19
|
+
@simplecov_information
|
20
|
+
end
|
21
|
+
|
22
|
+
def parse_method_coverage
|
23
|
+
Parser::CurrentRuby.parse(File.open(filename, "r").read)
|
24
|
+
end
|
25
|
+
|
26
|
+
def search_methods(ast)
|
27
|
+
if (ast.nil?)
|
28
|
+
return nil
|
29
|
+
end
|
30
|
+
if(!ast.respond_to?(:children))
|
31
|
+
return nil
|
32
|
+
end
|
33
|
+
ast.children.each do |child|
|
34
|
+
if(child.class.to_s == "Parser::AST::Node")
|
35
|
+
Rubycrap::logger.debug("TYPE AST - #{child.type.to_s}")
|
36
|
+
end
|
37
|
+
if(def_method?(child))
|
38
|
+
methodname = child.children[0].to_s == "(self)" ? child.children[1].to_s : child.children[0].to_s
|
39
|
+
startline = child.loc.line
|
40
|
+
lastline = child.loc.last_line
|
41
|
+
Rubycrap::logger.debug("\nmethodname: #{methodname}")
|
42
|
+
method_coverage = calculate_method_coverage(startline,lastline)
|
43
|
+
@simplecov_information << {:name => methodname,
|
44
|
+
:coverage => method_coverage ,
|
45
|
+
:startline => startline,
|
46
|
+
:lastline => lastline}
|
47
|
+
else
|
48
|
+
search_methods(child)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def calculate_method_coverage(startline,lastline)
|
54
|
+
total_lines = lastline-startline
|
55
|
+
Rubycrap::logger.debug("Startline #{startline} | Lastline #{lastline} | Total_lines #{total_lines}")
|
56
|
+
coveragelinestotal = coverage
|
57
|
+
coveragelines = coveragelinestotal.slice(startline-1,total_lines)
|
58
|
+
Rubycrap::logger.debug("coveragelines: #{coveragelines}")
|
59
|
+
covered_lines = 0
|
60
|
+
not_covered_lines = 0
|
61
|
+
coveragelines.each do |line|
|
62
|
+
if coverage?(line)
|
63
|
+
covered_lines += 1
|
64
|
+
end
|
65
|
+
if (line.to_s.eql? "0")
|
66
|
+
not_covered_lines += 1
|
67
|
+
end
|
68
|
+
end
|
69
|
+
valid_total_lines = covered_lines + not_covered_lines
|
70
|
+
method_coverage = covered_lines.to_f / valid_total_lines.to_f
|
71
|
+
Rubycrap::logger.debug("covered_lines: #{covered_lines}")
|
72
|
+
Rubycrap::logger.debug("not_covered_lines: #{not_covered_lines}")
|
73
|
+
Rubycrap::logger.debug("method_coverage: #{method_coverage}")
|
74
|
+
method_coverage
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
def def_method?(child)
|
79
|
+
child.class.to_s == "Parser::AST::Node" &&
|
80
|
+
(child.type.to_s == "def" or child.type.to_s == "defs")
|
81
|
+
end
|
82
|
+
|
83
|
+
def coverage?(line)
|
84
|
+
!(line.to_s.eql? "0" or line.to_s.eql? "")
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'flog_cli'
|
2
|
+
require 'rubycrap/logging'
|
3
|
+
|
4
|
+
module Rubycrap
|
5
|
+
class Crap
|
6
|
+
attr_reader :simplecov_information, :file
|
7
|
+
def initialize(simplecov_information,file)
|
8
|
+
@simplecov_information = simplecov_information
|
9
|
+
@file = file
|
10
|
+
@crap_methods= []
|
11
|
+
end
|
12
|
+
|
13
|
+
def crap(complexity,coverage)
|
14
|
+
complexity ** 2 * (1 - coverage) ** 3 + complexity
|
15
|
+
end
|
16
|
+
|
17
|
+
def calculate_with_flog
|
18
|
+
flog_file(file["filename"])
|
19
|
+
@flogger.each_by_score nil do |class_method, score, call_list|
|
20
|
+
startline = @flogger.method_locations[class_method].split(":")[1]
|
21
|
+
absolute_filename = @flogger.method_locations[class_method].split(":")[0]
|
22
|
+
Rubycrap::logger.debug("flogger class method name: #{class_method}")
|
23
|
+
flog_methodname = class_method.split("#")[1] || class_method.split("::")[1]
|
24
|
+
Rubycrap::logger.debug("flogger method name: #{flog_methodname}")
|
25
|
+
Rubycrap::logger.debug("flogger startline: #{startline}")
|
26
|
+
Rubycrap::logger.debug("SIMPPLECOV : #{simplecov_information}")
|
27
|
+
element = simplecov_information.detect {|f| f[:startline] == startline.to_i && f[:name] == flog_methodname}
|
28
|
+
if element.to_s == ""
|
29
|
+
Rubycrap::logger.debug("no match with simplecov for flogger class_method: #{class_method} startline: #{startline} ")
|
30
|
+
else
|
31
|
+
Rubycrap::logger.debug("flogger class_method: #{class_method} simplecov: #{element}")
|
32
|
+
test_coverage = element[:coverage]
|
33
|
+
@crap_methods << {:methodname => class_method,
|
34
|
+
:flog_score => score ,
|
35
|
+
:filepath => absolute_filename,
|
36
|
+
:startline => startline,
|
37
|
+
:method_coverage => test_coverage,
|
38
|
+
:crap_score => crap(score,test_coverage)}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
@crap_methods
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def flog_file(filename)
|
47
|
+
FlogCLI.load_plugins
|
48
|
+
options = FlogCLI.parse_options "-qma"
|
49
|
+
@flogger = FlogCLI.new options
|
50
|
+
@flogger.flog filename
|
51
|
+
Rubycrap::logger.debug("flogger absolute_filename: #{filename}")
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
data/lib/rubycrap/logging.rb
CHANGED
@@ -0,0 +1,83 @@
|
|
1
|
+
module Rubycrap
|
2
|
+
class Reporter
|
3
|
+
|
4
|
+
attr_reader :crap_methods, :crap_methods_by_score
|
5
|
+
|
6
|
+
def initialize(crap_methods)
|
7
|
+
@crap_methods = crap_methods
|
8
|
+
@crap_methods_by_score = crap_methods.sort_by { |k| -k[:crap_score] }
|
9
|
+
end
|
10
|
+
|
11
|
+
def html
|
12
|
+
File.open("CRAP.html", 'w') { |file| file.write(build_html) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def console
|
16
|
+
crap_methods_by_score[0..10].each do |element|
|
17
|
+
puts formated_result(element)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def build_html
|
24
|
+
html = Array.new(0)
|
25
|
+
html.push('<script src="https://code.jquery.com/jquery-2.2.2.min.js" integrity="sha256-36cp2Co+/62rEAAYHLmRCPIych47CvdM+uTBJwSzWjI=" crossorigin="anonymous"></script>')
|
26
|
+
html.push('<script src="https://cdn.datatables.net/1.10.11/js/jquery.dataTables.js" crossorigin="anonymous"></script>')
|
27
|
+
html.push('<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.11/css/jquery.dataTables.min.css">')
|
28
|
+
html.push('<script type="text/javascript">')
|
29
|
+
html.push(' $(document).ready(function(){')
|
30
|
+
html.push(' $(\'#myTable\').DataTable();')
|
31
|
+
html.push(' });')
|
32
|
+
html.push('</script>')
|
33
|
+
html.push('<table id="myTable">')
|
34
|
+
html.push('<thead>')
|
35
|
+
html.push(' <tr>')
|
36
|
+
html.push(' <th>methodname</th>')
|
37
|
+
html.push(' <th>flog_score</th>')
|
38
|
+
html.push(' <th>filepath</th>')
|
39
|
+
html.push(' <th>startline</th>')
|
40
|
+
html.push(' <th>method_coverage</th>')
|
41
|
+
html.push(' <th>crap_score</th>')
|
42
|
+
html.push(' </tr>')
|
43
|
+
html.push('</thead>')
|
44
|
+
html.push('<tbody>')
|
45
|
+
html.push('')
|
46
|
+
|
47
|
+
@crap_methods_by_score.each do |element|
|
48
|
+
html.push('<tr>')
|
49
|
+
html.push(" <td>#{element[:methodname]}</td>")
|
50
|
+
html.push(" <td>#{element[:flog_score]}</td>")
|
51
|
+
html.push(" <td>#{element[:filepath]}</td>")
|
52
|
+
html.push(" <td>#{element[:startline]}</td>")
|
53
|
+
html.push(" <td>#{element[:method_coverage]}</td>")
|
54
|
+
html.push(" <td>#{element[:crap_score]}</td>")
|
55
|
+
html.push('</tr>')
|
56
|
+
end
|
57
|
+
html.push('</tbody>')
|
58
|
+
html.push('</table>')
|
59
|
+
html.join("\n")
|
60
|
+
end
|
61
|
+
|
62
|
+
def formated_result(element)
|
63
|
+
@crap_element = element
|
64
|
+
"#{crap_score} | #{method_name} | #{file_path}:#{start_line}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def crap_score
|
68
|
+
@crap_element[:crap_score].round.to_s.ljust(6)
|
69
|
+
end
|
70
|
+
|
71
|
+
def method_name
|
72
|
+
@crap_element[:methodname].ljust(50)
|
73
|
+
end
|
74
|
+
|
75
|
+
def file_path
|
76
|
+
@crap_element[:filepath]
|
77
|
+
end
|
78
|
+
|
79
|
+
def start_line
|
80
|
+
@crap_element[:startline]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/rubycrap/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubycrap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ingo Gerhard Jauch
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-04-
|
12
|
+
date: 2016-04-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -98,8 +98,13 @@ files:
|
|
98
98
|
- README.md
|
99
99
|
- Rakefile
|
100
100
|
- bin/rubycrap
|
101
|
+
- lib/application.rb
|
101
102
|
- lib/rubycrap.rb
|
103
|
+
- lib/rubycrap/application.rb
|
104
|
+
- lib/rubycrap/coverage.rb
|
105
|
+
- lib/rubycrap/crap.rb
|
102
106
|
- lib/rubycrap/logging.rb
|
107
|
+
- lib/rubycrap/reporter.rb
|
103
108
|
- lib/rubycrap/version.rb
|
104
109
|
- rubycrap.gemspec
|
105
110
|
homepage: ''
|