kevinrutherford-reek 1.0.1 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/History.txt +42 -22
  2. data/config/defaults.reek +1 -0
  3. data/lib/reek.rb +1 -1
  4. data/lib/reek/block_context.rb +27 -5
  5. data/lib/reek/class_context.rb +5 -4
  6. data/lib/reek/code_parser.rb +26 -11
  7. data/lib/reek/method_context.rb +18 -12
  8. data/lib/reek/module_context.rb +1 -1
  9. data/lib/reek/name.rb +8 -1
  10. data/lib/reek/object_refs.rb +2 -3
  11. data/lib/reek/report.rb +11 -4
  12. data/lib/reek/sexp_formatter.rb +4 -46
  13. data/lib/reek/smells/large_class.rb +26 -7
  14. data/lib/reek/smells/long_parameter_list.rb +1 -1
  15. data/lib/reek/smells/smells.rb +4 -8
  16. data/lib/reek/source.rb +31 -4
  17. data/lib/reek/spec.rb +6 -0
  18. data/lib/reek/stop_context.rb +4 -16
  19. data/lib/reek/yield_call_context.rb +1 -3
  20. data/reek.gemspec +8 -5
  21. data/spec/reek/block_context_spec.rb +40 -0
  22. data/spec/reek/class_context_spec.rb +11 -0
  23. data/spec/reek/code_context_spec.rb +2 -1
  24. data/spec/reek/code_parser_spec.rb +0 -10
  25. data/spec/reek/config_spec.rb +2 -2
  26. data/spec/reek/method_context_spec.rb +14 -0
  27. data/spec/reek/name_spec.rb +13 -0
  28. data/spec/reek/object_refs_spec.rb +11 -9
  29. data/spec/reek/report_spec.rb +1 -1
  30. data/spec/reek/sexp_formatter_spec.rb +5 -4
  31. data/spec/reek/singleton_method_context_spec.rb +1 -1
  32. data/spec/reek/smells/duplication_spec.rb +2 -2
  33. data/spec/reek/smells/feature_envy_spec.rb +132 -36
  34. data/spec/reek/smells/large_class_spec.rb +48 -47
  35. data/spec/reek/smells/long_method_spec.rb +1 -1
  36. data/spec/reek/smells/long_parameter_list_spec.rb +4 -11
  37. data/spec/reek/smells/uncommunicative_name_spec.rb +6 -1
  38. data/spec/reek/smells/utility_function_spec.rb +6 -9
  39. data/spec/{samples → slow}/inline_spec.rb +14 -10
  40. data/spec/{samples → slow}/optparse_spec.rb +20 -12
  41. data/spec/{samples → slow}/redcloth_spec.rb +16 -8
  42. data/spec/{integration → slow}/reek_source_spec.rb +0 -0
  43. data/spec/{samples → slow/samples}/inline.rb +0 -0
  44. data/spec/{samples → slow/samples}/optparse.rb +0 -0
  45. data/spec/{samples → slow/samples}/redcloth.rb +0 -0
  46. data/spec/{integration → slow}/script_spec.rb +0 -0
  47. data/spec/{reek/source_spec.rb → slow/source_list_spec.rb} +3 -3
  48. data/spec/spec_helper.rb +2 -0
  49. data/tasks/rspec.rake +1 -1
  50. metadata +24 -12
@@ -28,7 +28,7 @@ describe Report, "to_s" do
28
28
  end
29
29
 
30
30
  it 'should place each detailed report on a separate line' do
31
- @report.length.should == 2
31
+ @report.should have_at_least(2).lines
32
32
  end
33
33
 
34
34
  it 'should mention every smell name' do
@@ -7,7 +7,7 @@ include Reek
7
7
 
8
8
  def should_print(example)
9
9
  it "should format #{example} correctly" do
10
- sexp = CodeParser.parse_tree_for(example)[0]
10
+ sexp = CodeParser.parse_tree_for(example)
11
11
  SexpFormatter.format(sexp).should == example
12
12
  end
13
13
  end
@@ -23,9 +23,10 @@ describe SexpFormatter do
23
23
  should_print 'obj.method(arg1, arg2)'
24
24
  should_print 'obj.method'
25
25
  should_print '$1'
26
- should_print 'o=q.downcase'
26
+ should_print 'o = q.downcase'
27
27
  should_print 'true'
28
28
  should_print '"-#{q}xxx#{z.size}"'
29
- should_print '0..5'
30
- should_print '0..temp'
29
+ should_print '(0..5)'
30
+ should_print '(0..temp)'
31
+ should_print 'result[opt] = false'
31
32
  end
