reek 0.3.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. data/History.txt +20 -0
  2. data/README.txt +4 -80
  3. data/Rakefile +15 -4
  4. data/bin/reek +10 -16
  5. data/config/defaults.reek +53 -0
  6. data/lib/reek.rb +1 -21
  7. data/lib/reek/block_context.rb +37 -0
  8. data/lib/reek/class_context.rb +73 -0
  9. data/lib/reek/code_context.rb +47 -0
  10. data/lib/reek/code_parser.rb +204 -0
  11. data/lib/reek/exceptions.reek +13 -0
  12. data/lib/reek/if_context.rb +25 -0
  13. data/lib/reek/method_context.rb +85 -0
  14. data/lib/reek/module_context.rb +34 -0
  15. data/lib/reek/name.rb +42 -0
  16. data/lib/reek/object_refs.rb +3 -6
  17. data/lib/reek/options.rb +60 -40
  18. data/lib/reek/rake_task.rb +20 -29
  19. data/lib/reek/report.rb +16 -27
  20. data/lib/reek/sexp_formatter.rb +52 -0
  21. data/lib/reek/singleton_method_context.rb +27 -0
  22. data/lib/reek/smell_warning.rb +49 -0
  23. data/lib/reek/smells/control_couple.rb +21 -13
  24. data/lib/reek/smells/duplication.rb +23 -27
  25. data/lib/reek/smells/feature_envy.rb +18 -25
  26. data/lib/reek/smells/large_class.rb +32 -17
  27. data/lib/reek/smells/long_method.rb +24 -16
  28. data/lib/reek/smells/long_parameter_list.rb +25 -18
  29. data/lib/reek/smells/long_yield_list.rb +7 -9
  30. data/lib/reek/smells/nested_iterators.rb +13 -9
  31. data/lib/reek/smells/smell_detector.rb +66 -0
  32. data/lib/reek/smells/smells.rb +71 -10
  33. data/lib/reek/smells/uncommunicative_name.rb +49 -41
  34. data/lib/reek/smells/utility_function.rb +18 -18
  35. data/lib/reek/source.rb +116 -0
  36. data/lib/reek/spec.rb +146 -0
  37. data/lib/reek/stop_context.rb +62 -0
  38. data/lib/reek/yield_call_context.rb +14 -0
  39. data/reek.gemspec +42 -0
  40. data/spec/integration/reek_source_spec.rb +20 -0
  41. data/spec/{script_spec.rb → integration/script_spec.rb} +11 -24
  42. data/spec/reek/class_context_spec.rb +198 -0
  43. data/spec/reek/code_context_spec.rb +92 -0
  44. data/spec/reek/code_parser_spec.rb +44 -0
  45. data/spec/reek/config_spec.rb +42 -0
  46. data/spec/reek/if_context_spec.rb +17 -0
  47. data/spec/reek/method_context_spec.rb +52 -0
  48. data/spec/reek/module_context_spec.rb +38 -0
  49. data/spec/reek/options_spec.rb +2 -28
  50. data/spec/reek/report_spec.rb +6 -40
  51. data/spec/reek/sexp_formatter_spec.rb +31 -0
  52. data/spec/reek/singleton_method_context_spec.rb +17 -0
  53. data/spec/reek/smells/control_couple_spec.rb +10 -18
  54. data/spec/reek/smells/duplication_spec.rb +53 -32
  55. data/spec/reek/smells/feature_envy_spec.rb +87 -49
  56. data/spec/reek/smells/large_class_spec.rb +45 -4
  57. data/spec/reek/smells/long_method_spec.rb +25 -41
  58. data/spec/reek/smells/long_parameter_list_spec.rb +30 -76
  59. data/spec/reek/smells/nested_iterators_spec.rb +19 -29
  60. data/spec/reek/smells/smell_spec.rb +9 -18
  61. data/spec/reek/smells/uncommunicative_name_spec.rb +88 -53
  62. data/spec/reek/smells/utility_function_spec.rb +45 -44
  63. data/spec/samples/inline_spec.rb +40 -0
  64. data/spec/samples/optparse_spec.rb +100 -0
  65. data/spec/samples/redcloth_spec.rb +93 -0
  66. data/spec/spec_helper.rb +3 -1
  67. data/tasks/reek.rake +1 -10
  68. data/tasks/rspec.rake +16 -35
  69. metadata +43 -46
  70. data/lib/reek/checker.rb +0 -66
  71. data/lib/reek/class_checker.rb +0 -25
  72. data/lib/reek/file_checker.rb +0 -20
  73. data/lib/reek/method_checker.rb +0 -198
  74. data/lib/reek/printer.rb +0 -154
  75. data/lib/reek/smells/smell.rb +0 -56
  76. data/lib/reek/version.rb +0 -9
  77. data/setup.rb +0 -1585
  78. data/spec/integration_spec.rb +0 -30
  79. data/spec/reek/class_checker_spec.rb +0 -48
  80. data/spec/reek/method_checker_spec.rb +0 -67
  81. data/spec/reek/printer_spec.rb +0 -30
  82. data/spec/reek_source_spec.rb +0 -12
  83. data/spec/samples/inline.reek +0 -27
  84. data/spec/samples/optparse.reek +0 -79
  85. data/spec/samples/optparse/date.rb +0 -17
  86. data/spec/samples/optparse/shellwords.rb +0 -6
  87. data/spec/samples/optparse/time.rb +0 -10
  88. data/spec/samples/optparse/uri.rb +0 -6
  89. data/spec/samples/optparse/version.rb +0 -70
  90. data/spec/samples/redcloth.reek +0 -65
  91. data/tasks/samples.rake +0 -17
  92. data/website/index.html +0 -71
  93. data/website/index.txt +0 -40
  94. data/website/javascripts/rounded_corners_lite.inc.js +0 -285
  95. data/website/stylesheets/screen.css +0 -138
  96. data/website/template.rhtml +0 -48
