reek 1.2.2 → 1.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.
Files changed (59) hide show
  1. data/History.txt +4 -0
  2. data/Rakefile +0 -1
  3. data/config/defaults.reek +4 -6
  4. data/features/masking_smells.feature +9 -9
  5. data/features/options.feature +2 -2
  6. data/features/profile.feature +34 -0
  7. data/features/rake_task.feature +74 -0
  8. data/features/reports.feature +1 -1
  9. data/features/samples.feature +4 -4
  10. data/features/stdin.feature +1 -1
  11. data/features/step_definitions/reek_steps.rb +11 -7
  12. data/features/support/env.rb +26 -18
  13. data/lib/reek.rb +1 -1
  14. data/lib/reek/adapters/application.rb +9 -2
  15. data/lib/reek/adapters/command_line.rb +2 -2
  16. data/lib/reek/adapters/source.rb +4 -1
  17. data/lib/reek/adapters/spec.rb +1 -3
  18. data/lib/reek/block_context.rb +14 -8
  19. data/lib/reek/class_context.rb +6 -66
  20. data/lib/reek/code_context.rb +10 -0
  21. data/lib/reek/code_parser.rb +25 -53
  22. data/lib/reek/configuration.rb +12 -6
  23. data/lib/reek/if_context.rb +2 -3
  24. data/lib/reek/method_context.rb +3 -12
  25. data/lib/reek/module_context.rb +30 -22
  26. data/lib/reek/name.rb +2 -0
  27. data/lib/reek/object_refs.rb +0 -3
  28. data/lib/reek/sexp_formatter.rb +0 -2
  29. data/lib/reek/smells/class_variable.rb +17 -4
  30. data/lib/reek/smells/control_couple.rb +3 -10
  31. data/lib/reek/smells/data_clump.rb +10 -10
  32. data/lib/reek/smells/feature_envy.rb +1 -8
  33. data/lib/reek/smells/large_class.rb +3 -3
  34. data/lib/reek/smells/simulated_polymorphism.rb +17 -3
  35. data/lib/reek/smells/smell_detector.rb +11 -2
  36. data/lib/reek/smells/utility_function.rb +1 -1
  37. data/lib/reek/sniffer.rb +0 -8
  38. data/lib/reek/stop_context.rb +1 -1
  39. data/lib/reek/tree_dresser.rb +74 -0
  40. data/reek.gemspec +3 -3
  41. data/spec/reek/block_context_spec.rb +6 -6
  42. data/spec/reek/class_context_spec.rb +2 -23
  43. data/spec/reek/code_context_spec.rb +149 -67
  44. data/spec/reek/code_parser_spec.rb +0 -102
  45. data/spec/reek/method_context_spec.rb +4 -4
  46. data/spec/reek/singleton_method_context_spec.rb +1 -1
  47. data/spec/reek/smells/attribute_spec.rb +1 -1
  48. data/spec/reek/smells/class_variable_spec.rb +96 -7
  49. data/spec/reek/smells/control_couple_spec.rb +1 -1
  50. data/spec/reek/smells/data_clump_spec.rb +31 -13
  51. data/spec/reek/smells/feature_envy_spec.rb +1 -1
  52. data/spec/reek/smells/large_class_spec.rb +32 -18
  53. data/spec/reek/smells/simulated_polymorphism_spec.rb +66 -18
  54. data/spec/reek/sniffer_spec.rb +1 -0
  55. data/spec/samples/not_quite_masked/dirty.rb +2 -0
  56. data/spec/spec_helper.rb +1 -1
  57. data/tasks/reek.rake +1 -1
  58. metadata +5 -3
  59. data/lib/reek/exceptions.reek +0 -20
@@ -17,7 +17,7 @@ describe Attribute do
17
17
  [ClassContext, ModuleContext].each do |klass|
18
18
  context "in a #{klass}" do
19
19
  before :each do
20
- @ctx = klass.create(StopContext.new, "Fred")
20
+ @ctx = klass.create(StopContext.new, s(:null, :Fred))
21
21
  end
22
22
 
23
23
  it_should_behave_like 'a variable detector'
@@ -3,24 +3,113 @@ require File.dirname(__FILE__) + '/../../spec_helper.rb'
3
3
  require 'reek/smells/class_variable'
4
4
  require 'reek/class_context'
5
5
 
