reek 0.2.2 → 0.2.3
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 +11 -0
- data/README.txt +16 -5
- data/lib/reek/checker.rb +13 -3
- data/lib/reek/method_checker.rb +54 -47
- data/lib/reek/object_refs.rb +53 -0
- data/lib/reek/printer.rb +65 -12
- data/lib/reek/smells.rb +22 -29
- data/lib/reek/version.rb +1 -1
- data/spec/integration_spec.rb +3 -11
- data/spec/reek/feature_envy_spec.rb +163 -24
- data/spec/reek/long_method_spec.rb +11 -11
- data/spec/reek/method_checker_spec.rb +34 -0
- data/spec/reek/nested_iterators_spec.rb +4 -4
- data/spec/reek/object_refs_spec.rb +129 -0
- data/spec/reek/printer_spec.rb +30 -0
- data/spec/reek/report_spec.rb +3 -3
- data/spec/reek/smell_spec.rb +1 -1
- data/spec/reek/utility_function_spec.rb +27 -0
- data/spec/reek_source_spec.rb +11 -0
- data/spec/samples/inline.rb +704 -0
- data/spec/samples/inline.reek +17 -0
- data/spec/samples/optparse.reek +10 -16
- data/spec/samples/redcloth.reek +6 -19
- data/tasks/samples.rake +2 -0
- data/website/index.html +26 -63
- data/website/index.txt +4 -12
- metadata +8 -2
data/lib/reek/version.rb
CHANGED
data/spec/integration_spec.rb
CHANGED
@@ -3,7 +3,7 @@ require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
3
3
|
require 'reek/version'
|
4
4
|
|
5
5
|
describe 'Reek version number' do
|
6
|
-
it 'should
|
6
|
+
it 'should report the correct value' do
|
7
7
|
actual = `ruby -Ilib bin/reek --version`.split
|
8
8
|
actual[0].should == 'reek'
|
9
9
|
actual[1].should == Reek::VERSION::STRING
|
@@ -20,6 +20,7 @@ describe 'Integration test:' do
|
|
20
20
|
|
21
21
|
it 'should report the correct smells' do
|
22
22
|
actual = `ruby -Ilib bin/reek #{source} 2>/dev/null`.split(/\n/)
|
23
|
+
actual.length.should == @expected.length
|
23
24
|
@expected.zip(actual).each do |p|
|
24
25
|
actual = p[1] ? p[1].chomp : p[1]
|
25
26
|
actual.should == p[0]
|
@@ -28,6 +29,7 @@ describe 'Integration test:' do
|
|
28
29
|
|
29
30
|
it 'should report the correct smells in smell order' do
|
30
31
|
actual = `ruby -Ilib bin/reek --sort smell #{source} 2>/dev/null`.split(/\n/)
|
32
|
+
actual.length.should == @expected.length
|
31
33
|
@expected.sort.zip(actual).each do |p|
|
32
34
|
actual = p[1] ? p[1].chomp : p[1]
|
33
35
|
actual.should == p[0]
|
@@ -36,13 +38,3 @@ describe 'Integration test:' do
|
|
36
38
|
end
|
37
39
|
end
|
38
40
|
end
|
39
|
-
|
40
|
-
describe 'Reek source code:' do
|
41
|
-
Dir['lib/**/*.rb'].each do |source|
|
42
|
-
describe source do
|
43
|
-
it 'should report no smells' do
|
44
|
-
`ruby -Ilib bin/reek #{source}`.should == "\n"
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
@@ -1,49 +1,87 @@
|
|
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
|
9
|
-
|
9
|
+
describe FeatureEnvy, 'with only messages to self' do
|
10
10
|
before(:each) do
|
11
11
|
@rpt = Report.new
|
12
12
|
@cchk = MethodChecker.new(@rpt, 'Thing')
|
13
13
|
end
|
14
14
|
|
15
|
-
it 'should not report
|
16
|
-
@cchk.check_source('def simple(
|
15
|
+
it 'should not report use of self' do
|
16
|
+
@cchk.check_source('def simple() self.to_s + self.to_i end')
|
17
|
+
@rpt.should be_empty
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should not report vcall with no argument' do
|
21
|
+
@cchk.check_source('def simple() func + grunc end')
|
22
|
+
@rpt.should be_empty
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should not report vcall with argument' do
|
26
|
+
@cchk.check_source('def simple(arga) func(17) + grunc(arga) end')
|
17
27
|
@rpt.should be_empty
|
18
28
|
end
|
29
|
+
end
|
19
30
|
|
20
|
-
|
21
|
-
|
22
|
-
@rpt
|
31
|
+
describe FeatureEnvy, 'when the receiver is a parameter' do
|
32
|
+
before(:each) do
|
33
|
+
@rpt = Report.new
|
34
|
+
@cchk = MethodChecker.new(@rpt, 'Thing')
|
23
35
|
end
|
24
36
|
|
25
|
-
it 'should report
|
26
|
-
@cchk.check_source('def
|
27
|
-
@rpt.length.should ==
|
28
|
-
|
29
|
-
|
37
|
+
it 'should not report single use' do
|
38
|
+
@cchk.check_source('def no_envy(arga) arga.barg(@item) end')
|
39
|
+
@rpt.length.should == 0
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should not report return value' do
|
43
|
+
@cchk.check_source('def no_envy(arga) arga.barg(@item); arga end')
|
44
|
+
@rpt.length.should == 0
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should report many calls to parameter' do
|
48
|
+
@cchk.check_source('def envy(arga) arga.b(arga) + arga.c(@fred) end')
|
49
|
+
@rpt.length.should == 1
|
50
|
+
FeatureEnvy.should === @rpt[0]
|
51
|
+
@rpt[0].to_s.should match(/arga/)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe FeatureEnvy, 'when there are many possible receivers' do
|
56
|
+
before(:each) do
|
57
|
+
@rpt = Report.new
|
58
|
+
@cchk = MethodChecker.new(@rpt, 'Thing')
|
30
59
|
end
|
31
60
|
|
32
61
|
it 'should report highest affinity' do
|
33
|
-
@cchk.check_source('def
|
62
|
+
@cchk.check_source('def total_envy() @total += @item.price; @total += @item.tax; @total *= 1.15 end')
|
34
63
|
@rpt.length.should == 1
|
35
|
-
@rpt[0]
|
64
|
+
FeatureEnvy.should === @rpt[0]
|
65
|
+
@rpt[0].to_s.should match(/@total/)
|
36
66
|
end
|
37
67
|
|
38
68
|
it 'should report multiple affinities' do
|
39
|
-
@cchk.check_source('def
|
69
|
+
@cchk.check_source('def total_envy() @total += @item.price; @total += @item.tax end')
|
40
70
|
@rpt.length.should == 1
|
41
|
-
@rpt[0].should
|
71
|
+
@rpt[0].to_s.should match(/@total/)
|
72
|
+
@rpt[0].to_s.should match(/@item/)
|
42
73
|
end
|
74
|
+
end
|
43
75
|
|
44
|
-
|
45
|
-
|
46
|
-
@rpt
|
76
|
+
describe FeatureEnvy, 'when the receiver is external' do
|
77
|
+
before(:each) do
|
78
|
+
@rpt = Report.new
|
79
|
+
@cchk = MethodChecker.new(@rpt, 'Thing')
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should ignore global variables' do
|
83
|
+
@cchk.check_source('def no_envy() $s2.to_a; $s2[@item] end')
|
84
|
+
@rpt.should be_empty
|
47
85
|
end
|
48
86
|
|
49
87
|
it 'should not report class methods' do
|
@@ -52,10 +90,111 @@ describe MethodChecker, "(Feature Envy)" do
|
|
52
90
|
end
|
53
91
|
end
|
54
92
|
|
55
|
-
describe FeatureEnvy, '
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
93
|
+
describe FeatureEnvy, 'when the receiver is an ivar' do
|
94
|
+
before(:each) do
|
95
|
+
@rpt = Report.new
|
96
|
+
@cchk = MethodChecker.new(@rpt, 'Thing')
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should not report single use of an ivar' do
|
100
|
+
@cchk.check_source('def no_envy() @item.to_a end')
|
101
|
+
@rpt.length.should == 0
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'should not report returning an ivar' do
|
105
|
+
@cchk.check_source('def no_envy() @item.to_a; @item end')
|
106
|
+
@rpt.length.should == 0
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should report many calls to ivar' do
|
110
|
+
@cchk.check_source('def envy; @item.price + @item.tax end')
|
111
|
+
@rpt.length.should == 1
|
112
|
+
FeatureEnvy.should === @rpt[0]
|
113
|
+
@rpt[0].to_s.should match(/@item/)
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'should not report ivar usage in a parameter' do
|
117
|
+
@cchk.check_source('def no_envy; @item.price + tax(@item) - savings(@item) end')
|
118
|
+
@rpt.should be_empty
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe FeatureEnvy, 'when the receiver is an lvar' do
|
123
|
+
before(:each) do
|
124
|
+
@rpt = Report.new
|
125
|
+
@cchk = MethodChecker.new(@rpt, 'Thing')
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'should not report single use of an lvar' do
|
129
|
+
@cchk.check_source('def no_envy() lv = @item; lv.to_a end')
|
130
|
+
@rpt.length.should == 0
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'should not report returning an lvar' do
|
134
|
+
@cchk.check_source('def no_envy() lv = @item; lv.to_a; lv end')
|
135
|
+
@rpt.length.should == 0
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'should report many calls to lvar' do
|
139
|
+
@cchk.check_source('def envy; lv = @item; lv.price + lv.tax end')
|
140
|
+
@rpt.length.should == 1
|
141
|
+
FeatureEnvy.should === @rpt[0]
|
142
|
+
@rpt[0].to_s.should match(/lv/)
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'should not report lvar usage in a parameter' do
|
146
|
+
@cchk.check_source('def no_envy; lv = @item; lv.price + tax(lv) - savings(lv) end')
|
147
|
+
@rpt.should be_empty
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe FeatureEnvy, 'when the receiver is a returned value' do
|
152
|
+
before(:each) do
|
153
|
+
@rpt = Report.new
|
154
|
+
@cchk = MethodChecker.new(@rpt, 'Thing')
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should not report single use of a return value' do
|
158
|
+
@cchk.check_source('def no_envy() f.g.price end')
|
159
|
+
@rpt.length.should == 0
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'should not report return value' do
|
163
|
+
@cchk.check_source('def no_envy() f.g.wibble; f.g end')
|
164
|
+
@rpt.length.should == 1
|
165
|
+
FeatureEnvy.should === @rpt[0]
|
166
|
+
@rpt[0].to_s.should match(/f/)
|
167
|
+
@rpt[0].to_s.should_not match(/f.g/)
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'should report many calls to a returned value' do
|
171
|
+
@cchk.check_source('def envy; f.g.price + f.g.tax end')
|
172
|
+
@rpt.length.should == 1
|
173
|
+
FeatureEnvy.should === @rpt[0]
|
174
|
+
@rpt[0].to_s.should match(/f.g/)
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'should not report value usage in a parameter' do
|
178
|
+
@cchk.check_source('def no_envy; f.g.price + tax(f.g) - savings(f.g) end')
|
179
|
+
@rpt.should be_empty
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe FeatureEnvy, 'when the receiver is a dvar' do
|
184
|
+
before(:each) do
|
185
|
+
@rpt = Report.new
|
186
|
+
@cchk = MethodChecker.new(@rpt, 'Thing')
|
60
187
|
end
|
188
|
+
|
189
|
+
it "should not report method with successive iterators" do
|
190
|
+
pending('this is a bug!')
|
191
|
+
source =<<EOS
|
192
|
+
def bad(fred)
|
193
|
+
@fred.each {|item| item.each }
|
194
|
+
@jim.each {|item| item.each }
|
195
|
+
end
|
196
|
+
EOS
|
197
|
+
@chk.check_source(source)
|
198
|
+
@rpt.should be_empty
|
199
|
+
end
|
61
200
|
end
|
@@ -27,18 +27,18 @@ describe MethodChecker, "(Long Method)" do
|
|
27
27
|
source =<<EOS
|
28
28
|
def standard_entries(rbconfig)
|
29
29
|
@abc = rbconfig
|
30
|
-
rubypath = File.join(@abc['bindir'], @
|
31
|
-
major =
|
32
|
-
minor =
|
33
|
-
teeny =
|
30
|
+
rubypath = File.join(@abc['bindir'], @abcf['ruby_install_name'] + cff['EXEEXT'])
|
31
|
+
major = yyy['MAJOR'].to_i
|
32
|
+
minor = zzz['MINOR'].to_i
|
33
|
+
teeny = ccc['TEENY'].to_i
|
34
34
|
version = ""
|
35
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']}"
|
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
42
|
end
|
43
43
|
end
|
44
44
|
EOS
|
@@ -59,7 +59,7 @@ describe MethodChecker, "(Long Block)" do
|
|
59
59
|
src = <<EOS
|
60
60
|
def long(arga)
|
61
61
|
f(3)
|
62
|
-
|
62
|
+
self.each do |xyzero|
|
63
63
|
xyzero = 1
|
64
64
|
xyzero = 2
|
65
65
|
xyzero = 3
|
@@ -31,3 +31,37 @@ describe MethodChecker, 'when given a C extension' do
|
|
31
31
|
@cchk.check_object(Enumerable)
|
32
32
|
end
|
33
33
|
end
|
34
|
+
|
35
|
+
describe MethodChecker, 'when a yield is the receiver' do
|
36
|
+
it 'should report no problems' do
|
37
|
+
source = 'def values(*args)
|
38
|
+
@to_sql += case
|
39
|
+
when block_given? then " #{yield.to_sql}"
|
40
|
+
else " values (#{args.to_sql})"
|
41
|
+
end
|
42
|
+
self
|
43
|
+
end'
|
44
|
+
rpt = Report.new
|
45
|
+
chk = MethodChecker.new(rpt, 'Thing')
|
46
|
+
chk.check_source(source)
|
47
|
+
rpt.should be_empty
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe MethodChecker, '#is_override?' do
|
52
|
+
it 'should be false for non-override method' do
|
53
|
+
MethodChecker.is_override?('String', 'gsub').should == false
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should be true for overriding method' do
|
57
|
+
MethodChecker.is_override?('MethodChecker', 'to_s').should == true
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should be false for non-existent class' do
|
61
|
+
MethodChecker.is_override?('Flibble', 'to_s').should == false
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should be true for smells' do
|
65
|
+
MethodChecker.is_override?('UtilityFunction', 'recognise?').should == true
|
66
|
+
end
|
67
|
+
end
|
@@ -21,8 +21,8 @@ describe MethodChecker, " nested iterators" do
|
|
21
21
|
source =<<EOS
|
22
22
|
def bad(fred)
|
23
23
|
@fred.each {|item| item.each }
|
24
|
-
@jim.each {|
|
25
|
-
end
|
24
|
+
@jim.each {|ting| ting.each }
|
25
|
+
end
|
26
26
|
EOS
|
27
27
|
@chk.check_source(source)
|
28
28
|
@rpt.should be_empty
|
@@ -31,8 +31,8 @@ EOS
|
|
31
31
|
it "should report nested iterators only once per method" do
|
32
32
|
source =<<EOS
|
33
33
|
def bad(fred)
|
34
|
-
@fred.each {|item| item.each {|part| @
|
35
|
-
@jim.each {|
|
34
|
+
@fred.each {|item| item.each {|part| @joe.send} }
|
35
|
+
@jim.each {|ting| ting.each {|piece| @hal.send} }
|
36
36
|
end
|
37
37
|
EOS
|
38
38
|
@chk.check_source(source)
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
|
3
|
+
require 'reek/object_refs'
|
4
|
+
|
5
|
+
include Reek
|
6
|
+
|
7
|
+
describe ObjectRefs, 'when empty' do
|
8
|
+
before(:each) do
|
9
|
+
@refs = ObjectRefs.new
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should report one ref to self' do
|
13
|
+
@refs.refs_to_self.should == 1
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe ObjectRefs, 'with no refs to self' do
|
18
|
+
before(:each) do
|
19
|
+
@refs = ObjectRefs.new
|
20
|
+
@refs.record_ref('a')
|
21
|
+
@refs.record_ref('b')
|
22
|
+
@refs.record_ref('a')
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should report no refs to self' do
|
26
|
+
@refs.refs_to_self.should == 1
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should report :a as the max' do
|
30
|
+
@refs.max_keys.should == ['a']
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should not report self as the max' do
|
34
|
+
@refs.self_is_max?.should == false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe ObjectRefs, 'with one ref to self' do
|
39
|
+
before(:each) do
|
40
|
+
@refs = ObjectRefs.new
|
41
|
+
@refs.record_ref('a')
|
42
|
+
@refs.record_ref('b')
|
43
|
+
@refs.record_ref('a')
|
44
|
+
@refs.record_reference_to_self
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should report 1 ref to self' do
|
48
|
+
@refs.refs_to_self.should == 2
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should report self among the max' do
|
52
|
+
@refs.max_keys.should be_include('a')
|
53
|
+
@refs.max_keys.should be_include(Sexp.from_array([:lit, :self]))
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should report self as the max' do
|
57
|
+
@refs.self_is_max?.should == true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe ObjectRefs, 'with many refs to self' do
|
62
|
+
before(:each) do
|
63
|
+
@refs = ObjectRefs.new
|
64
|
+
@refs.record_reference_to_self
|
65
|
+
@refs.record_ref('a')
|
66
|
+
@refs.record_reference_to_self
|
67
|
+
@refs.record_ref('b')
|
68
|
+
@refs.record_ref('a')
|
69
|
+
@refs.record_reference_to_self
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should report all refs to self' do
|
73
|
+
@refs.refs_to_self.should == 4
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should report self among the max' do
|
77
|
+
@refs.max_keys.should == [Sexp.from_array([:lit, :self])]
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should report self as the max' do
|
81
|
+
@refs.self_is_max?.should == true
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe ObjectRefs, 'when self is not the only max' do
|
86
|
+
before(:each) do
|
87
|
+
@refs = ObjectRefs.new
|
88
|
+
@refs.record_ref('a')
|
89
|
+
@refs.record_reference_to_self
|
90
|
+
@refs.record_ref('b')
|
91
|
+
@refs.record_ref('a')
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should report all refs to self' do
|
95
|
+
@refs.refs_to_self.should == 2
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should report self among the max' do
|
99
|
+
@refs.max_keys.should be_include('a')
|
100
|
+
@refs.max_keys.should be_include(Sexp.from_array([:lit, :self]))
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should report self as the max' do
|
104
|
+
@refs.self_is_max?.should == true
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe ObjectRefs, 'when self is not among the max' do
|
109
|
+
before(:each) do
|
110
|
+
@refs = ObjectRefs.new
|
111
|
+
@refs.record_ref('a')
|
112
|
+
@refs.record_ref('b')
|
113
|
+
@refs.record_ref('a')
|
114
|
+
@refs.record_ref('b')
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'should report all refs to self' do
|
118
|
+
@refs.refs_to_self.should == 1
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'should not report self among the max' do
|
122
|
+
@refs.max_keys.should be_include('a')
|
123
|
+
@refs.max_keys.should be_include('b')
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'should not report self as the max' do
|
127
|
+
@refs.self_is_max?.should == false
|
128
|
+
end
|
129
|
+
end
|