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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4351c2bdcf92e1b45ec1dace93b7923afca53c3f
4
- data.tar.gz: 753445520ef195ff57eecc8697e93486ebd370fe
3
+ metadata.gz: d4067a9440e714228bbb8e47e2ca38d7a9bb6f60
4
+ data.tar.gz: 3664dc167f75e0b183b7437e1ec1b92cb8d36448
5
5
  SHA512:
6
- metadata.gz: 0756a302d0bd0b14477e6dbd36b77c7aa9fb5c86428b8171635fa245c1e9b985ce0a57bb0ee9902c0946f6b27a9e253a0fad15db5713238acdad948829deb5c3
7
- data.tar.gz: 3ad8999a9690de69339b9af840f0e6a1733d206c3b18767873ed2b9477ff2864e0bcb8d59a29883a800708f6e00db75834305dec01d005e44529d924b80c843f
6
+ metadata.gz: 8e3ca8381b8338b0a31a7e2c79b7a57987e21db4821b8ebbeb2647d4611e02c046c078ff0c63822cef808a79c6c2cad226c270d4561b311f07de3ab4265e3046
7
+ data.tar.gz: 153fdd0e09f92ad22993e2d8800709b265ac0b29ddc5cae3fb65506c7dc72049e6778056bdf26c9dab0f8598731da7f9e04ada6e35db91f94d191e0ee22fb4e8
data/.hound.yml ADDED
@@ -0,0 +1,7 @@
1
+ AccessModifierIndentation:
2
+ Description: Check indentation of private/protected visibility modifiers.
3
+ Enabled: false
4
+
5
+ LineLength:
6
+ Description: 'Limit lines to 100 characters.'
7
+ Max: 100
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][fl]]
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
- [fl]: https://github.com/seattlerb/flog
51
+ [flog]: https://github.com/seattlerb/flog
52
+ [flay]: https://github.com/seattlerb/flay
data/Rakefile CHANGED
@@ -3,7 +3,7 @@ require 'bundler/gem_tasks'
3
3
 
4
4
  Rake::TestTask.new do |test|
5
5
  test.verbose = true
6
- test.libs << "spec"
6
+ test.libs << 'spec'
7
7
  test.test_files = FileList['spec/**/*_spec.rb']
8
8
  end
9
9
 
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.1']
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/churn_calculator'
2
- require 'flog_cli'
3
- require 'ripper'
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
- @files.each do |file|
19
- stat = Stat.new(file)
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 measure_flog(stat)
42
- flogger = FlogCLI.new(all: true)
43
- flogger.flog(stat.file)
44
- flogger.calculate
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
- stat.definition_complexity = stat.definition_complexity.round(0)
44
+ def measure_duplication
45
+ DuplicationCalculator.new(@files, @stats).measure
58
46
  end
59
47
  end
60
48
  end
@@ -5,13 +5,26 @@ require 'code_poetry-html'
5
5
 
6
6
  module CodePoetry
7
7
  class CLI
8
- def self.excecute
9
- files = Array(FileList['app/**/*.rb']).compact
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
@@ -1,22 +1,38 @@
1
1
  module CodePoetry
2
2
  class Method
3
- attr_accessor :node, :name, :first_line, :last_line, :complexity
4
-
5
- def initialize(node, name, first_line, last_line)
6
- @node = node
7
- @name = name
8
- @first_line = first_line
9
- @last_line = last_line
10
- @complexity = 0
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.prepend(symbol)
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
@@ -5,7 +5,7 @@ module CodePoetry
5
5
  railtie_name :code_poetry
6
6
 
7
7
  rake_tasks do
8
- load "tasks/code_poetry.rake"
8
+ load 'tasks/code_poetry.rake'
9
9
  end
10
10
  end
11
11
  end
@@ -1,18 +1,22 @@
1
1
  require 'code_poetry/method'
2
2
  require 'code_poetry/warning_scanner'
3
- require 'code_poetry/smell'
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
- attr_accessor :file, :name, :lines, :lines_of_code, :churns, :complexity, :methods, :smells
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
- @lines_of_code, @churns, @complexity, @complexity_per_method, @definition_complexity = 0, 0, 0, 0, 0
12
- @file = file
13
- @lines = {}
14
- @methods = []
15
- @smells = []
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.find{|method| method.name == name}
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.find{|method| method.first_line <= line && method.last_line >= line}
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, "r").read
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, "r").read
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'].find{|first, last| first == first_line}
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("ComplexClass") if @complexity > 150
122
- @smells << Smell.new("ComplexClassDefinition") if @definition_complexity > 40
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("ComplexMethod", method)}
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
@@ -1,3 +1,3 @@
1
1
  module CodePoetry
2
- VERSION = "0.1.0"
2
+ VERSION = '0.2.0'
3
3
  end
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
@@ -1,5 +1,8 @@
1
1
  desc "Generate code metrics"