6
- require 'spec/reek/smells/behaves_like_variable_detector'
7
-
8
6
  include Reek
9
7
  include Reek::Smells
10
8
 
11
9
  describe ClassVariable do
12
10
  before :each do
13
11
  @detector = ClassVariable.new
14
- @record_variable = :record_class_variable
15
12
  end
16
13
 
17
- [ClassContext, ModuleContext].each do |klass|
18
- context "in a #{klass}" do
14
+ context 'with no class variables' do
15
+ it 'records nothing in the class' do
16
+ ctx = ClassContext.from_s('class Fred; end')
17
+ @detector.class_variables_in(ctx).should be_empty
18
+ end
19
+ it 'records nothing in the module' do
20
+ ctx = ModuleContext.from_s('module Fred; end')
21
+ @detector.class_variables_in(ctx).should be_empty
22
+ end
23
+ end
24
+
25
+ context 'with one class variable' do
26
+ shared_examples_for 'one variable found' do
27
+ it 'records the class variable' do
28
+ @detector.class_variables_in(@ctx).should include(:@@tools)
29
+ end
30
+ it 'records only that class variable' do
31
+ @detector.class_variables_in(@ctx).length.should == 1
32
+ end
33
+ end
34
+
35
+ context 'declared in a class' do
36
+ before :each do
37
+ @ctx = ClassContext.from_s('class Fred; @@tools = {}; end')
38
+ end
39
+
40
+ it_should_behave_like 'one variable found'
41
+ end
42
+
43
+ context 'used in a class' do
44
+ before :each do
45
+ @ctx = ClassContext.from_s('class Fred; def jim() @@tools = {}; end; end')
46
+ end
47
+
48
+ it_should_behave_like 'one variable found'
49
+ end
50
+
51
+ context 'indexed in a class' do
52
+ before :each do
53
+ @ctx = ClassContext.from_s('class Fred; def jim() @@tools[mash] = {}; end; end')
54
+ end
55
+
56
+ it_should_behave_like 'one variable found'
57
+ end
58
+
59
+ context 'declared and used in a class' do
60
+ before :each do
61
+ @ctx = ClassContext.from_s('class Fred; @@tools = {}; def jim() @@tools = {}; end; end')
62
+ end
63
+
64
+ it_should_behave_like 'one variable found'
65
+ end
66
+
67
+ context 'used twice in a class' do
68
+ before :each do
69
+ @ctx = ClassContext.from_s('class Fred; def jeff() @@tools = {}; end; def jim() @@tools = {}; end; end')
70
+ end
71
+
72
+ it_should_behave_like 'one variable found'
73
+ end
74
+
75
+ context 'declared in a module' do
76
+ before :each do
77
+ @ctx = ClassContext.from_s('module Fred; @@tools = {}; end')
78
+ end
79
+
80
+ it_should_behave_like 'one variable found'
81
+ end
82
+
83
+ context 'used in a module' do
84
+ before :each do
85
+ @ctx = ClassContext.from_s('module Fred; def jim() @@tools = {}; end; end')
86
+ end
87
+
88
+ it_should_behave_like 'one variable found'
89
+ end
90
+
91
+ context 'indexed in a module' do
92
+ before :each do
93
+ @ctx = ClassContext.from_s('module Fred; def jim() @@tools[mash] = {}; end; end')
94
+ end
95
+
96
+ it_should_behave_like 'one variable found'
97
+ end
98
+
99
+ context 'declared and used in a module' do
100
+ before :each do
101
+ @ctx = ClassContext.from_s('module Fred; @@tools = {}; def jim() @@tools = {}; end; end')
102
+ end
103
+
104
+ it_should_behave_like 'one variable found'
105
+ end
106
+
107
+ context 'used twice in a module' do
19
108
  before :each do
20
- @ctx = klass.create(StopContext.new, "Fred")
109
+ @ctx = ClassContext.from_s('module Fred; def jeff() @@tools = {}; end; def jim() @@tools = {}; end; end')
21
110
  end
22
111
 
23
- it_should_behave_like 'a variable detector'
112
+ it_should_behave_like 'one variable found'
24
113
  end
25
114
  end
26
115
  end
@@ -6,7 +6,7 @@ include Reek::Smells
6
6
 
7
7
  describe ControlCouple do
8
8
  it 'should report a ternary check on a parameter' do