@@ -1,13 +1,11 @@
1
- require File.dirname(__FILE__) + '/spec_helper.rb'
2
-
3
- require 'reek/version'
1
+ require 'reek'
4
2
 
5
3
  describe 'version number' do
6
4
  it 'should report the correct value' do
7
5
  actual = `ruby -Ilib bin/reek --version`.split
8
6
  $?.exitstatus.should == 0
9
7
  actual[0].should == 'reek'
10
- actual[1].should == Reek::VERSION::STRING
8
+ actual[1].should == Reek::VERSION
11
9
  end
12
10
  end
13
11
 
@@ -17,51 +15,40 @@ describe 'exit status', 'when reek is used incorrectly' do
17
15
  $?.exitstatus.should == 1
18
16
  end
19
17
 
20
- it 'should not complain about missing file' do
21
- `ruby -Ilib bin/reek nosuchfile.rb 2>/dev/null`
22
- $?.exitstatus.should == 0
18
+ it 'should complain about missing file' do
19
+ `ruby -Ilib bin/reek nosuchfile.rb`
20
+ $?.exitstatus.should == 1
23
21
  end
24
22
 
25
23
  it 'should return non-zero status on missing argument' do
26
- `ruby -Ilib bin/reek -s 2>/dev/null`
24
+ `ruby -Ilib bin/reek -f 2>/dev/null`
27
25
  $?.exitstatus.should == 1
28
26
  end
29
-
30
- it 'should not complain when no source given' do
31
- `ruby -Ilib bin/reek 2>/dev/null`
32
- $?.exitstatus.should == 0
33
- end
34
27
  end
35
28
 
36
29
  describe 'exit status', 'when reek is used correctly' do
37
30
  it 'should return non-zero status when smells are reported' do
38
- `ruby -Ilib bin/reek "def x() 3; end"`
31
+ `echo "def x() 3; end" | ruby -Ilib bin/reek`
39
32
  $?.exitstatus.should == 2
40
33
  end
41
34
 
42
35
  it 'should return zero status with no smells' do
43
- `ruby -Ilib bin/reek "def simple() @fred = 3 end"`
36
+ `echo "def simple() @fred = 3 end" | ruby -Ilib bin/reek`
44
37
  $?.exitstatus.should == 0
45
38
  end
46
39
  end
47
40
 
48
- describe 'report format', 'with no sources' do
49
- it 'should output nothing' do
50
- `ruby -Ilib bin/reek`.should be_empty
51
- end
52
- end
53
-
54
41
  describe 'report format', 'with one source' do
55
42
  it 'should output nothing with empty source' do
56
- `ruby -Ilib bin/reek ""`.should be_empty
43
+ `echo "" | ruby -Ilib bin/reek`.should be_empty
57
44
  end
58
45
 
59
46
  it 'should output nothing when no smells' do
60
- `ruby -Ilib bin/reek "def simple() @fred = 3; end"`.should be_empty
47
+ `echo "def simple() @fred = 3; end" | ruby -Ilib bin/reek`.should be_empty
61
48
  end
62
49
 
63
50
  it 'should not adorn the list of warnings' do
