kevinrutherford-reek 1.0.1 → 1.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.
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