2
- task :metrics do
2
+ task :metrics, :path do |t, args|
3
3
  require 'code_poetry/cli'
4
- CodePoetry::CLI.excecute
4
+
5
+ path = ARGV.last unless ARGV.last == 'metrics'
6
+
7
+ CodePoetry::CLI.excecute(path)
5
8
  end
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, "smelly?", 8, 10) }
5
+ let(:method) { CodePoetry::Method.new(:def, 'smelly?', 8, 10, 'path') }
6
6
 
7
- describe ".smell?" do
8
- it "returns true if the complexity is greater 25" do
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 "returns false if the complexity is less or equal 25" do
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 ".pretty_name" do
20
- it "prepends a '.' to the name if the method is an instance method" do
21
- expect(method.pretty_name).to eq(".smelly?")
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 "prepends a '#' to the name if the method is an class method" do
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("#smelly?")
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
- let(:stat) { CodePoetry::Stat.new(test_file(1)) }
5
+ before(:all) { @stat = CodePoetry::Stat.new(test_file(2)) }
6
+ after(:each) { @stat.smells = [] }
7
7
 
8
- describe ".initialize" do
9
- it "sets the correct stat name" do
10
- expect(stat.name).to eq("Foo")
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 "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)
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 "sets the methods correctly" do
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("bar?")
22
- expect(method.node).to eq(:defs)
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 eq(5)
24
+ expect(method.last_line).to eq(5)
25
25
 
26
- method = stat.get_method("fooz?")
27
- expect(method.node).to eq(:def)
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 eq(9)
29
+ expect(method.last_line).to eq(9)
30
30
  end
31
+ end
31
32
 
32
- it "sets the methods correctly even if they have indentation errors" do
33
- stat = CodePoetry::Stat.new(test_file(2))
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
- method = stat.get_method("bar?")
38
- expect(method.node).to eq(:defs)
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
- method = stat.get_method("fooz?")
43
- expect(method.node).to eq(:def)
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 ".set_churns" do
50
- it "sets the churns" do
51
- stat.set_churns(10)
52
- expect(stat.churns).to eq(10)
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 "leaves the churns alone, if the param is nil" do
56
- stat.churns = 10
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 ".get_method" do
63
- it "returns the method with the specified name" do
64
- expect(stat.get_method("bar?").first_line).to eq(3)
65
- expect(stat.get_method("fooz?").first_line).to eq(7)
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 "returns nil if there is no mehtod with the specified name" do
69
- expect(stat.get_method("bar")).to eq(nil)
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 ".set_method_complexity" do
74
- it "sets the complexity of the method with the specified name" do
75
- stat.set_method_complexity("bar?", 200)
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 "adds the complexity to the definition_complexity if there is no method with the specified name" do
80
- stat.definition_complexity = 25
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 ".get_method_at_line" do
87
- it "return the method at the specified line" do
88
- expect(stat.get_method_at_line(3).name).to eq("bar?")
89
- expect(stat.get_method_at_line(7).name).to eq("fooz?")
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 "returns nil if there is no mehtod at the specified line" do
93
- expect(stat.get_method_at_line(20)).to eq(nil)
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
- describe ".set_smells" do
98
- it "creates a 'ComplexClass' smell if the overall complexity is greater 150" do
99
- stat.complexity = 151
100
- stat.set_smells
101
- expect(stat.smells.find{|smell| smell.type == "ComplexClass"}).to_not be_nil
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 "creates no 'ComplexClass' smell if the overall complexity is less 151" do
105
- stat.complexity = 150
106
- stat.set_smells
107
- expect(stat.smells.find{|smell| smell.type == "ComplexClass"}).to be_nil
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 "creates a 'ComplexClassDefinition' smell if the definition complexity is greater 40" do
111
- stat.definition_complexity = 41
112
- stat.set_smells
113
- expect(stat.smells.find{|smell| smell.type == "ComplexClassDefinition"}).to_not be_nil
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 "creates no 'ComplexClassDefinition' smell if the definition complexity is less 41" do
117
- stat.definition_complexity = 40
118
- stat.set_smells
119
- expect(stat.smells.find{|smell| smell.type == "ComplexClassDefinition"}).to be_nil
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 "creates a 'ComplexMethod' smell if a methods complexity is greater 25" do
123
- stat.set_method_complexity("bar?", 26)
124
- stat.set_smells
125
- expect(stat.smells.find{|smell| smell.type == "ComplexMethod"}).to_not be_nil
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
- it "creates no 'ComplexMethod' smell if no methods complexity is greater 25" do
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 ".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
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), "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])
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.1.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: 2013-12-01 00:00:00.000000000 Z
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.1'
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.1'
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.3
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
@@ -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