@@ -11,7 +11,7 @@ describe SingletonMethodContext, 'outer_name' do
11
11
  it "should report full context" do
12
12
  element = StopContext.new
13
13
  element = ModuleContext.new(element, [0, :mod])
14
- element = SingletonMethodContext.new(element, [:defs, [:vcall, :a], :b, nil])
14
+ element = SingletonMethodContext.new(element, [:defs, [:call, nil, :a, [:arglist]], :b, nil])
15
15
  element.outer_name.should match(/mod::a\.b/)
16
16
  end
17
17
  end
@@ -13,7 +13,7 @@ describe Duplication, "repeated method calls" do
13
13
  end
14
14
 
15
15
  it 'should report repeated call to lvar' do
16
- 'def double_thing() other[@thing] + other[@thing] end'.should reek_only_of(:Duplication, /other\[@thing\]/)
16
+ 'def double_thing(other) other[@thing] + other[@thing] end'.should reek_only_of(:Duplication, /other\[@thing\]/)
17
17
  end
18
18
 
19
19
  it 'should report call parameters' do
@@ -50,7 +50,7 @@ describe Duplication, '#examine' do
50
50
  end
51
51
 
52
52
  it 'should return true when reporting a smell' do
53
- @mc.calls = {'x' => 47}
53
+ @mc.calls = {s(:call, nil, :other, s(:arglist)) => 47}
54
54
  @dup.examine(@mc, []).should == true
55
55
  end
56
56
 
@@ -6,7 +6,7 @@ require 'reek/stop_context'
6
6
  include Reek
7
7
  include Reek::Smells
8
8
 
9
- describe FeatureEnvy, 'with only messages to self' do
9
+ describe FeatureEnvy do
10
10
  it 'should not report use of self' do
11
11
  'def simple() self.to_s + self.to_i end'.should_not reek
12
12
  end
@@ -18,23 +18,26 @@ describe FeatureEnvy, 'with only messages to self' do
18
18
  it 'should not report vcall with argument' do
19
19
  'def simple(arga) func(17); end'.should_not reek
20
20
  end
21
- end
22
21
 
23
- describe FeatureEnvy, 'when the receiver is a parameter' do
24
22
  it 'should not report single use' do
25
- 'def no_envy(arga) arga.barg(@item) end'.should_not reek
23
+ 'def no_envy(arga)
24
+ arga.barg(@item)
25
+ end'.should_not reek
26
26
  end
27
27
 
28
28
  it 'should not report return value' do
29
- 'def no_envy(arga) arga.barg(@item); arga end'.should_not reek
29
+ 'def no_envy(arga)
30
+ arga.barg(@item)
31
+ arga
32
+ end'.should_not reek
30
33
  end
31
34
 
32
35
  it 'should report many calls to parameter' do
33
- 'def envy(arga) arga.b(arga) + arga.c(@fred) end'.should reek_only_of(:FeatureEnvy, /arga/)
36
+ 'def envy(arga)
37
+ arga.b(arga) + arga.c(@fred)
38
+ end'.should reek_only_of(:FeatureEnvy, /arga/)
34
39
  end
35
- end
36
40
 
37
- describe FeatureEnvy, 'when there are many possible receivers' do
38
41
  it 'should report highest affinity' do
39
42
  ruby = 'def total_envy
40
43
  fred = @item
@@ -56,9 +59,7 @@ describe FeatureEnvy, 'when there are many possible receivers' do
56
59
  ruby.should reek_of(:FeatureEnvy, /total/)
57
60
  ruby.should reek_of(:FeatureEnvy, /fred/)
58
61
  end
59
- end
60
62
 
61
- describe FeatureEnvy, 'when the receiver is external' do
62
63
  it 'should ignore global variables' do
63
64
  'def no_envy() $s2.to_a; $s2[@item] end'.should_not reek
64
65
  end
@@ -66,9 +67,7 @@ describe FeatureEnvy, 'when the receiver is external' do
66
67
  it 'should not report class methods' do
67
68
  'def simple() self.class.new.flatten_merge(self) end'.should_not reek
68
69
  end
69
- end
70
70
 
71
- describe FeatureEnvy, 'when the receiver is an ivar' do
72
71
  it 'should not report single use of an ivar' do
73
72
  'def no_envy() @item.to_a end'.should_not reek
74
73
  end
@@ -78,17 +77,132 @@ describe FeatureEnvy, 'when the receiver is an ivar' do
78
77
  end
