reek 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +8 -0
- data/README.txt +1 -1
- data/bin/reek +0 -0
- data/lib/reek/method_checker.rb +12 -7
- data/lib/reek/printer.rb +10 -1
- data/lib/reek/report.rb +10 -4
- data/lib/reek/smells.rb +17 -30
- data/lib/reek/version.rb +1 -1
- data/spec/reek/feature_envy_spec.rb +3 -3
- data/spec/reek/long_method_spec.rb +25 -2
- data/spec/reek/nested_iterators_spec.rb +22 -0
- data/spec/reek/report_spec.rb +24 -1
- data/spec/reek/smell_spec.rb +18 -2
- data/website/index.html +1 -1
- metadata +1 -1
data/History.txt
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
== 0.1.1 2008-09-09
|
2
|
+
|
3
|
+
* Some tweaks:
|
4
|
+
* Fixed report printing for Feature Envy when the receiver is a block
|
5
|
+
* Fixed: successive iterators reported as nested
|
6
|
+
* Fixed: Long Method now reports the total length of the method
|
7
|
+
* Fixed: each smell reported only once
|
8
|
+
|
1
9
|
== 0.1.0 2008-09-09
|
2
10
|
|
3
11
|
* 1 minor enhancement:
|
data/README.txt
CHANGED
data/bin/reek
CHANGED
File without changes
|
data/lib/reek/method_checker.rb
CHANGED
@@ -12,19 +12,20 @@ module Reek
|
|
12
12
|
super(smells)
|
13
13
|
@class_name = klass_name
|
14
14
|
@description = klass_name
|
15
|
-
@top_level_block = true
|
16
15
|
@calls = Hash.new(0)
|
17
16
|
@lvars = Set.new
|
18
17
|
@inside_an_iter = false
|
19
18
|
end
|
20
19
|
|
21
20
|
def process_defn(exp)
|
21
|
+
@num_statements = 0
|
22
22
|
@description = "#{@class_name}##{exp[1]}"
|
23
23
|
UncommunicativeName.check(exp[1], self, 'method')
|
24
24
|
process(exp[2])
|
25
25
|
@lvars.each {|lvar| UncommunicativeName.check(lvar, self, 'local variable') }
|
26
26
|
UtilityFunction.check(@calls, self)
|
27
27
|
FeatureEnvy.check(@calls, self)
|
28
|
+
LongMethod.check(@num_statements, self)
|
28
29
|
s(exp)
|
29
30
|
end
|
30
31
|
|
@@ -41,18 +42,14 @@ module Reek
|
|
41
42
|
|
42
43
|
def process_iter(exp)
|
43
44
|
NestedIterators.check(@inside_an_iter, self)
|
44
|
-
@top_level_block = false
|
45
45
|
@inside_an_iter = true
|
46
46
|
exp[1..-1].each { |s| process(s) }
|
47
|
+
@inside_an_iter = false
|
47
48
|
s(exp)
|
48
49
|
end
|
49
50
|
|
50
51
|
def process_block(exp)
|
51
|
-
|
52
|
-
LongMethod.check(exp, self)
|
53
|
-
else
|
54
|
-
LongBlock.check(exp, self)
|
55
|
-
end
|
52
|
+
@num_statements += count_statements(exp)
|
56
53
|
exp[1..-1].each { |s| process(s) }
|
57
54
|
s(exp)
|
58
55
|
end
|
@@ -108,5 +105,13 @@ module Reek
|
|
108
105
|
process(exp[2])
|
109
106
|
s(exp)
|
110
107
|
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def count_statements(exp)
|
112
|
+
result = exp.length - 1
|
113
|
+
result -= 1 if Array === exp[1] and exp[1][0] == :args
|
114
|
+
result
|
115
|
+
end
|
111
116
|
end
|
112
117
|
end
|
data/lib/reek/printer.rb
CHANGED
@@ -19,7 +19,11 @@ module Reek
|
|
19
19
|
|
20
20
|
def print(sexp)
|
21
21
|
@report = sexp.inspect
|
22
|
-
|
22
|
+
begin
|
23
|
+
process(sexp)
|
24
|
+
rescue
|
25
|
+
raise "Error in print, parsing:\n #{sexp.inspect}"
|
26
|
+
end
|
23
27
|
@report
|
24
28
|
end
|
25
29
|
|
@@ -43,6 +47,11 @@ module Reek
|
|
43
47
|
s(exp)
|
44
48
|
end
|
45
49
|
|
50
|
+
def process_iter(exp)
|
51
|
+
@report = 'block'
|
52
|
+
s(exp)
|
53
|
+
end
|
54
|
+
|
46
55
|
def process_call(exp)
|
47
56
|
@report = "#{exp[1]}.#{exp[2]}"
|
48
57
|
@report += "(#{exp[3]})" if exp.length > 3
|
data/lib/reek/report.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
$:.unshift File.dirname(__FILE__)
|
2
2
|
|
3
|
+
require 'set'
|
4
|
+
|
3
5
|
module Reek
|
4
6
|
|
5
7
|
class Report
|
6
8
|
def initialize # :nodoc:
|
7
|
-
@smells =
|
9
|
+
@smells = SortedSet.new
|
8
10
|
end
|
9
11
|
|
10
12
|
def <<(smell) # :nodoc:
|
@@ -14,13 +16,17 @@ module Reek
|
|
14
16
|
def empty? # :nodoc:
|
15
17
|
@smells.empty?
|
16
18
|
end
|
17
|
-
|
19
|
+
|
20
|
+
def include?(obj) # :nodoc:
|
21
|
+
@smells.include?(obj)
|
22
|
+
end
|
23
|
+
|
18
24
|
def length # :nodoc:
|
19
25
|
@smells.length
|
20
26
|
end
|
21
|
-
|
27
|
+
|
22
28
|
def [](i) # :nodoc:
|
23
|
-
@smells[i]
|
29
|
+
@smells.to_a[i]
|
24
30
|
end
|
25
31
|
|
26
32
|
# Creates a formatted report of all the smells recorded in
|
data/lib/reek/smells.rb
CHANGED
@@ -5,6 +5,8 @@ require 'reek/printer'
|
|
5
5
|
module Reek
|
6
6
|
|
7
7
|
class Smell
|
8
|
+
include Comparable
|
9
|
+
|
8
10
|
def self.convert_camel_case(class_name)
|
9
11
|
class_name.gsub(/([a-z])([A-Z])/) { |s| "#{$1} #{$2}"}
|
10
12
|
end
|
@@ -18,8 +20,16 @@ module Reek
|
|
18
20
|
context.report(smell) if smell.recognise?(exp)
|
19
21
|
end
|
20
22
|
|
21
|
-
def
|
22
|
-
|
23
|
+
def hash # :nodoc:
|
24
|
+
report.hash
|
25
|
+
end
|
26
|
+
|
27
|
+
def <=>(other) # :nodoc:
|
28
|
+
self.report <=> other.report
|
29
|
+
end
|
30
|
+
|
31
|
+
def eql?(other) # :nodoc:
|
32
|
+
self.report.eql?(other.report)
|
23
33
|
end
|
24
34
|
|
25
35
|
def name
|
@@ -62,36 +72,13 @@ module Reek
|
|
62
72
|
class LongMethod < Smell
|
63
73
|
MAX_ALLOWED = 5
|
64
74
|
|
65
|
-
def
|
66
|
-
|
67
|
-
|
68
|
-
result
|
69
|
-
end
|
70
|
-
|
71
|
-
def recognise?(exp)
|
72
|
-
count_statements(exp) > MAX_ALLOWED
|
73
|
-
end
|
74
|
-
|
75
|
-
def detailed_report
|
76
|
-
"#{@context} has > #{MAX_ALLOWED} statements"
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
class LongBlock < Smell
|
81
|
-
MAX_ALLOWED = 5
|
82
|
-
|
83
|
-
def count_statements(exp)
|
84
|
-
result = exp.length - 1
|
85
|
-
result -= 1 if Array === exp[1] and exp[1][0] == :args
|
86
|
-
result
|
87
|
-
end
|
88
|
-
|
89
|
-
def recognise?(exp)
|
90
|
-
count_statements(exp) > MAX_ALLOWED
|
75
|
+
def recognise?(num_stmts)
|
76
|
+
@num_stmts = num_stmts
|
77
|
+
num_stmts > MAX_ALLOWED
|
91
78
|
end
|
92
79
|
|
93
80
|
def detailed_report
|
94
|
-
"#{@context} has
|
81
|
+
"#{@context} has approx #{@num_stmts} statements"
|
95
82
|
end
|
96
83
|
end
|
97
84
|
|
@@ -105,7 +92,7 @@ module Reek
|
|
105
92
|
max = calls.empty? ? 0 : calls.values.max
|
106
93
|
return false unless max > calls[:self]
|
107
94
|
receivers = calls.keys.select { |key| calls[key] == max }
|
108
|
-
@receiver = receivers.map {|r| Printer.print(r)}.sort.join('
|
95
|
+
@receiver = receivers.map {|r| Printer.print(r)}.sort.join(' and ')
|
109
96
|
return true
|
110
97
|
end
|
111
98
|
|
data/lib/reek/version.rb
CHANGED
@@ -25,8 +25,8 @@ describe MethodChecker, "(Feature Envy)" do
|
|
25
25
|
it 'should report simple parameter call' do
|
26
26
|
@cchk.check_source('def simple(arga) arga[3] end')
|
27
27
|
@rpt.length.should == 2
|
28
|
-
@rpt[
|
29
|
-
@rpt[
|
28
|
+
@rpt[1].should == UtilityFunction.new(@cchk)
|
29
|
+
@rpt[0].should == FeatureEnvy.new(@cchk, ':arga')
|
30
30
|
end
|
31
31
|
|
32
32
|
it 'should report highest affinity' do
|
@@ -38,7 +38,7 @@ describe MethodChecker, "(Feature Envy)" do
|
|
38
38
|
it 'should report multiple affinities' do
|
39
39
|
@cchk.check_source('def simple(arga) s1 = ""; s1.to_s; s2 = ""; s2.to_s; @m = 34; end')
|
40
40
|
@rpt.length.should == 1
|
41
|
-
@rpt[0].should == FeatureEnvy.new(@cchk, ':s1
|
41
|
+
@rpt[0].should == FeatureEnvy.new(@cchk, ':s1 and :s2')
|
42
42
|
end
|
43
43
|
|
44
44
|
it 'should not reference global variables' do
|
@@ -20,7 +20,31 @@ describe MethodChecker, "(Long Method)" do
|
|
20
20
|
it 'should report long methods' do
|
21
21
|
@cchk.check_source('def long(arga) alf = f(1);@bet = 2;@cut = 3;@dit = 4; @emp = 5;@fry = 6;end')
|
22
22
|
@rpt.length.should == 1
|
23
|
-
@rpt[0].should
|
23
|
+
@rpt[0].should be_instance_of(LongMethod)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should only report a long method once' do
|
27
|
+
source =<<EOS
|
28
|
+
def standard_entries(rbconfig)
|
29
|
+
@abc = rbconfig
|
30
|
+
rubypath = File.join(@abc['bindir'], @abc['ruby_install_name'] + c['EXEEXT'])
|
31
|
+
major = c['MAJOR'].to_i
|
32
|
+
minor = c['MINOR'].to_i
|
33
|
+
teeny = c['TEENY'].to_i
|
34
|
+
version = ""
|
35
|
+
if c['rubylibdir']
|
36
|
+
libruby = "/lib/ruby"
|
37
|
+
librubyver = "/lib/ruby/"
|
38
|
+
librubyverarch = "/lib/ruby/"
|
39
|
+
siteruby = "lib/ruby/version/site_ruby"
|
40
|
+
siterubyver = siteruby
|
41
|
+
siterubyverarch = "$siterubyver/['arch']}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
EOS
|
45
|
+
@cchk.check_source(source)
|
46
|
+
@rpt.length.should == 1
|
47
|
+
@rpt[0].should be_instance_of(LongMethod)
|
24
48
|
end
|
25
49
|
end
|
26
50
|
|
@@ -47,6 +71,5 @@ end
|
|
47
71
|
EOS
|
48
72
|
@cchk.check_source(src)
|
49
73
|
@rpt.length.should == 1
|
50
|
-
@rpt[0].report.should match(/block/)
|
51
74
|
end
|
52
75
|
end
|
@@ -16,5 +16,27 @@ describe MethodChecker, " nested iterators" do
|
|
16
16
|
@chk.check_source('def bad(fred) @fred.each {|item| item.each {|ting| ting.ting} } end')
|
17
17
|
@rpt.length.should == 1
|
18
18
|
end
|
19
|
+
|
20
|
+
it "should not report method with successive iterators" do
|
21
|
+
source =<<EOS
|
22
|
+
def bad(fred)
|
23
|
+
@fred.each {|item| item.each }
|
24
|
+
@jim.each {|item| item.each }
|
25
|
+
end
|
26
|
+
EOS
|
27
|
+
@chk.check_source(source)
|
28
|
+
@rpt.should be_empty
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should report nested iterators only once per method" do
|
32
|
+
source =<<EOS
|
33
|
+
def bad(fred)
|
34
|
+
@fred.each {|item| item.each {|part| @jim.send} }
|
35
|
+
@jim.each {|item| item.each {|piece| @fred.send} }
|
36
|
+
end
|
37
|
+
EOS
|
38
|
+
@chk.check_source(source)
|
39
|
+
@rpt.length.should == 1
|
40
|
+
end
|
19
41
|
end
|
20
42
|
|
data/spec/reek/report_spec.rb
CHANGED
@@ -1,12 +1,26 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
2
|
|
3
3
|
require 'reek/method_checker'
|
4
|
+
require 'reek/smells'
|
4
5
|
require 'reek/report'
|
5
6
|
|
6
7
|
include Reek
|
7
8
|
|
8
|
-
describe Report, "
|
9
|
+
describe Report, " when empty" do
|
10
|
+
before(:each) do
|
11
|
+
@rpt = Report.new
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should have zero length' do
|
15
|
+
@rpt.length.should == 0
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should claim to be empty' do
|
19
|
+
@rpt.should be_empty
|
20
|
+
end
|
21
|
+
end
|
9
22
|
|
23
|
+
describe Report, "to_s" do
|
10
24
|
before(:each) do
|
11
25
|
rpt = Report.new
|
12
26
|
chk = MethodChecker.new(rpt, 'Thing')
|
@@ -24,3 +38,12 @@ describe Report, "to_s" do
|
|
24
38
|
end
|
25
39
|
end
|
26
40
|
|
41
|
+
describe Report, " as a SortedSet" do
|
42
|
+
it 'should only add a smell once' do
|
43
|
+
rpt = Report.new
|
44
|
+
rpt << UtilityFunction.new(self)
|
45
|
+
rpt.length.should == 1
|
46
|
+
rpt << UtilityFunction.new(self)
|
47
|
+
rpt.length.should == 1
|
48
|
+
end
|
49
|
+
end
|
data/spec/reek/smell_spec.rb
CHANGED
@@ -10,8 +10,24 @@ describe Smell, "camel case converter" do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
it "should display correct name in report" do
|
13
|
-
|
14
|
-
smell = FeatureEnvy.new(mchk, [:lvar, :fred])
|
13
|
+
smell = FeatureEnvy.new(self, [:lvar, :fred])
|
15
14
|
smell.report.should match(/[#{smell.name}]/)
|
16
15
|
end
|
17
16
|
end
|
17
|
+
|
18
|
+
describe Smell, ' in comparisons' do
|
19
|
+
it 'should hash equal when the smell is the same' do
|
20
|
+
UtilityFunction.new(self).hash.should == UtilityFunction.new(self).hash
|
21
|
+
NestedIterators.new(self).hash.should == NestedIterators.new(self).hash
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should compare equal when the smell is the same' do
|
25
|
+
UtilityFunction.new(self).should == UtilityFunction.new(self)
|
26
|
+
NestedIterators.new(self).should == NestedIterators.new(self)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should compare equal when using <=>' do
|
30
|
+
(UtilityFunction.new(self) <=> UtilityFunction.new(self)).should == 0
|
31
|
+
(NestedIterators.new(self) <=> NestedIterators.new(self)).should == 0
|
32
|
+
end
|
33
|
+
end
|
data/website/index.html
CHANGED
@@ -33,7 +33,7 @@
|
|
33
33
|
<h1>Code smell detector</h1>
|
34
34
|
<div id="version" class="clickable" onclick='document.location = "http://rubyforge.org/projects/reek"; return false'>
|
35
35
|
<p>Get Version</p>
|
36
|
-
<a href="http://rubyforge.org/projects/reek" class="numbers">0.1.
|
36
|
+
<a href="http://rubyforge.org/projects/reek" class="numbers">0.1.1</a>
|
37
37
|
</div>
|
38
38
|
<h1>→ ‘reek’</h1>
|
39
39
|
|