9
- 'def simple(arga) arga ? @ivar : 3 end'.should reek_of(:ControlCouple, /arga/)
9
+ 'def simple(arga) arga ? @ivar : 3 end'.should reek_only_of(:ControlCouple, /arga/)
10
10
  end
11
11
 
12
12
  it 'should not report a ternary check on an ivar' do
@@ -4,10 +4,10 @@ require 'reek/smells/data_clump'
4
4
 
5
5
  include Reek::Smells
6
6
 
7
- describe DataClump do
8
- it 'should not report small parameter sets' do
7
+ shared_examples_for 'a data clump detector' do
8
+ it 'does not report small parameter sets' do
9
9
  src = <<EOS
10
- class Scrunch
10
+ #{@context} Scrunch
11
11
  def first(pa) @field == :sym ? 0 : 3; end
12
12
  def second(pa) @field == :sym; end
13
13
  def third(pa) pa - pb + @fred; end
@@ -17,9 +17,9 @@ EOS
17
17
  src.should_not reek
18
18
  end
19
19
 
20
- it 'should report 3 identical pairs in a class' do
20
+ it 'reports 3 identical pairs in a class' do
21
21
  src = <<EOS
22
- class Scrunch
22
+ #{@context} Scrunch
23
23
  def first(pa, pb) @field == :sym ? 0 : 3; end
24
24
  def second(pa, pb) @field == :sym; end
25
25
  def third(pa, pb) pa - pb + @fred; end
@@ -29,9 +29,9 @@ EOS
29
29
  src.should reek_of(:DataClump, /\[pa, pb\]/, /3 methods/)
30
30
  end
31
31
 
32
- it 'should report 3 swapped pairs in a class' do
32
+ it 'reports 3 swapped pairs in a class' do
33
33
  src = <<EOS
34
- class Scrunch
34
+ #{@context} Scrunch
35
35
  def one(pa, pb) @field == :sym ? 0 : 3; end
36
36
  def two(pb, pa) @field == :sym; end
37
37
  def tri(pa, pb) pa - pb + @fred; end
@@ -41,9 +41,9 @@ EOS
41
41
  src.should reek_of(:DataClump, /\[pa, pb\]/, /3 methods/)
42
42
  end
43
43
 
44
- it 'should report 3 identical parameter sets in a class' do
44
+ it 'reports 3 identical parameter sets in a class' do
45
45
  src = <<EOS
46
- class Scrunch
46
+ #{@context} Scrunch
47
47
  def first(pa, pb, pc) @field == :sym ? 0 : 3; end
48
48
  def second(pa, pb, pc) @field == :sym; end
49
49
  def third(pa, pb, pc) pa - pb + @fred; end
@@ -56,9 +56,9 @@ EOS
56
56
  src.should_not reek_of(:DataClump, /\[pb, pc\]/, /3 methods/)
57
57
  end
58
58
 
59
- it 'should recognise re-ordered identical parameter sets' do
59
+ it 'recognises re-ordered identical parameter sets' do
60
60
  src = <<EOS
61
- class Scrunch
61
+ #{@context} Scrunch
62
62
  def first(pb, pa, pc) @field == :sym ? 0 : 3; end
63
63
  def second(pc, pb, pa) @field == :sym; end
64
64
  def third(pa, pb, pc) pa - pb + @fred; end
@@ -71,9 +71,9 @@ EOS
71
71
  src.should_not reek_of(:DataClump, /\[pb, pc\]/, /3 methods/)
72
72
  end
73
73
 
74
- it 'should count only identical parameter sets' do
74
+ it 'counts only identical parameter sets' do
75
75
  src = <<EOS
76
- class RedCloth
76
+ #{@context} RedCloth
77
77
  def fa(p1, p2, p3, conten) end
78
78
  def fb(p1, p2, p3, conten) end
79
79
  def fc(name, windowW, windowH) end
@@ -82,6 +82,24 @@ EOS
82
82
 
83
83
  src.should_not reek_of(:DataClump)
84
84
  end
85
+ end
86
+
87
+ describe DataClump do
88
+ context 'in a class' do
89
+ before :each do
90
+ @context = 'class'
91
+ end
92
+
93
+ it_should_behave_like 'a data clump detector'
94
+ end
95
+
96
+ context 'in a module' do
97
+ before :each do
98
+ @context = 'module'
99
+ end
100
+
101
+ it_should_behave_like 'a data clump detector'
102
+ end
85
103
 