79
78
 
80
79
  it 'should not report ivar usage in a parameter' do
81
- 'def no_envy; @item.price + tax(@item) - savings(@item) end'.should_not reek
80
+ 'def no_envy
81
+ @item.price + tax(@item) - savings(@item)
82
+ end'.should_not reek
82
83
  end
83
84
 
84
85
  it 'should not be fooled by duplication' do
85
- ruby = Source.from_s('def feed(thing); @cow.feed_to(thing.pig); @duck.feed_to(thing.pig); end')
86
- ruby.should reek_only_of(:Duplication, /thing.pig/)
86
+ 'def feed(thing)
87
+ @cow.feed_to(thing.pig)
88
+ @duck.feed_to(thing.pig)
89
+ end'.should reek_only_of(:Duplication, /thing.pig/)
87
90
  end
88
91
 
89
92
  it 'should count local calls' do
90
- ruby = Source.from_s('def feed(thing); cow.feed_to(thing.pig); duck.feed_to(thing.pig); end')
91
- ruby.should reek_only_of(:Duplication, /thing.pig/)
93
+ 'def feed(thing)
94
+ cow.feed_to(thing.pig)
95
+ duck.feed_to(thing.pig)
96
+ end'.should reek_only_of(:Duplication, /thing.pig/)
97
+ end
98
+
99
+ it 'should not report single use of an lvar' do
100
+ 'def no_envy()
101
+ lv = @item
102
+ lv.to_a
103
+ end'.should_not reek
104
+ end
105
+
106
+ it 'should not report returning an lvar' do
107
+ 'def no_envy()
108
+ lv = @item
109
+ lv.to_a
110
+ lv
111
+ end'.should_not reek
112
+ end
113
+
114
+ it 'should report many calls to lvar' do
115
+ 'def envy
116
+ lv = @item
117
+ lv.price + lv.tax
118
+ end'.should reek_only_of(:FeatureEnvy, /lv/)
119
+ #
120
+ # def moved_version
121
+ # price + tax
122
+ # end
123
+ #
124
+ # def envy
125
+ # @item.moved_version
126
+ # end
127
+ end
128
+
129
+ it 'ignores lvar usage in a parameter' do
130
+ 'def no_envy
131
+ lv = @item
132
+ lv.price + tax(lv) - savings(lv)
133
+ end'.should_not reek
134
+ end
135
+
136
+ it 'reports the most-used ivar' do
137
+ pending('bug')
138
+ 'def func
139
+ @other.a
140
+ @other.b
141
+ @nother.c
142
+ end'.should reek_of(:FeatureEnvy, /@other/)
143
+ #
144
+ # def other.func(me)
145
+ # a
146
+ # b
147
+ # me.nother_c
148
+ # end
149
+ #
150
+ end
151
+
152
+ it 'ignores multiple ivars' do
153
+ 'def func
154
+ @other.a
155
+ @other.b
156
+ @nother.c
157
+ @nother.d
158
+ end'.should_not reek
159
+ #
160
+ # def other.func(me)
161
+ # a
162
+ # b
163
+ # me.nother_c
164
+ # me.nother_d
165
+ # end
166
+ #
167
+ end
168
+
169
+ it 'ignores frequent use of a call' do
170
+ 'def func
171
+ other.a
172
+ other.b
173
+ nother.c
174
+ end'.should_not reek_of(:FeatureEnvy)
175
+ end
176
+
177
+ it 'counts self references correctly' do
178
+ 'def adopt!(other)
179
+ other.keys.each do |key|
180
+ ov = other[key]
181
+ if Array === ov and has_key?(key)
182
+ self[key] += ov
183
+ else
184
+ self[key] = ov
185
+ end
186
+ end
187
+ self
188
+ end'.should_not reek
189
+ end
190
+ end
191
+
192
+ describe FeatureEnvy do
193
+ it 'counts references to self correctly' do
194
+ ruby = <<EOS
195
+ def report
196
+ unless @report
197
+ @report = Report.new
198
+ cf = SmellConfig.new
199
+ cf = cf.load_local(@dir) if @dir
200
+ CodeParser.new(@report, cf.smell_listeners).check_source(@source)
201
+ end
202
+ @report
203
+ end
204
+ EOS
205
+ ruby.should reek_only_of(:LongMethod)
92
206
  end
93
207
  end
94
208
 
@@ -104,26 +218,8 @@ describe FeatureEnvy, '#examine' do
104
218
  @context.refs.record_ref([:lvar, :thing])
