code_poetry 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.hound.yml +7 -0
- data/README.md +4 -2
- data/Rakefile +1 -1
- data/code_poetry.gemspec +2 -1
- data/lib/code_poetry/calculator.rb +15 -27
- data/lib/code_poetry/cli.rb +15 -2
- data/lib/code_poetry/complexity_calculator.rb +42 -0
- data/lib/code_poetry/duplication_calculator.rb +76 -0
- data/lib/code_poetry/method.rb +27 -11
- data/lib/code_poetry/railtie.rb +1 -1
- data/lib/code_poetry/stat.rb +48 -23
- data/lib/code_poetry/version.rb +1 -1
- data/lib/code_poetry.rb +0 -2
- data/lib/tasks/code_poetry.rake +5 -2
- data/spec/method_spec.rb +33 -9
- data/spec/stat_spec.rb +93 -81
- data/spec/warning_scanner_spec.rb +7 -7
- metadata +22 -8
- data/lib/code_poetry/smell.rb +0 -22
- data/spec/smell_spec.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d4067a9440e714228bbb8e47e2ca38d7a9bb6f60
|
4
|
+
data.tar.gz: 3664dc167f75e0b183b7437e1ec1b92cb8d36448
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8e3ca8381b8338b0a31a7e2c79b7a57987e21db4821b8ebbeb2647d4611e02c046c078ff0c63822cef808a79c6c2cad226c270d4561b311f07de3ab4265e3046
|
7
|
+
data.tar.gz: 153fdd0e09f92ad22993e2d8800709b265ac0b29ddc5cae3fb65506c7dc72049e6778056bdf26c9dab0f8598731da7f9e04ada6e35db91f94d191e0ee22fb4e8
|
data/.hound.yml
ADDED
data/README.md
CHANGED
@@ -10,7 +10,8 @@ Currently it uses the following metrics:
|
|
10
10
|
|
11
11
|
* Lines of Code
|
12
12
|
* Churns [[Churn][ch]]
|
13
|
-
* Code Complexity [[Flog][
|
13
|
+
* Code Complexity [[Flog][flog]]
|
14
|
+
* Duplication [[Flay][flay]]
|
14
15
|
|
15
16
|
## Installation
|
16
17
|
|
@@ -47,4 +48,5 @@ This will generate a HTML report to ```metrics/index.html```.
|
|
47
48
|
|
48
49
|
[cc]: https://codeclimate.com
|
49
50
|
[ch]: https://github.com/danmayer/churn
|
50
|
-
[
|
51
|
+
[flog]: https://github.com/seattlerb/flog
|
52
|
+
[flay]: https://github.com/seattlerb/flay
|
data/Rakefile
CHANGED
data/code_poetry.gemspec
CHANGED
@@ -21,7 +21,8 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_development_dependency 'bundler', '~> 1.3'
|
22
22
|
spec.add_development_dependency 'rake'
|
23
23
|
|
24
|
-
spec.add_runtime_dependency 'code_poetry-html', ['~> 0.
|
24
|
+
spec.add_runtime_dependency 'code_poetry-html', ['~> 0.2']
|
25
25
|
spec.add_runtime_dependency 'churn', ['~> 0.0']
|
26
26
|
spec.add_runtime_dependency 'flog', ['~> 4.2']
|
27
|
+
spec.add_runtime_dependency 'flay', ['~> 2.4']
|
27
28
|
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
require 'churn/
|
2
|
-
require '
|
3
|
-
require '
|
1
|
+
require 'churn/calculator'
|
2
|
+
require 'code_poetry/complexity_calculator'
|
3
|
+
require 'code_poetry/duplication_calculator'
|
4
4
|
|
5
5
|
module CodePoetry
|
6
6
|
class Calculator
|
@@ -14,18 +14,13 @@ module CodePoetry
|
|
14
14
|
puts 'Calculating'
|
15
15
|
|
16
16
|
measure_churns
|
17
|
+
measure_complexity
|
18
|
+
measure_duplication
|
17
19
|
|
18
|
-
@
|
19
|
-
stat
|
20
|
-
|
21
|
-
stat.set_churns(@churns[file])
|
22
|
-
measure_flog(stat)
|
20
|
+
@stats.each do |stat|
|
21
|
+
stat.set_churns(@churns[stat.file])
|
23
22
|
stat.set_smells
|
24
|
-
|
25
|
-
@stats << stat
|
26
23
|
end
|
27
|
-
|
28
|
-
@stats
|
29
24
|
end
|
30
25
|
|
31
26
|
private
|
@@ -38,23 +33,16 @@ module CodePoetry
|
|
38
33
|
end
|
39
34
|
end
|
40
35
|
|
41
|
-
def
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
unless flogger.scores.empty?
|
47
|
-
klass = flogger.scores.first[0]
|
48
|
-
stat.complexity = flogger.total_score.round(0)
|
49
|
-
stat.complexity_per_method = flogger.average.round(0)
|
50
|
-
|
51
|
-
flogger.method_scores[klass].each do |name, score|
|
52
|
-
name = (name.match(/#(.+)/) || name.match(/::(.+)/))[1]
|
53
|
-
stat.set_method_complexity(name, score)
|
54
|
-
end
|
36
|
+
def measure_complexity
|
37
|
+
@files.each do |file|
|
38
|
+
stat = Stat.new(file)
|
39
|
+
ComplexityCalculator.new(stat).measure
|
40
|
+
@stats << stat
|
55
41
|
end
|
42
|
+
end
|
56
43
|
|
57
|
-
|
44
|
+
def measure_duplication
|
45
|
+
DuplicationCalculator.new(@files, @stats).measure
|
58
46
|
end
|
59
47
|
end
|
60
48
|
end
|
data/lib/code_poetry/cli.rb
CHANGED
@@ -5,13 +5,26 @@ require 'code_poetry-html'
|
|
5
5
|
|
6
6
|
module CodePoetry
|
7
7
|
class CLI
|
8
|
-
|
9
|
-
|
8
|
+
DIRECOTRIES = 'app,lib'
|
9
|
+
EXTENSIONS = 'rb,rake'
|
10
|
+
|
11
|
+
def self.excecute(path)
|
12
|
+
files = Array(expand_directories_to_files(path).sort).compact
|
10
13
|
calculator = Calculator.new(files)
|
11
14
|
stats = calculator.calculate
|
12
15
|
|
13
16
|
formatter = CodePoetry::Formatter::HTMLFormatter.new
|
14
17
|
formatter.format(stats)
|
15
18
|
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def self.expand_directories_to_files(path)
|
23
|
+
if path
|
24
|
+
Dir[File.join(path, "{#{DIRECOTRIES}}**", '**', "*.{#{EXTENSIONS}}")]
|
25
|
+
else
|
26
|
+
FileList[File.join("{#{DIRECOTRIES}}**", '**', "*.{#{EXTENSIONS}}")]
|
27
|
+
end
|
28
|
+
end
|
16
29
|
end
|
17
30
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'flog_cli'
|
2
|
+
|
3
|
+
module CodePoetry
|
4
|
+
class ComplexityCalculator
|
5
|
+
def initialize(stat)
|
6
|
+
@stat = stat
|
7
|
+
@flogger = FlogCLI.new(all: true)
|
8
|
+
end
|
9
|
+
|
10
|
+
def measure
|
11
|
+
flog_file
|
12
|
+
|
13
|
+
unless @flogger.scores.empty?
|
14
|
+
set_file_complexity
|
15
|
+
set_methods_complexity
|
16
|
+
end
|
17
|
+
|
18
|
+
@stat.round_definition_complexity
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def flog_file
|
24
|
+
@flogger.flog(@stat.file)
|
25
|
+
@flogger.calculate
|
26
|
+
end
|
27
|
+
|
28
|
+
def set_file_complexity
|
29
|
+
@stat.complexity = @flogger.total_score.round(0)
|
30
|
+
@stat.complexity_per_method = @flogger.average.round(0)
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_methods_complexity
|
34
|
+
klass = @flogger.scores.first[0]
|
35
|
+
|
36
|
+
@flogger.method_scores[klass].each do |name, score|
|
37
|
+
name = (name.match(/#(.+)/) || name.match(/::(.+)/))[1]
|
38
|
+
@stat.set_method_complexity(name, score)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'flay'
|
2
|
+
|
3
|
+
module CodePoetry
|
4
|
+
class DuplicationCalculator
|
5
|
+
def initialize(files, stats)
|
6
|
+
@files = files
|
7
|
+
@stats = stats
|
8
|
+
@flay = Flay.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def measure
|
12
|
+
flay_files
|
13
|
+
|
14
|
+
sorted_flays.each do |hash, mass|
|
15
|
+
evaluate_flay(hash, mass)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def flay_files
|
22
|
+
@flay.process(*@files)
|
23
|
+
@flay.analyze
|
24
|
+
end
|
25
|
+
|
26
|
+
def sorted_flays
|
27
|
+
@flay.masses.sort_by do |h, m|
|
28
|
+
[
|
29
|
+
-m,
|
30
|
+
@flay.hashes[h].first.file,
|
31
|
+
@flay.hashes[h].first.line,
|
32
|
+
@flay.hashes[h].first.first.to_s
|
33
|
+
]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def evaluate_flay(hash, mass)
|
38
|
+
nodes = fetch_nodes(hash)
|
39
|
+
|
40
|
+
stats, methods = fetch_stats_and_methods(nodes)
|
41
|
+
|
42
|
+
stats.each do |stat, method|
|
43
|
+
stat.duplications << Duplication.new(severity(hash), note_type(nodes), mass, methods)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def fetch_nodes(hash)
|
48
|
+
@flay.hashes[hash].sort_by { |node| [node.file, node.line] }
|
49
|
+
end
|
50
|
+
|
51
|
+
def fetch_stats_and_methods(nodes)
|
52
|
+
nodes.inject([[], []]) do |result, node|
|
53
|
+
stat = find_stat(node.file)
|
54
|
+
result[0] << stat
|
55
|
+
|
56
|
+
method = stat.get_method_at_line(node.line)
|
57
|
+
method.increase_duplication_count
|
58
|
+
result[1] << method
|
59
|
+
|
60
|
+
result
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def find_stat(filename)
|
65
|
+
@stats.detect { |stat| stat.file == filename }
|
66
|
+
end
|
67
|
+
|
68
|
+
def severity(hash)
|
69
|
+
@flay.identical[hash] ? 'Identical' : 'Similar'
|
70
|
+
end
|
71
|
+
|
72
|
+
def note_type(nodes)
|
73
|
+
nodes.first.first
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/code_poetry/method.rb
CHANGED
@@ -1,22 +1,38 @@
|
|
1
1
|
module CodePoetry
|
2
2
|
class Method
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
@
|
8
|
-
@
|
9
|
-
@
|
10
|
-
@
|
3
|
+
attr_reader :first_line, :last_line, :name
|
4
|
+
attr_accessor :complexity, :duplication_count, :node
|
5
|
+
|
6
|
+
def initialize(node, name, first_line, last_line, location)
|
7
|
+
@node = node
|
8
|
+
@name = name
|
9
|
+
@first_line = first_line
|
10
|
+
@last_line = last_line
|
11
|
+
@complexity = 0
|
12
|
+
@duplication_count = 0
|
13
|
+
|
14
|
+
@location = location
|
11
15
|
end
|
12
16
|
|
13
17
|
def smelly?
|
14
|
-
complexity > 25
|
18
|
+
@complexity > 25
|
19
|
+
end
|
20
|
+
|
21
|
+
def duplicated?
|
22
|
+
@duplication_count > 0
|
15
23
|
end
|
16
24
|
|
17
25
|
def pretty_name
|
18
|
-
symbol = node == :def ?
|
19
|
-
name
|
26
|
+
symbol = @node == :def ? '.' : '#'
|
27
|
+
"#{symbol}#{@name}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def pretty_location
|
31
|
+
"#{@location}:#{@first_line}..#{@last_line}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def increase_duplication_count
|
35
|
+
@duplication_count =+ 1
|
20
36
|
end
|
21
37
|
|
22
38
|
end
|
data/lib/code_poetry/railtie.rb
CHANGED
data/lib/code_poetry/stat.rb
CHANGED
@@ -1,18 +1,22 @@
|
|
1
1
|
require 'code_poetry/method'
|
2
2
|
require 'code_poetry/warning_scanner'
|
3
|
-
require '
|
3
|
+
require 'ripper'
|
4
4
|
|
5
5
|
module CodePoetry
|
6
|
+
Duplication = Struct.new(:severity, :node, :mass, :methods)
|
7
|
+
Smell = Struct.new(:type, :object)
|
8
|
+
|
6
9
|
class Stat
|
7
|
-
|
8
|
-
attr_accessor :complexity_per_method, :definition_complexity
|
10
|
+
attr_reader :duplication, :file, :lines, :lines_of_code, :name, :methods
|
11
|
+
attr_accessor :churns, :complexity, :complexity_per_method, :definition_complexity
|
12
|
+
attr_accessor :duplications, :smells
|
9
13
|
|
10
14
|
def initialize(file)
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@
|
14
|
-
@methods = []
|
15
|
-
@
|
15
|
+
@file = file
|
16
|
+
@lines_of_code, @churns = 0, 0
|
17
|
+
@complexity, @complexity_per_method, @definition_complexity = 0, 0, 0
|
18
|
+
@methods, @smells, @duplications = [], [], []
|
19
|
+
@lines = {}
|
16
20
|
|
17
21
|
parse_file
|
18
22
|
end
|
@@ -22,7 +26,7 @@ module CodePoetry
|
|
22
26
|
end
|
23
27
|
|
24
28
|
def get_method(name)
|
25
|
-
@methods.
|
29
|
+
@methods.detect { |method| method.name == name }
|
26
30
|
end
|
27
31
|
|
28
32
|
def set_method_complexity(name, score)
|
@@ -34,18 +38,27 @@ module CodePoetry
|
|
34
38
|
end
|
35
39
|
|
36
40
|
def get_method_at_line(line)
|
37
|
-
@methods.
|
41
|
+
@methods.detect { |method| method.first_line <= line && method.last_line >= line }
|
38
42
|
end
|
39
43
|
|
40
44
|
def set_smells
|
41
45
|
set_class_smells
|
42
46
|
set_method_smells
|
47
|
+
set_duplication_smells
|
48
|
+
end
|
49
|
+
|
50
|
+
def duplication
|
51
|
+
@duplications.map { |duplication| duplication.mass / duplication.methods.count }.inject(0, :+)
|
52
|
+
end
|
53
|
+
|
54
|
+
def round_definition_complexity
|
55
|
+
@definition_complexity = @definition_complexity.round(0)
|
43
56
|
end
|
44
57
|
|
45
58
|
private
|
46
59
|
|
47
60
|
def parse_file
|
48
|
-
@content = File.open(@file,
|
61
|
+
@content = File.open(@file, 'r').read
|
49
62
|
@indentation_warnings = indentation_warnings
|
50
63
|
|
51
64
|
set_name
|
@@ -54,7 +67,7 @@ module CodePoetry
|
|
54
67
|
end
|
55
68
|
|
56
69
|
def set_name
|
57
|
-
@content = File.open(@file,
|
70
|
+
@content = File.open(@file, 'r').read
|
58
71
|
|
59
72
|
if match = /^\s*class\s+(\S+)/.match(@content) || /^\s*module\s+(\S+)/.match(@content)
|
60
73
|
@name = match[1]
|
@@ -84,13 +97,13 @@ module CodePoetry
|
|
84
97
|
name, first_line = find_method_params(element)
|
85
98
|
|
86
99
|
if @indentation_warnings['def'] && @indentation_warnings['def'].any? { |first, last| first == first_line }
|
87
|
-
warning = @indentation_warnings['def'].
|
100
|
+
warning = @indentation_warnings['def'].detect{ |first, last| first == first_line }
|
88
101
|
last_line = warning[1]
|
89
102
|
else
|
90
103
|
last_line = find_last_line(name, first_line)
|
91
104
|
end
|
92
105
|
|
93
|
-
@methods << Method.new(element.first, name, first_line, last_line)
|
106
|
+
@methods << Method.new(element.first, name, first_line, last_line, @file)
|
94
107
|
else
|
95
108
|
scan_sexp(element)
|
96
109
|
end
|
@@ -105,6 +118,11 @@ module CodePoetry
|
|
105
118
|
end
|
106
119
|
end
|
107
120
|
|
121
|
+
def indentation_warnings
|
122
|
+
warning_scanner = WarningScanner.new
|
123
|
+
warning_scanner.scan(@content)
|
124
|
+
end
|
125
|
+
|
108
126
|
def find_last_line(token_name, line)
|
109
127
|
token_indentation = @lines[line].index('def')
|
110
128
|
|
@@ -112,19 +130,26 @@ module CodePoetry
|
|
112
130
|
last_line ? last_line + line + 1 : nil
|
113
131
|
end
|
114
132
|
|
115
|
-
def indentation_warnings
|
116
|
-
warning_scanner = WarningScanner.new
|
117
|
-
warning_scanner.scan(@content)
|
118
|
-
end
|
119
|
-
|
120
133
|
def set_class_smells
|
121
|
-
@smells << Smell.new(
|
122
|
-
@smells << Smell.new(
|
134
|
+
@smells << Smell.new('complex_class', nil) if @complexity > 150
|
135
|
+
@smells << Smell.new('complex_class_definition', nil) if @definition_complexity > 40
|
123
136
|
end
|
124
137
|
|
125
138
|
def set_method_smells
|
126
|
-
smelly_methods = @methods.select{|method| method.smelly?}
|
127
|
-
@smells.concat smelly_methods.map{|method| Smell.new(
|
139
|
+
smelly_methods = @methods.select { |method| method.smelly? }
|
140
|
+
@smells.concat smelly_methods.map { |method| Smell.new('complex_method', method) }
|
141
|
+
end
|
142
|
+
|
143
|
+
def set_duplication_smells
|
144
|
+
unique_duplications = []
|
145
|
+
|
146
|
+
@duplications.each do |duplication|
|
147
|
+
unless unique_duplications.any? { |d| d.methods == duplication.methods }
|
148
|
+
unique_duplications << duplication
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
@smells.concat unique_duplications.map { |duplication| Smell.new('duplication', duplication) }
|
128
153
|
end
|
129
154
|
end
|
130
155
|
end
|
data/lib/code_poetry/version.rb
CHANGED
data/lib/code_poetry.rb
CHANGED
@@ -9,7 +9,6 @@ module CodePoetry
|
|
9
9
|
end
|
10
10
|
|
11
11
|
class << self
|
12
|
-
|
13
12
|
def root
|
14
13
|
return @root if defined? @root
|
15
14
|
@root = File.expand_path(Dir.getwd)
|
@@ -29,6 +28,5 @@ module CodePoetry
|
|
29
28
|
def project_name
|
30
29
|
File.basename(root.split('/').last).capitalize.gsub('_', ' ')
|
31
30
|
end
|
32
|
-
|
33
31
|
end
|
34
32
|
end
|
data/lib/tasks/code_poetry.rake
CHANGED
data/spec/method_spec.rb
CHANGED
@@ -2,28 +2,52 @@ require 'spec_helper'
|
|
2
2
|
require_relative '../lib/code_poetry/method'
|
3
3
|
|
4
4
|
describe CodePoetry::Method do
|
5
|
-
let(:method) { CodePoetry::Method.new(:def,
|
5
|
+
let(:method) { CodePoetry::Method.new(:def, 'smelly?', 8, 10, 'path') }
|
6
6
|
|
7
|
-
describe
|
8
|
-
it
|
7
|
+
describe '#smelly?' do
|
8
|
+
it 'returns true if the complexity is greater 25' do
|
9
9
|
method.complexity = 26
|
10
10
|
expect(method.smelly?).to be_true
|
11
11
|
end
|
12
12
|
|
13
|
-
it
|
13
|
+
it 'returns false if the complexity is less or equal 25' do
|
14
14
|
method.complexity = 25
|
15
15
|
expect(method.smelly?).to be_false
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
describe
|
20
|
-
it
|
21
|
-
|
19
|
+
describe '#duplicated?' do
|
20
|
+
it 'returns true if the duplication count is greater 0' do
|
21
|
+
method.duplication_count = 1
|
22
|
+
expect(method.duplicated?).to be_true
|
22
23
|
end
|
23
24
|
|
24
|
-
it
|
25
|
+
it 'returns false if the duplication count is equal 0' do
|
26
|
+
method.duplication_count = 0
|
27
|
+
expect(method.duplicated?).to be_false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#pretty_name' do
|
32
|
+
it 'prepends a "." to the name if the method is an instance method' do
|
33
|
+
expect(method.pretty_name).to eq('.smelly?')
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'prepends a "#" to the name if the method is an class method' do
|
25
37
|
method.node = :defs
|
26
|
-
expect(method.pretty_name).to eq(
|
38
|
+
expect(method.pretty_name).to eq('#smelly?')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#pretty_location' do
|
43
|
+
it 'something' do
|
44
|
+
expect(method.pretty_location).to eq('path:8..10')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#increase_duplication_count' do
|
49
|
+
it 'increases the duplication count by 1' do
|
50
|
+
expect { method.increase_duplication_count }.to change { method.duplication_count }.by(1)
|
27
51
|
end
|
28
52
|
end
|
29
53
|
end
|
data/spec/stat_spec.rb
CHANGED
@@ -1,133 +1,145 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'ripper'
|
3
2
|
require_relative '../lib/code_poetry/stat'
|
4
3
|
|
5
4
|
describe CodePoetry::Stat do
|
6
|
-
|
5
|
+
before(:all) { @stat = CodePoetry::Stat.new(test_file(2)) }
|
6
|
+
after(:each) { @stat.smells = [] }
|
7
7
|
|
8
|
-
describe
|
9
|
-
it
|
10
|
-
expect(stat.name).to eq(
|
8
|
+
describe '#initialize' do
|
9
|
+
it 'sets the correct stat name' do
|
10
|
+
expect(@stat.name).to eq('Foo')
|
11
11
|
end
|
12
12
|
|
13
|
-
it
|
14
|
-
expect(stat.lines.count).to
|
15
|
-
expect(stat.lines_of_code).to eq(8)
|
13
|
+
it 'counts the correct number of lines and lines of code' do
|
14
|
+
expect(@stat.lines.count).to eq(11)
|
15
|
+
expect(@stat.lines_of_code).to eq(8)
|
16
16
|
end
|
17
17
|
|
18
|
-
it
|
19
|
-
expect(stat.methods.count).to eq(2)
|
18
|
+
it 'sets the methods correctly' do
|
19
|
+
expect(@stat.methods.count).to eq(2)
|
20
20
|
|
21
|
-
method = stat.get_method(
|
22
|
-
expect(method.node).to
|
21
|
+
method = @stat.get_method('bar?')
|
22
|
+
expect(method.node).to eq(:defs)
|
23
23
|
expect(method.first_line).to eq(3)
|
24
|
-
expect(method.last_line).to
|
24
|
+
expect(method.last_line).to eq(5)
|
25
25
|
|
26
|
-
method = stat.get_method(
|
27
|
-
expect(method.node).to
|
26
|
+
method = @stat.get_method('fooz?')
|
27
|
+
expect(method.node).to eq(:def)
|
28
28
|
expect(method.first_line).to eq(7)
|
29
|
-
expect(method.last_line).to
|
29
|
+
expect(method.last_line).to eq(9)
|
30
30
|
end
|
31
|
+
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
expect(stat.methods.count).to eq(2)
|
33
|
+
describe '#set_churns' do
|
34
|
+
it 'sets the churns' do
|
35
|
+
@stat.set_churns(10)
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
expect(method.first_line).to eq(3)
|
40
|
-
expect(method.last_line).to eq(5)
|
37
|
+
expect(@stat.churns).to eq(10)
|
38
|
+
end
|
41
39
|
|
42
|
-
|
43
|
-
expect(
|
44
|
-
expect(method.first_line).to eq(7)
|
45
|
-
expect(method.last_line).to eq(9)
|
40
|
+
it 'leaves the churns alone, if the param is nil' do
|
41
|
+
expect { @stat.set_churns(nil) }.not_to change { @stat.churns }
|
46
42
|
end
|
47
43
|
end
|
48
44
|
|
49
|
-
describe
|
50
|
-
it
|
51
|
-
stat.
|
52
|
-
expect(stat.
|
45
|
+
describe '#get_method' do
|
46
|
+
it 'returns the method with the specified name' do
|
47
|
+
expect(@stat.get_method('bar?').first_line).to eq(3)
|
48
|
+
expect(@stat.get_method('fooz?').first_line).to eq(7)
|
53
49
|
end
|
54
50
|
|
55
|
-
it
|
56
|
-
stat.
|
57
|
-
stat.set_churns(nil)
|
58
|
-
expect(stat.churns).to eq(10)
|
51
|
+
it 'returns nil if there is no method with the specified name' do
|
52
|
+
expect(@stat.get_method('bar')).to eq(nil)
|
59
53
|
end
|
60
54
|
end
|
61
55
|
|
62
|
-
describe
|
63
|
-
it
|
64
|
-
|
65
|
-
|
56
|
+
describe '#set_method_complexity' do
|
57
|
+
it 'sets the complexity of the method with the specified name' do
|
58
|
+
@stat.set_method_complexity('bar?', 200)
|
59
|
+
|
60
|
+
expect(@stat.get_method('bar?').complexity).to eq(200)
|
66
61
|
end
|
67
62
|
|
68
|
-
it
|
69
|
-
|
63
|
+
it 'adds the complexity to the definition_complexity if there is no method with the specified name' do
|
64
|
+
@stat.definition_complexity = 25
|
65
|
+
@stat.set_method_complexity('baz', 120)
|
66
|
+
|
67
|
+
expect(@stat.definition_complexity).to eq(145)
|
70
68
|
end
|
71
69
|
end
|
72
70
|
|
73
|
-
describe
|
74
|
-
it
|
75
|
-
stat.
|
76
|
-
expect(stat.get_method("bar?").complexity).to eq(200)
|
71
|
+
describe '#get_method_at_line' do
|
72
|
+
it 'return the method at the specified line' do
|
73
|
+
expect(@stat.get_method_at_line(3).name).to eq('bar?')
|
77
74
|
end
|
78
75
|
|
79
|
-
it
|
80
|
-
stat.
|
81
|
-
stat.set_method_complexity("baz", 120)
|
82
|
-
expect(stat.definition_complexity).to eq(145)
|
76
|
+
it 'returns nil if there is no mehtod at the specified line' do
|
77
|
+
expect(@stat.get_method_at_line(20)).to eq(nil)
|
83
78
|
end
|
84
79
|
end
|
85
80
|
|
86
|
-
describe
|
87
|
-
it
|
88
|
-
|
89
|
-
|
81
|
+
describe '#set_smells' do
|
82
|
+
it 'creates no complex_class smell if the overall complexity is less 151' do
|
83
|
+
@stat.complexity = 150
|
84
|
+
@stat.set_smells
|
85
|
+
|
86
|
+
expect(@stat.smells.detect { |smell| smell.type == 'complex_class' }).to be_nil
|
90
87
|
end
|
91
88
|
|
92
|
-
it
|
93
|
-
|
89
|
+
it 'creates a complex_class smell if the overall complexity is greater 150' do
|
90
|
+
@stat.complexity = 151
|
91
|
+
@stat.set_smells
|
92
|
+
|
93
|
+
expect(@stat.smells.detect { |smell| smell.type == 'complex_class' }).to_not be_nil
|
94
94
|
end
|
95
|
-
end
|
96
95
|
|
97
|
-
|
98
|
-
|
99
|
-
stat.
|
100
|
-
|
101
|
-
expect(stat.smells.
|
96
|
+
it 'creates no complex_class_definition smell if the definition complexity is less 41' do
|
97
|
+
@stat.definition_complexity = 40
|
98
|
+
@stat.set_smells
|
99
|
+
|
100
|
+
expect(@stat.smells.detect { |smell| smell.type == 'complex_class_definition' }).to be_nil
|
102
101
|
end
|
103
102
|
|
104
|
-
it
|
105
|
-
stat.
|
106
|
-
stat.set_smells
|
107
|
-
|
103
|
+
it 'creates a complex_class_definition smell if the definition complexity is greater 40' do
|
104
|
+
@stat.definition_complexity = 41
|
105
|
+
@stat.set_smells
|
106
|
+
|
107
|
+
expect(@stat.smells.detect { |smell| smell.type == 'complex_class_definition' }).to_not be_nil
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'creates no complex_method smell if no methods complexity is greater 25' do
|
111
|
+
@stat.set_method_complexity('bar?', 2)
|
112
|
+
@stat.set_smells
|
113
|
+
|
114
|
+
expect(@stat.smells.detect { |smell| smell.type == 'complex_method' }).to be_nil
|
108
115
|
end
|
109
116
|
|
110
|
-
it
|
111
|
-
stat.
|
112
|
-
stat.set_smells
|
113
|
-
|
117
|
+
it 'creates a complex_method smell if a methods complexity is greater 25' do
|
118
|
+
@stat.set_method_complexity('bar?', 26)
|
119
|
+
@stat.set_smells
|
120
|
+
|
121
|
+
expect(@stat.smells.detect { |smell| smell.type == 'complex_method' }).to_not be_nil
|
114
122
|
end
|
115
123
|
|
116
|
-
it
|
117
|
-
stat.
|
118
|
-
|
119
|
-
expect(stat.smells.
|
124
|
+
it 'creates no duplication smell if there is no duplication' do
|
125
|
+
@stat.set_smells
|
126
|
+
|
127
|
+
expect(@stat.smells.detect { |smell| smell.type == 'duplication' }).to be_nil
|
120
128
|
end
|
121
129
|
|
122
|
-
it
|
123
|
-
stat.
|
124
|
-
stat.set_smells
|
125
|
-
|
130
|
+
it 'creates a duplication smell if there is duplication' do
|
131
|
+
@stat.duplications = [double]
|
132
|
+
@stat.set_smells
|
133
|
+
|
134
|
+
expect(@stat.smells.detect { |smell| smell.type == 'duplication' }).to_not be_nil
|
126
135
|
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe '#duplication' do
|
139
|
+
it 'adds up the masses of the duplications' do
|
140
|
+
@stat.duplications = [double(mass: 10, methods: [double]), double(mass: 5, methods: [double])]
|
127
141
|
|
128
|
-
|
129
|
-
stat.set_smells
|
130
|
-
expect(stat.smells.find{|smell| smell.type == "ComplexMethod"}).to be_nil
|
142
|
+
expect(@stat.duplication).to eq(15)
|
131
143
|
end
|
132
144
|
end
|
133
145
|
end
|
@@ -4,15 +4,15 @@ require_relative '../lib/code_poetry/warning_scanner'
|
|
4
4
|
describe CodePoetry::WarningScanner do
|
5
5
|
let(:scanner) { CodePoetry::WarningScanner.new }
|
6
6
|
|
7
|
-
describe
|
8
|
-
it
|
9
|
-
source = File.open(test_file(1),
|
7
|
+
describe '#scan' do
|
8
|
+
it 'returns an array with warnings for every indendation in the source' do
|
9
|
+
source = File.open(test_file(1), 'r').read
|
10
10
|
expect(scanner.scan(source).size).to eq(0)
|
11
11
|
|
12
|
-
source = File.open(test_file(2),
|
13
|
-
expect(scanner.scan(source)[
|
14
|
-
expect(scanner.scan(source)[
|
15
|
-
expect(scanner.scan(source)[
|
12
|
+
source = File.open(test_file(2), 'r').read
|
13
|
+
expect(scanner.scan(source)['def'].size).to eq(2)
|
14
|
+
expect(scanner.scan(source)['def'][0]).to eq([3, 5])
|
15
|
+
expect(scanner.scan(source)['def'][1]).to eq([7, 9])
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: code_poetry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bastian Bartmann
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-05-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - ~>
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '0.
|
47
|
+
version: '0.2'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '0.
|
54
|
+
version: '0.2'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: churn
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - ~>
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '4.2'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: flay
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.4'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.4'
|
83
97
|
description: Analyzes the code of your Rails app and generates a straightforward HTML
|
84
98
|
report.
|
85
99
|
email:
|
@@ -89,6 +103,7 @@ extensions: []
|
|
89
103
|
extra_rdoc_files: []
|
90
104
|
files:
|
91
105
|
- .gitignore
|
106
|
+
- .hound.yml
|
92
107
|
- .rspec
|
93
108
|
- .travis.yml
|
94
109
|
- Gemfile
|
@@ -99,10 +114,11 @@ files:
|
|
99
114
|
- lib/code_poetry.rb
|
100
115
|
- lib/code_poetry/calculator.rb
|
101
116
|
- lib/code_poetry/cli.rb
|
117
|
+
- lib/code_poetry/complexity_calculator.rb
|
118
|
+
- lib/code_poetry/duplication_calculator.rb
|
102
119
|
- lib/code_poetry/formatter.rb
|
103
120
|
- lib/code_poetry/method.rb
|
104
121
|
- lib/code_poetry/railtie.rb
|
105
|
-
- lib/code_poetry/smell.rb
|
106
122
|
- lib/code_poetry/stat.rb
|
107
123
|
- lib/code_poetry/version.rb
|
108
124
|
- lib/code_poetry/warning_scanner.rb
|
@@ -110,7 +126,6 @@ files:
|
|
110
126
|
- spec/dummy_files/1.rb
|
111
127
|
- spec/dummy_files/2.rb
|
112
128
|
- spec/method_spec.rb
|
113
|
-
- spec/smell_spec.rb
|
114
129
|
- spec/spec_helper.rb
|
115
130
|
- spec/stat_spec.rb
|
116
131
|
- spec/warning_scanner_spec.rb
|
@@ -134,7 +149,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
149
|
version: '0'
|
135
150
|
requirements: []
|
136
151
|
rubyforge_project:
|
137
|
-
rubygems_version: 2.0.
|
152
|
+
rubygems_version: 2.0.14
|
138
153
|
signing_key:
|
139
154
|
specification_version: 4
|
140
155
|
summary: The poor men's Code Climate
|
@@ -142,7 +157,6 @@ test_files:
|
|
142
157
|
- spec/dummy_files/1.rb
|
143
158
|
- spec/dummy_files/2.rb
|
144
159
|
- spec/method_spec.rb
|
145
|
-
- spec/smell_spec.rb
|
146
160
|
- spec/spec_helper.rb
|
147
161
|
- spec/stat_spec.rb
|
148
162
|
- spec/warning_scanner_spec.rb
|
data/lib/code_poetry/smell.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
module CodePoetry
|
2
|
-
class Smell
|
3
|
-
attr_accessor :type, :method
|
4
|
-
|
5
|
-
def initialize(type, method = nil)
|
6
|
-
@type = type
|
7
|
-
@method = method
|
8
|
-
end
|
9
|
-
|
10
|
-
def complex_class?
|
11
|
-
@type == "ComplexClass"
|
12
|
-
end
|
13
|
-
|
14
|
-
def complex_class_definition?
|
15
|
-
@type == "ComplexClassDefinition"
|
16
|
-
end
|
17
|
-
|
18
|
-
def complex_method?
|
19
|
-
@type == "ComplexMethod"
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
data/spec/smell_spec.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require_relative '../lib/code_poetry/smell'
|
3
|
-
|
4
|
-
describe CodePoetry::Smell do
|
5
|
-
let(:smell) { CodePoetry::Smell.new("ComplexClass") }
|
6
|
-
|
7
|
-
describe ".complex_class?" do
|
8
|
-
it "returns true if the smells type is 'ComplexClass'" do
|
9
|
-
expect(smell.complex_class?).to be_true
|
10
|
-
end
|
11
|
-
|
12
|
-
it "returns false if the smell type isn't 'ComplexClass'" do
|
13
|
-
smell.type = "ComplexClassDefinition"
|
14
|
-
expect(smell.complex_class?).to be_false
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
describe ".complex_class_definition?" do
|
19
|
-
it "returns true if the smells type is 'ComplexClassDefinition'" do
|
20
|
-
smell.type = "ComplexClassDefinition"
|
21
|
-
expect(smell.complex_class_definition?).to be_true
|
22
|
-
end
|
23
|
-
|
24
|
-
it "returns false if the smell type isn't 'ComplexClassDefinition'" do
|
25
|
-
expect(smell.complex_class_definition?).to be_false
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
describe ".complex_method?" do
|
30
|
-
it "returns true if the smells type is 'ComplexMethod'" do
|
31
|
-
smell.type = "ComplexMethod"
|
32
|
-
expect(smell.complex_method?).to be_true
|
33
|
-
end
|
34
|
-
|
35
|
-
it "returns false if the smell type isn't 'ComplexMethod'" do
|
36
|
-
expect(smell.complex_method?).to be_false
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|