86
104
  # TODO: include singleton methods in the calcs
87
105
  end
@@ -204,7 +204,7 @@ end
204
204
  describe FeatureEnvy, '#examine' do
205
205
 
206
206
  before :each do
207
- @context = MethodContext.new(StopContext.new, [:defn, :cool])
207
+ @context = MethodContext.new(StopContext.new, s(:defn, :cool))
208
208
  @fe = FeatureEnvy.new
209
209
  end
210
210
 
@@ -9,21 +9,30 @@ include Reek
9
9
  include Reek::Smells
10
10
 
11
11
  describe LargeClass, 'when exceptions are listed' do
12
-
13
12
  before(:each) do
14
- @ctx = ClassContext.create(StopContext.new, [0, :Humungous])
15
- 30.times { |num| @ctx.record_method("method#{num}") }
13
+ @class_name = 'Humungous'
14
+ src = <<EOS
15
+ class #{@class_name}
16
+ def me01x()3 end;def me02x()3 end;def me03x()3 end;def me04x()3 end;def me05x()3 end
17
+ def me11x()3 end;def me12x()3 end;def me13x()3 end;def me14x()3 end;def me15x()3 end
18
+ def me21x()3 end;def me22x()3 end;def me23x()3 end;def me24x()3 end;def me25x()3 end
19
+ def me31x()3 end;def me32x()3 end;def me33x()3 end;def me34x()3 end;def me35x()3 end
20
+ def me41x()3 end;def me42x()3 end;def me43x()3 end;def me44x()3 end;def me45x()3 end
21
+ def me51x()3 end
22
+ end
23
+ EOS
24
+ @ctx = ClassContext.from_s(src)
16
25
  @config = LargeClass.default_config
17
26
  end
18
27
 
19
28
  it 'should ignore first excepted name' do
20
- @config[LargeClass::EXCLUDE_KEY] = ['Humungous']
29
+ @config[LargeClass::EXCLUDE_KEY] = [@class_name]
21
30
  lc = LargeClass.new(@config)
22
31
  lc.examine(@ctx).should == false
23
32
  end
24
33
 
25
34
  it 'should ignore second excepted name' do
26
- @config[LargeClass::EXCLUDE_KEY] = ['Oversized', 'Humungous']
35
+ @config[LargeClass::EXCLUDE_KEY] = ['Oversized', @class_name]
27
36
  lc = LargeClass.new(@config)
28
37
  lc.examine(@ctx).should == false
29
38
  end
@@ -37,7 +46,7 @@ end
37
46
 
38
47
  describe LargeClass, 'checking source code' do
39
48
 
40
- describe 'counting instance variables' do
49
+ context 'counting instance variables' do
41
50
  it 'should not report empty class' do
42
51
  ClassContext.from_s('class Empty;end').should have(0).variable_names
43
52
  end
@@ -68,18 +77,7 @@ EOS
68
77
  end
69
78
  end
70
79
 
71
- describe 'counting methods' do
72
- it 'should not report empty class' do
73
- ClassContext.from_s('class Empty;end').num_methods.should == 0
74
- end
75
-
76
- it 'should count 1 method' do
77
- ClassContext.from_s('class Empty;def ivars() @aa=@ab; end;end').num_methods.should == 1
78
- end
79
-
80
- it 'should count 2 methods' do
81
- ClassContext.from_s('class Empty;def meth1() @aa=@ab;end;def meth2() @aa=@ab;end;end').num_methods.should == 2
82
- end
80
+ context 'counting methods' do
83
81
 
84
82
  it 'should not report 25 methods' do
85
83
  src = <<EOS
@@ -108,4 +106,20 @@ EOS
108
106
  src.should reek_of(:LargeClass)
109
107
  end
110
108
  end
109
+
110
+ context 'with a nested module' do
111
+ it 'stops at a nested module' do
112
+ src = <<EOS
113
+ class Full
114
+ def me01x()3 end;def me02x()3 end;def me03x()3 end;def me04x()3 end;def me05x()3 end
115
+ def me11x()3 end;def me12x()3 end;def me13x()3 end;def me14x()3 end;def me15x()3 end
116
+ def me21x()3 end;def me22x()3 end;def me23x()3 end;def me24x()3 end;def me25x()3 end
117
+ def me31x()3 end;def me32x()3 end;def me33x()3 end;def me34x()3 end;def me35x()3 end
118
+ module Hidden; def me41x()3 end;def me42x()3 end;def me43x()3 end;def me44x()3 end;def me45x()3 end; end
119
+ def me51x()3 end
120
+ end
121
+ EOS
122
+ src.should_not reek_of(:LargeClass)
123
+ end
124
+ end
111
125
  end