105
219
  @fe.examine(@context, []).should == true
106
220
  end
107
-
221
+
108
222
  it 'should return false when not reporting a smell' do
109
223
  @fe.examine(@context, []).should == false
110
224
  end
111
225
  end
112
-
113
- describe FeatureEnvy, 'when the receiver is an lvar' do
114
- it 'should not report single use of an lvar' do
115
- 'def no_envy() lv = @item; lv.to_a end'.should_not reek
116
- end
117
-
118
- it 'should not report returning an lvar' do
119
- 'def no_envy() lv = @item; lv.to_a; lv end'.should_not reek
120
- end
121
-
122
- it 'should report many calls to lvar' do
123
- 'def envy; lv = @item; lv.price + lv.tax end'.should reek_only_of(:FeatureEnvy, /lv/)
124
- end
125
-
126
- it 'should not report lvar usage in a parameter' do
127
- 'def no_envy; lv = @item; lv.price + tax(lv) - savings(lv) end'.should_not reek
128
- end
129
- end
@@ -8,18 +8,19 @@ include Reek::Smells
8
8
 
9
9
  describe LargeClass do
10
10
 
11
- class BigOne
12
- 26.times do |i|
13
- define_method "method#{i}".to_sym do
14
- @melting
11
+ it 'should report large class' do
12
+ class BigOne
13
+ 26.times do |i|
14
+ define_method "method#{i}".to_sym do
15
+ @melting
16
+ end
15
17
  end
16
18
  end
19
+ BigOne.should reek_only_of(:LargeClass, /BigOne/)
17
20
  end
21
+ end
18
22
 
19
- before(:each) do
20
- @rpt = Report.new
21
- @cchk = CodeParser.new(@rpt, SmellConfig.new.smell_listeners)
22
- end
23
+ describe LargeClass do
23
24
 
24
25
  it 'should not report short class' do
25
26
  class ShortClass
@@ -30,57 +31,57 @@ describe LargeClass do
30
31
  def method5() @var5; end
31
32
  def method6() @var6; end
32
33
  end
33
- @cchk.check_object(ShortClass)
34
- @rpt.should be_empty
34
+ ShortClass.should_not reek
35
35
  end
36
+ end
36
37
 
37
- it 'should report large class' do
38
- @cchk.check_object(BigOne)
39
- @rpt.length.should == 1
40
- end
38
+ describe LargeClass, 'when exceptions are listed' do
41
39
 
42
- it 'should report class name' do
43
- @cchk.check_object(BigOne)
44
- @rpt[0].report.should match(/BigOne/)
40
+ before(:each) do
41
+ @rpt = Report.new
42
+ @ctx = ClassContext.create(StopContext.new, [0, :Humungous])
43
+ 30.times { |num| @ctx.record_method("method#{num}") }
44
+ @config = LargeClass.default_config
45
45
  end
46
46
 
47
- describe 'when exceptions are listed' do
48
- before :each do
49
- @ctx = ClassContext.create(StopContext.new, [0, :Humungous])
50
- 30.times { |num| @ctx.record_method("method#{num}") }
51
- @config = LargeClass.default_config
52
- end
47
+ it 'should ignore first excepted name' do
48
+ @config[LargeClass::EXCLUDE_KEY] = ['Humungous']
49
+ lc = LargeClass.new(@config)
50
+ lc.examine(@ctx, @rpt).should == false
51
+ @rpt.length.should == 0
52
+ end
53
53
 
54
- it 'should ignore first excepted name' do
55
- @config[LargeClass::EXCLUDE_KEY] = ['Humungous']
56
- lc = LargeClass.new(@config)
57
- lc.examine(@ctx, @rpt).should == false
58
- @rpt.length.should == 0
59
- end
54
+ it 'should ignore second excepted name' do
55
+ @config[LargeClass::EXCLUDE_KEY] = ['Oversized', 'Humungous']
56
+ lc = LargeClass.new(@config)
57
+ lc.examine(@ctx, @rpt).should == false
58
+ @rpt.length.should == 0
59
+ end
60
60
 
61
- it 'should ignore second excepted name' do
62
- @config[LargeClass::EXCLUDE_KEY] = ['Oversized', 'Humungous']
63
- lc = LargeClass.new(@config)
64
- lc.examine(@ctx, @rpt).should == false
65
- @rpt.length.should == 0
66
- end
61
+ it 'should report non-excepted name' do
62
+ @config[LargeClass::EXCLUDE_KEY] = ['SmellMe']
63
+ lc = LargeClass.new(@config)
64
+ lc.examine(@ctx, @rpt).should == true
65
+ @rpt.length.should == 1
66
+ end
67
+ end
67
68
 
