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 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