code_poetry 0.1.0 → 0.2.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.
- 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
|