64
- report = `ruby -Ilib bin/reek "def y() @x = 3; end"`
51
+ report = `echo "class Turn; def y() @x = 3; end end" | ruby -Ilib bin/reek`
65
52
  report.split(/\n/).length.should == 2
66
53
  report.should_not match(/\n\n/)
67
54
  end
@@ -0,0 +1,198 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ require 'reek/class_context'
4
+ require 'reek/stop_context'
5
+ require 'reek/smells/feature_envy'
6
+
7
+ include Reek
8
+ include Reek::Smells
9
+
10
+ describe ClassContext do
11
+ it 'should report Long Parameter List' do
12
+ ruby = 'class Inner; def simple(arga, argb, argc, argd) f(3);true end end'
13
+ ruby.should reek_of(:LongParameterList, /Inner/, /simple/, /4 parameters/)
14
+ end
15
+
16
+ it 'should report two different methods' do
17
+ src = <<EOEX
18
+ class Fred
19
+ def simple(arga, argb, argc, argd) f(3);true end
20
+ def simply(arga, argb, argc, argd) f(3);false end
21
+ end
22
+ EOEX
23
+ src.should reek_of(:LongParameterList, /Fred/, /simple/)
24
+ src.should reek_of(:LongParameterList, /Fred/, /simply/)
25
+ end
26
+
27
+ it 'should report many different methods' do
28
+ src = <<EOEX
29
+ class Fred
30
+ def textile_bq(tag, atts, cite, content) f(3);end
31
+ def textile_p(tag, atts, cite, content) f(3);end
32
+ def textile_fn_(tag, num, atts, cite, content) f(3);end
33
+ def textile_popup_help(name, windowW, windowH) f(3);end
34
+ end
35
+ EOEX
36
+ src.should reek_of(:LongParameterList, /Fred/, /textile_bq/)
37
+ src.should reek_of(:LongParameterList, /Fred/, /textile_fn_/)
38
+ src.should reek_of(:LongParameterList, /Fred/, /textile_p/)
39
+ end
40
+ end
41
+
42
+ describe Class do
43
+
44
+ module Insert
45
+ def meth_a() end
46
+ private
47
+ def meth_b() end
48
+ protected
49
+ def meth_c() end
50
+ end
51
+
52
+ class Parent
53
+ def meth1() end
54
+ private
55
+ def meth2() end
56
+ protected
57
+ def meth3() end
58
+ end
59
+
60
+ class FullChild < Parent
61
+ include Insert
62
+ def meth7() end
63
+ private
64
+ def meth8() end
65
+ protected
66
+ def meth6() end
67
+ end
68
+
69
+ describe 'with no superclass or modules' do
70
+ it 'should report correct number of methods' do
71
+ Parent.non_inherited_methods.length.should == 3
72
+ end
73
+ end
74
+
75
+ describe 'with superclass and modules' do
76
+ it 'should report correct number of methods' do
77
+ FullChild.non_inherited_methods.length.should == 3
78
+ end
79
+ end
80
+ end
81
+
82
+ describe ClassContext, 'overridden methods' do
83
+ class Above
84
+ def above() end
85
+ def both() end
86
+ end
87
+
88
+ class Below < Above
89
+ def both() end
90
+ def below() end
91
+ end
92
+
93
+ describe 'of loaded class' do
94
+ before :each do
95
+ @ctx = ClassContext.create(StopContext.new, [0, :Below])
96
+ end
97
+
98
+ it 'should recognise non-overridden method' do
99
+ @ctx.is_overriding_method?('below').should == false
100
+ @ctx.is_overriding_method?('above').should == false
101
+ end
102
+
103
+ it 'should recognise overridden method' do
104
+ @ctx.is_overriding_method?('both').should == true
105
+ end
106
+
107
+ it 'should recognise methods in current codebase' do
108
+ ctx = ClassContext.create(StopContext.new, [0, :FeatureEnvy])
109
+ ctx.is_overriding_method?('examine_context').should == true
110
+ end
111
+ end
112
+
113
+ describe 'of non-loaded class' do
114
+ before :each do
115
+ @ctx = ClassContext.create(StopContext.new, [0, :Missing])
116
+ end
117
+
118
+ it 'should recognise non-overridden method' do
119
+ @ctx.is_overriding_method?('below').should == false
120
+ @ctx.is_overriding_method?('above').should == false
121
+ end
122
+
123
+ it 'should recognise overridden method' do
124
+ @ctx.is_overriding_method?('both').should == false
125
+ end
126
+ end
127
+ end
128
+
129
+ describe 'Integration defect:' do
130
+ it 'should not report UtilityFunction for FeatureEnvy#examine_context' do
131
+ kelement = ClassContext.create(StopContext.new, [0, :FeatureEnvy, s(:const, :SmellDetector)])
132
+ meth = Name.new(:examine_context)
133
+ kelement.is_overriding_method?(meth).should == true
134
+ melement = MethodContext.new(kelement, [0, :examine_context])
135
+ melement.is_overriding_method?(meth).should == true
136
+ melement.depends_on_instance?.should == true
137
+ end
138
+ end
139
+
140
+ describe CodeContext, 'find class' do
141
+ module Mod1
142
+ class Klass1
143
+ module Mod2
144
+ class Klass2
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ before :each do
151
+ @stop = StopContext.new
152
+ @mod1 = ModuleContext.create(@stop, [0, :Mod1])
153
+ @klass1 = ClassContext.create(@mod1, [0, :Klass1])
154
+ @mod2 = ModuleContext.create(@klass1, [0, :Mod2])
155
+ @klass2 = ClassContext.create(@mod2, [0, :Klass2])
156
+ end
157
+
158
+ describe StopContext do
159
+ it 'should not find unqualified class' do
160
+ @stop.find_module('Klass2').should == nil
161
+ end
162
+
163
+ it 'should find unqualified module' do
164
+ @stop.find_module('Mod1').name.should == 'Mod1'
165
+ end
166
+ end
167
+
168
+ describe ModuleContext do
169
+ it 'should find local name' do
170
+ @mod1.find_module('Klass1').name.should == 'Mod1::Klass1'
171
+ @mod2.find_module('Klass2').name.should == 'Mod1::Klass1::Mod2::Klass2'
172
+ end
173
+
174
+ it 'should not find deeper class' do
175
+ @mod1.find_module('Klass2').should == nil
176
+ end
177
+
178
+ it 'should find own Module' do
179
+ @mod1.myself.name.should == 'Mod1'
180
+ @mod2.myself.name.should == 'Mod1::Klass1::Mod2'
181
+ end
182
+ end
183
+
184
+ describe ClassContext do
185
+ it 'should find local module' do
186
+ @klass1.find_module('Mod2').name.should == 'Mod1::Klass1::Mod2'
187
+ end
188
+
189
+ it 'should not find deeper module' do
190
+ @klass1.find_module('Klass2').should == nil
191
+ end
192
+
193
+ it 'should find own Class' do
194
+ @klass1.myself.name.should == 'Mod1::Klass1'
195
+ @klass2.myself.name.should == 'Mod1::Klass1::Mod2::Klass2'
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,92 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ require 'reek/block_context'
4
+ require 'reek/if_context'
5
+ require 'reek/class_context'
6
+ require 'reek/module_context'
7
+ require 'reek/method_context'
8
+ require 'reek/stop_context'
9
+
10
+ include Reek
11
+
12
+ describe CodeContext, 'to_s' do
13
+
14
+ it "should report full context" do
15
+ element = StopContext.new
16
+ element = ModuleContext.new(element, [0, :mod])
17
+ element = ClassContext.new(element, [0, :klass])
18
+ element = MethodContext.new(element, [0, :bad])
19
+ element = BlockContext.new(element, nil)
20
+ element.to_s.should match(/bad/)
21
+ element.to_s.should match(/klass/)
22
+ element.to_s.should match(/mod/)
23
+ end
24
+
25
+ it "should report method name via if context" do
26
+ element1 = StopContext.new
27
+ element2 = MethodContext.new(element1, [0, :bad])
28
+ element3 = IfContext.new(element2, [0,1])
29
+ BlockContext.new(element3, nil).to_s.should match(/bad/)
30
+ end
31
+
32
+ it "should report method name via nested blocks" do
33
+ element1 = StopContext.new
34
+ element2 = MethodContext.new(element1, [0, :bad])
35
+ element3 = BlockContext.new(element2, nil)
36
+ BlockContext.new(element3, nil).to_s.should match(/bad/)
37
+ end
38
+ end
39
+
40
+ describe CodeContext, 'instance variables' do
41
+ it 'should pass instance variables down to the first class' do
42
+ element = StopContext.new
43
+ element = ModuleContext.new(element, [0, :mod])
44
+ class_element = ClassContext.new(element, [0, :klass])
45
+ element = MethodContext.new(class_element, [0, :bad])
46
+ element = BlockContext.new(element, nil)
47
+ element.record_instance_variable(:fred)
48
+ class_element.variable_names.should == [Name.new(:fred)]
49
+ end
50
+ end
51
+
52
+ describe CodeContext, 'generics' do
53
+ it 'should pass unknown method calls down the stack' do
54
+ stop = StopContext.new
55
+ def stop.bananas(arg1, arg2) arg1 + arg2 + 43 end
56
+ element = ModuleContext.new(stop, [0, :mod])
57
+ class_element = ClassContext.new(element, [0, :klass])
58
+ element = MethodContext.new(class_element, [0, :bad])
59
+ element = BlockContext.new(element, nil)
60
+ element.bananas(17, -5).should == 55
61
+ end
62
+ end
63
+
64
+ describe CodeContext do
65
+ it 'should recognise itself in a collection of names' do
66
+ element = StopContext.new
67
+ element = ModuleContext.new(element, [0, :mod])
68
+ element.matches?(['banana', 'mod']).should == true
69
+ end
70
+
71
+ it 'should recognise itself in a collection of REs' do
72
+ element = StopContext.new
73
+ element = ModuleContext.new(element, [0, :mod])
74
+ element.matches?([/banana/, /mod/]).should == true
75
+ end
76
+
77
+ it 'should recognise its fq name in a collection of names' do
78
+ element = StopContext.new
79
+ element = ModuleContext.new(element, [0, :mod])
80
+ element = ClassContext.create(element, [0, :klass])
81
+ element.matches?(['banana', 'mod']).should == true
82
+ element.matches?(['banana', 'mod::klass']).should == true
83
+ end
84
+
85
+ it 'should recognise its fq name in a collection of names' do
86
+ element = StopContext.new
87
+ element = ModuleContext.new(element, [0, :mod])
88
+ element = ClassContext.create(element, [0, :klass])
89
+ element.matches?([/banana/, /mod/]).should == true
90
+ element.matches?([/banana/, /mod::klass/]).should == true
91
+ end
92
+ end
@@ -0,0 +1,44 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ require 'reek/code_parser'
4
+ require 'reek/report'
5
+
6
+ include Reek
7
+
8
+ describe CodeParser, "with no method definitions" do
9
+ it 'should report no problems for empty source code' do
10
+ ''.should_not reek
11
+ end
12
+ it 'should report no problems for empty class' do
13
+ 'class Fred; end'.should_not reek
14
+ end
15
+ end
16
+
17
+ describe CodeParser, 'with a global method definition' do
18
+ it 'should report no problems for simple method' do
19
+ 'def Outermost::fred() true; end'.should_not reek
20
+ end
21
+ end
22
+
23
+ describe CodeParser, 'when given a C extension' do
24
+ before(:each) do
25
+ @cchk = CodeParser.new(Report.new, SmellConfig.new.smell_listeners)
26
+ end
27
+
28
+ it 'should ignore :cfunc' do
29
+ @cchk.check_object(Enumerable)
30
+ end
31
+ end
32
+
33
+ describe CodeParser, 'when a yield is the receiver' do
34
+ it 'should report no problems' do
35
+ source = 'def values(*args)
36
+ @to_sql += case
37
+ when block_given? then " #{yield.to_sql}"
38
+ else " values (#{args.to_sql})"
39
+ end
40
+ self
41
+ end'
42
+ source.should_not reek
43
+ end
44
+ end
@@ -0,0 +1,42 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ require 'reek/smells/smells'
4
+ require 'yaml'
5
+
6
+ include Reek
7
+
8
+ describe 'Config' do
9
+ before :each do
10
+ @first = {
11
+ 'one' => {'two' => 3, 'three' => 4},
12
+ 'two' => {'four' => true}
13
+ }
14
+ end
15
+
16
+ it 'should deep merge Hashes' do
17
+ other = Hash.new {|hash,key| hash[key] = {} }
18
+ other['one']['gunk'] = 45
19
+ other['two']['four'] = false
20
+ @first.value_merge!(other).to_yaml
21
+ @first['two']['four'].should == false
22
+ @first['one'].keys.length.should == 3
23
+ end
24
+
25
+ it 'should deep copy Hashes' do
26
+ second = @first.deep_copy
27
+ second['two'].object_id.should_not be_eql(@first['two'].object_id)
28
+ end
29
+
30
+ it 'should merge array values' do
31
+ @first['three'] = [1,2,3]
32
+ end
33
+ end
34
+
35
+ describe Config, 'merging arrays' do
36
+ it 'should merge array values' do
37
+ first = {'key' => {'one' => [1,2,3]}}
38
+ second = {'key' => {'one' => [4,5]}}
39
+ first.value_merge!(second)
40
+ first['key']['one'].should == [1,2,3,4,5]
41
+ end
42
+ end