@@ -1,49 +1,97 @@
1
1
  require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
2
 
3
3
  require 'reek/smells/simulated_polymorphism'
4
+ require 'reek/code_context'
4
5
 
6
+ include Reek
5
7
  include Reek::Smells
6
8
 
7
9
  describe SimulatedPolymorphism do
8
- it 'should report 3 similar conditions in a class' do
9
- src = <<EOS
10
+ before :each do
11
+ @detector = SimulatedPolymorphism.new
12
+ end
13
+ context 'with no conditionals' do
14
+ it 'gathers an empty hash' do
15
+ ast = 'module Stable; end'.to_reek_source.syntax_tree
16
+ ctx = CodeContext.new(nil, ast)
17
+ @detector.conditional_counts(ctx).length.should == 0
18
+ end
19
+ end
20
+
21
+ context 'with a test of block_given?' do
22
+ it 'does not record the condition' do
23
+ ast = 'def fred() yield(3) if block_given?; end'.to_reek_source.syntax_tree
24
+ ctx = CodeContext.new(nil, ast)
25
+ @detector.conditional_counts(ctx).length.should == 0
26
+ end
27
+ end
28
+
29
+ context 'with three identical conditionals' do
30
+ before :each do
31
+ cond = '@field == :sym'
32
+ @cond_expr = cond.to_reek_source.syntax_tree
33
+ src = <<EOS
10
34
  class Scrunch
11
35
  def first
12
- return @field == :sym ? 0 : 3;
36
+ return #{cond} ? 0 : 3;
13
37
  end
14
38
  def second
15
- if @field == :sym
39
+ if #{cond}
16
40
  @other += " quarts"
17
41
  end
18
42
  end
19
43
  def third
20
- raise 'flu!' unless @field == :sym
44
+ raise 'flu!' unless #{cond}
21
45
  end
22
46
  end
23
47
  EOS
24
48
 
25
- src.should reek_only_of(:SimulatedPolymorphism, /@field == :sym/)
49
+ ast = src.to_reek_source.syntax_tree
50
+ ctx = CodeContext.new(nil, ast)
51
+ @conds = @detector.conditional_counts(ctx)
52
+ end
53
+ it 'finds one matching conditional' do
54
+ @conds.length.should == 1
55
+ end
56
+ it 'returns the condition expr' do
57
+ @conds.keys[0].should == @cond_expr
58
+ end
59
+ it 'knows there are three copies' do
60
+ @conds[@cond_expr].should == 3
61
+ end
26
62
  end
27
63
 
28
- it 'should include case statements in the count' do
29
- src = <<EOS
64
+ context 'with a matching if and case' do
65
+ before :each do
66
+ cond = '@field == :sym'
67
+ @cond_expr = cond.to_reek_source.syntax_tree
68
+ src = <<EOS
30
69
  class Scrunch
31
- def first
32
- return @field ? 0 : 3;
70
+ def alpha
71
+ return #{cond} ? 0 : 2;
33
72
  end
34
- def second
35
- case @field
36
- when :sym
37
- @other += " quarts"
73
+ def beta
74
+ case #{cond}
75
+ when :symbol
76
+ @tother += " pints"
38
77
  end
39
78
  end
40
- def third
41
- raise 'flu!' unless @field
42
- end
43
79
  end
44
80
  EOS
45
81
 
46
- src.should reek_only_of(:SimulatedPolymorphism, /@field/)
82
+ ast = src.to_reek_source.syntax_tree
83
+ ctx = CodeContext.new(nil, ast)
84
+ @conds = @detector.conditional_counts(ctx)
85
+ end
86
+ it 'finds exactly one conditional' do
87
+ @conds.length.should == 1
88
+ end
89
+ it 'returns the condition expr' do
90
+ @conds.keys[0].should == @cond_expr
91
+ end
92
+ it 'knows there are three copies' do
93
+ @conds[@cond_expr].should == 2
94
+ end
47
95
  end
48
96
 
49
97
  # And count code in superclasses, if we have it