68
- it 'should report non-excepted name' do
69
- @config[LargeClass::EXCLUDE_KEY] = ['SmellMe']
70
- lc = LargeClass.new(@config)
71
- lc.examine(@ctx, @rpt).should == true
72
- @rpt.length.should == 1
69
+ describe LargeClass, 'counting instance variables' do
70
+ it 'warns about class with 10 ivars' do
71
+ class ManyIvars
72
+ def method
73
+ @vara = @varb = @varc = @vard = @vare
74
+ @varf = @varg = @varh = @vari = @varj
75
+ end
73
76
  end
77
+ ManyIvars.should reek_of(:LargeClass, /10/)
74
78
  end
75
- end
76
79
 
77
- describe LargeClass do
78
- it 'should not report empty class in another module' do
79
- 'class Treetop::Runtime::SyntaxNode; end'.should_not reek
80
+ it 'ignores class with only a couple of ivars' do
81
+ LargeClass.should_not reek_of(:LargeClass)
80
82
  end
81
83
 
82
- it 'should deal with :: scoped names' do
83
- element = ClassContext.create(StopContext.new, [:colon2, [:colon2, [:const, :Treetop], :Runtime], :SyntaxNode])
84
- element.num_methods.should == 0
84
+ it 'ignores fq class with only a couple of ivars' do
85
+ Reek::Smells::LargeClass.should_not reek_of(:LargeClass)
85
86
  end
86
87
  end
@@ -5,7 +5,7 @@ require 'reek/report'
5
5
 
6
6
  include Reek
7
7
 
8
- describe CodeParser, "(Long Method)" do
8
+ describe LongMethod do
9
9
  it 'should not report short methods' do
10
10
  'def short(arga) alf = f(1);@bet = 2;@cut = 3;@dit = 4; @emp = 5;end'.should_not reek
11
11
  end
@@ -7,7 +7,7 @@ require 'reek/report'
7
7
  include Reek
8
8
  include Reek::Smells
9
9
 
10
- describe CodeParser, "(Long Parameter List)" do
10
+ describe LongParameterList do
11
11
 
12
12
  describe 'for methods with few parameters' do
13
13
  it 'should report nothing for no parameters' do
@@ -25,7 +25,7 @@ describe CodeParser, "(Long Parameter List)" do
25
25
  it 'should not report inner block with too many parameters' do
26
26
  'def simple(yep,zero); m[3]; rand(34); f.each { |arga, argb, argc, argd| true}; end'.should_not reek
27
27
  end
28
-
28
+
29
29
  describe 'and default values' do
30
30
  it 'should report nothing for 1 parameter' do
31
31
  'def simple(zero=nil) f(3);false end'.should_not reek
@@ -57,20 +57,13 @@ describe CodeParser, "(Long Parameter List)" do
57
57
  end
58
58
 
59
59
  describe 'in a class' do
60
- before(:each) do
61
- @rpt = Report.new
62
- @cchk = CodeParser.new(@rpt, SmellConfig.new.smell_listeners)
63
- end
64
-
65
60
  class InnerTest
66
61
  def xyzero(arga,argb) f(3);true end
67
62
  def abc(argx,yep,zero,argm) f(3);false end
68
63
  end
69
64
 
70
65
  it 'should only report long param list' do
71
- @cchk.check_object(InnerTest)
72
- @rpt.length.should == 1
73
- @rpt[0].report.should match(/Long Parameter List/)
66
+ InnerTest.should reek_only_of(:LongParameterList, /abc/)
74
67
  end
75
68
  end
76
69
  end
@@ -83,7 +76,7 @@ describe CodeParser, "(Long Parameter List)" do
83
76
  'def simple(arga, argb, &blk) f(3);yield a,b; end'.should_not reek
84
77
  end
85
78
  it 'should report yield with many parameters' do
86
- 'def simple(arga, argb, &blk) f(3);yield a,b,a,b; end'.should reek_only_of(:LongYieldList, /simple/, /yields/, /4/)
79
+ 'def simple(arga, argb, &blk) f(3);yield arga,argb,arga,argb; end'.should reek_only_of(:LongYieldList, /simple/, /yields/, /4/)
87
80
  end
88
81
  it 'should not report yield of a long expression' do
89
82
  'def simple(arga, argb, &blk) f(3);yield(if @dec then argb else 5+3 end); end'.should_not reek