reek 0.3.1 → 1.0.0

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 (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
@@ -0,0 +1,17 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ require 'reek/if_context'
4
+
5
+ include Reek
6
+
7
+ describe IfContext do
8
+ it 'should find a class within top-level code' do
9
+ 'unless jim; class Array; end; end'.should_not reek
10
+ end
11
+
12
+ it 'should find class within top-level code' do
13
+ stopctx = StopContext.new
14
+ ifctx = IfContext.new(stopctx, [:if, [:vcall, :jim]])
15
+ ifctx.find_module(Name.new(:Array)).should_not be_nil
16
+ end
17
+ end
@@ -0,0 +1,52 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ require 'reek/method_context'
4
+ require 'reek/stop_context'
5
+
6
+ include Reek
7
+
8
+ describe MethodContext, 'matching' do
9
+ before :each do
10
+ @element = MethodContext.new(StopContext.new, [0, :mod])
11
+ end
12
+
13
+ it 'should recognise itself in a collection of names' do
14
+ @element.matches?(['banana', 'mod']).should == true
15
+ @element.matches?(['banana']).should == false
16
+ end
17
+
18
+ it 'should recognise itself in a collection of REs' do
19
+ @element.matches?([/banana/, /mod/]).should == true
20
+ @element.matches?([/banana/]).should == false
21
+ end
22
+ end
23
+
24
+ describe MethodContext, 'matching fq names' do
25
+ before :each do
26
+ element = StopContext.new
27
+ element = ModuleContext.new(element, [0, :mod])
28
+ element = ClassContext.new(element, [0, :klass])
29
+ @element = MethodContext.new(element, [0, :meth])
30
+ end
31
+
32
+ it 'should recognise itself in a collection of names' do
33
+ @element.matches?(['banana', 'meth']).should == true
34
+ @element.matches?(['banana', 'klass#meth']).should == true
35
+ @element.matches?(['banana']).should == false
36
+ end
37
+
38
+ it 'should recognise itself in a collection of names' do
39
+ @element.matches?([/banana/, /meth/]).should == true
40
+ @element.matches?([/banana/, /klass#meth/]).should == true
41
+ @element.matches?([/banana/]).should == false
42
+ end
43
+ end
44
+
45
+ describe MethodContext do
46
+ it 'should record ivars as refs to self' do
47
+ mctx = MethodContext.new(StopContext.new, [:defn, :feed])
48
+ mctx.envious_receivers.should == []
49
+ mctx.record_call_to([:call, [:ivar, :@cow], :feed_to])
50
+ mctx.envious_receivers.should == []
51
+ end
52
+ end
@@ -0,0 +1,38 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+ require 'reek/module_context'
3
+ require 'reek/stop_context'
4
+
5
+ include Reek
6
+
7
+ describe ModuleContext do
8
+ it 'should report module name for smell in method' do
9
+ 'module Fred; def simple(x) true; end; end'.should reek_of(:UncommunicativeName, /x/, /simple/, /Fred/)
10
+ end
11
+
12
+ it 'should not report module with empty class' do
13
+ 'module Fred; class Jim; end; end'.should_not reek
14
+ end
15
+
16
+ it 'should handle module with empty class' do
17
+ stop = StopContext.new
18
+ modctx = ModuleContext.create(stop, [:module, :Fred, []])
19
+ modctx.find_module(Name.new(:Jim)).should be_nil
20
+ end
21
+ end
22
+
23
+ describe ModuleContext do
24
+ it 'should recognise global constant' do
25
+ 'module ::Global class Inside; end; end'.should_not reek
26
+ end
27
+
28
+ it 'should not find missing global constant' do
29
+ element = ModuleContext.create(StopContext.new, [:module, [:colon3, :Global], nil])
30
+ element.myself.should be_nil
31
+ end
32
+
33
+ it 'should find global constant' do
34
+ module ::GlobalTestModule; end
35
+ element = ModuleContext.create(StopContext.new, [:module, [:colon3, :GlobalTestModule], nil])
36
+ element.myself.name.should == 'GlobalTestModule'
37
+ end
38
+ end
@@ -6,34 +6,8 @@ include Reek
6
6
 
7
7
  describe Options, ' when given no arguments' do
8
8
  it "should retain the default sort order" do
9
- default_order = Options[:sort_order]
9
+ default_order = Options[:format]
10
10
  Options.parse ['nosuchfile.rb']
11
- Options[:sort_order].should == default_order
12
- end
13
- end
14
-
15
- describe Options, ' when --sort_order is specified' do
16
- before :each do
17
- @default_order = Options[:sort_order]
18
- end
19
-
20
- it 'should require an argument' do
21
- lambda { Options.parse_args(['-s']) }.should raise_error(OptionParser::MissingArgument)
22
- Options[:sort_order].should == @default_order
23
- end
24
-
25
- it "should allow sort by smell" do
26
- Options.parse %w{-s smell xx}
27
- Options[:sort_order].should == Report::SORT_ORDERS[:smell]
28
- end
29
-
30
- it "should allow sort by context" do
31
- Options.parse %w{-s context xx}
32
- Options[:sort_order].should == Report::SORT_ORDERS[:context]
33
- end
34
-
35
- it "should reject illegal sort order" do
36
- lambda { Options.parse_args(%w{-s tarts}) }.should raise_error(OptionParser::InvalidArgument)
37
- Options[:sort_order].should == @default_order
11
+ Options[:format].should == default_order
38
12
  end
39
13
  end
@@ -1,8 +1,9 @@
1
1
  require File.dirname(__FILE__) + '/../spec_helper.rb'
2
2
 
3
- require 'reek/method_checker'
4
- require 'reek/smells'
3
+ require 'reek/smells/smell_detector'
5
4
  require 'reek/report'
5
+ require 'reek/source'
6
+ require 'reek/smells/feature_envy'
6
7
 
7
8
  include Reek
8
9
 
@@ -22,9 +23,7 @@ end
22
23
 
23
24
  describe Report, "to_s" do
24
25
  before(:each) do
25
- rpt = Report.new
26
- chk = MethodChecker.new(rpt, 'Thing')
27
- chk.check_source('def simple(a) a[3] end')
26
+ rpt = Source.from_s('def simple(a) a[3] end').report
28
27
  @report = rpt.to_s.split("\n")
29
28
  end
30
29
 
@@ -41,42 +40,9 @@ end
41
40
  describe Report, " as a SortedSet" do
42
41
  it 'should only add a smell once' do
43
42
  rpt = Report.new
44
- rpt << UtilityFunction.new(self, 1)
43
+ rpt << SmellWarning.new(Smells::FeatureEnvy.new, "self", 'too many!')
45
44
  rpt.length.should == 1
46
- rpt << UtilityFunction.new(self, 1)
45
+ rpt << SmellWarning.new(Smells::FeatureEnvy.new, "self", 'too many!')
47
46
  rpt.length.should == 1
48
47
  end
49
48
  end
50
-
51
- describe SortByContext do
52
- before :each do
53
- @sorter = SortByContext
54
- end
55
-
56
- it 'should return 0 for identical smells' do
57
- smell = LongMethod.new('Class#method', 30)
58
- @sorter.compare(smell, smell).should == 0
59
- end
60
-
61
- it 'should return non-0 for different smells' do
62
- @sorter.compare(LongMethod.new('x', 30), LargeClass.new('y')).should == -1
63
- end
64
- end
65
-
66
- describe SortBySmell do
67
- before :each do
68
- @sorter = SortBySmell
69
- end
70
-
71
- it 'should return 0 for identical smells' do
72
- @sorter.compare(LongMethod.new('x', 30), LongMethod.new('x', 30)).should == 0
73
- end
74
-
75
- it 'should differentiate identical smells with different contexts' do
76
- @sorter.compare(LongMethod.new('x', 29), LongMethod.new('y', 29)).should == -1
77
- end
78
-
79
- it 'should differentiate different smells with identical contexts' do
80
- @sorter.compare(LongMethod.new('x', 28), LargeClass.new('x')).should == 1
81
- end
82
- end
@@ -0,0 +1,31 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ require 'reek/code_parser'
4
+ require 'reek/sexp_formatter'
5
+
6
+ include Reek
7
+
8
+ def should_print(example)
9
+ it "should format #{example} correctly" do
10
+ sexp = CodeParser.parse_tree_for(example)[0]
11
+ SexpFormatter.format(sexp).should == example
12
+ end
13
+ end
14
+
15
+ describe SexpFormatter do
16
+ should_print 'self'
17
+ should_print 'Alpha'
18
+ should_print 'Alpha::Beta'
19
+ should_print '@@fred'
20
+ should_print '`ls`'
21
+ should_print 'array[0]'
22
+ should_print 'array[0, 1, 2]'
23
+ should_print 'obj.method(arg1, arg2)'
24
+ should_print 'obj.method'
25
+ should_print '$1'
26
+ should_print 'o=q.downcase'
27
+ should_print 'true'
28
+ should_print '"-#{q}xxx#{z.size}"'
29
+ should_print '0..5'
30
+ should_print '0..temp'
31
+ end
@@ -0,0 +1,17 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ require 'reek/module_context'
4
+ require 'reek/singleton_method_context'
5
+ require 'reek/stop_context'
6
+
7
+ include Reek
8
+
9
+ describe SingletonMethodContext, 'outer_name' do
10
+
11
+ it "should report full context" do
12
+ element = StopContext.new
13
+ element = ModuleContext.new(element, [0, :mod])
14
+ element = SingletonMethodContext.new(element, [:defs, [:vcall, :a], :b, nil])
15
+ element.outer_name.should match(/mod::a\.b/)
16
+ end
17
+ end
@@ -1,31 +1,23 @@
1
1
  require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
2
 
3
- require 'reek/method_checker'
4
- require 'reek/report'
3
+ require 'reek/smells/control_couple'
5
4
 
6
- include Reek
7
-
8
- describe MethodChecker, "(Control Couple)" do
9
-
10
- before(:each) do
11
- @rpt = Report.new
12
- @cchk = MethodChecker.new(@rpt, 'Thing')
13
- end
5
+ include Reek::Smells
14
6
 
7
+ describe ControlCouple do
15
8
  it 'should report a ternary check on a parameter' do
16
- @cchk.check_source('def simple(arga) arga ? @ivar : 3 end')
17
- @rpt.length.should == 1
18
- ControlCouple.should === @rpt[0]
19
- @rpt[0].to_s.should match(/arga/)
9
+ 'def simple(arga) arga ? @ivar : 3 end'.should reek_of(:ControlCouple, /arga/)
20
10
  end
21
11
 
22
12
  it 'should not report a ternary check on an ivar' do
23
- @cchk.check_source('def simple(arga) @ivar ? arga : 3 end')
24
- @rpt.should be_empty
13
+ 'def simple(arga) @ivar ? arga : 3 end'.should_not reek
25
14
  end
26
15
 
27
16
  it 'should not report a ternary check on a lvar' do
28
- @cchk.check_source('def simple(arga) lvar = 27; lvar ? arga : @ivar end')
29
- @rpt.should be_empty
17
+ 'def simple(arga) lvar = 27; lvar ? arga : @ivar end'.should_not reek
18
+ end
19
+
20
+ it 'should spot a couple inside a block' do
21
+ 'def blocks(arg) @text.map { |blk| arg ? blk : "#{blk}" } end'.should reek_of(:ControlCouple, /arg/)
30
22
  end
31
23
  end
@@ -1,60 +1,81 @@
1
1
  require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
-
3
- require 'reek/method_checker'
4
- require 'reek/smells/duplication'
5
2
  require 'reek/report'
3
+ require 'reek/method_context'
4
+ require 'reek/stop_context'
5
+ require 'reek/smells/duplication'
6
6
 
7
7
  include Reek
8
8
  include Reek::Smells
9
9
 
10
- def check(desc, src, expected, pending_str = nil)
11
- it(desc) do
12
- pending(pending_str) unless pending_str.nil?
13
- rpt = Report.new
14
- cchk = MethodChecker.new(rpt, 'Thing')
15
- cchk.check_source(src)
16
- rpt.length.should == expected.length
17
- (0...rpt.length).each do |smell|
18
- expected[smell].each { |patt| rpt[smell].detailed_report.should match(patt) }
19
- end
10
+ describe Duplication, "repeated method calls" do
11
+ it 'should report repeated call' do
12
+ 'def double_thing() @other.thing + @other.thing end'.should reek_only_of(:Duplication, /@other.thing/)
20
13
  end
21
- end
22
14
 
23
- describe Duplication, "repeated method calls" do
24
- check 'should report repeated call',
25
- 'def double_thing() @other.thing + @other.thing end', [[/@other.thing/]]
26
- check 'should report repeated call to lvar',
27
- 'def double_thing() other[@thing] + other[@thing] end', [[/other\[@thing\]/]]
28
- check 'should report call parameters',
29
- 'def double_thing() @other.thing(2,3) + @other.thing(2,3) end', [[/@other.thing\(2, 3\)/]]
30
- check 'should report nested calls',
31
- 'def double_thing() @other.thing.foo + @other.thing.foo end', [[/@other.thing[^\.]/], [/@other.thing.foo/]]
32
- check 'should ignore calls to new',
33
- 'def double_thing() @other.new + @other.new end', []
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\]/)
17
+ end
18
+
19
+ it 'should report call parameters' do
20
+ 'def double_thing() @other.thing(2,3) + @other.thing(2,3) end'.should reek_only_of(:Duplication, /@other.thing\(2, 3\)/)
21
+ end
22
+
23
+ it 'should report nested calls' do
24
+ ruby = 'def double_thing() @other.thing.foo + @other.thing.foo end'.to_source
25
+ ruby.should reek_of(:Duplication, /@other.thing[^\.]/)
26
+ ruby.should reek_of(:Duplication, /@other.thing.foo/)
27
+ end
28
+
29
+ it 'should ignore calls to new' do
30
+ 'def double_thing() @other.new + @other.new end'.should_not reek
31
+ end
34
32
  end
35
33
 
36
34
  describe Duplication, "non-repeated method calls" do
37
- check 'should not report similar calls',
38
- 'def equals(other) other.thing == self.thing end', []
39
- check 'should respect call parameters',
40
- 'def double_thing() @other.thing(3) + @other.thing(2) end', []
35
+ it 'should not report similar calls' do
36
+ 'def equals(other) other.thing == self.thing end'.should_not reek
37
+ end
38
+
39
+ it 'should respect call parameters' do
40
+ 'def double_thing() @other.thing(3) + @other.thing(2) end'.should_not reek
41
+ end
41
42
  end
42
43
 
43
44
  require 'ostruct'
44
45
 
45
46
  describe Duplication, '#examine' do
46
-
47
47
  before :each do
48
48
  @mc = OpenStruct.new
49
+ @dup = Duplication.new
49
50
  end
50
51
 
51
52
  it 'should return true when reporting a smell' do
52
53
  @mc.calls = {'x' => 47}
53
- Duplication.examine(@mc, []).should == true
54
+ @dup.examine(@mc, []).should == true
54
55
  end
55
56
 
56
57
  it 'should return false when not reporting a smell' do
57
58
  @mc.calls = []
58
- Duplication.examine(@mc, []).should == false
59
+ @dup.examine(@mc, []).should == false
60
+ end
61
+
62
+ it 'should return false when not reporting calls to new' do
63
+ @mc.calls = {[:call, :Set, :new] => 4}
64
+ @dup.examine(@mc, []).should == false
65
+ end
66
+ end
67
+
68
+ describe Duplication, 'when disabled' do
69
+ before :each do
70
+ @ctx = MethodContext.new(StopContext.new, [0, :double_thing])
71
+ @dup = Duplication.new({SmellDetector::ENABLED_KEY => false})
72
+ @rpt = Report.new
73
+ end
74
+
75
+ it 'should not report repeated call' do
76
+ @ctx.record_call_to([:fred])
77
+ @ctx.record_call_to([:fred])
78
+ @dup.examine(@ctx, @rpt).should == false
79
+ @rpt.length.should == 0
59
80
  end
60
81
  end
@@ -1,91 +1,129 @@
1
1
  require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
-
3
- require 'reek/method_checker'
4
- require 'reek/smells'
5
- require 'reek/report'
2
+ require 'reek/smells/feature_envy'
3
+ require 'reek/method_context'
4
+ require 'reek/stop_context'
6
5
 
7
6
  include Reek
8
7
  include Reek::Smells
9
8
 
10
- def check(desc, src, expected, pending_str = nil)
11
- it(desc) do
12
- pending(pending_str) unless pending_str.nil?
13
- rpt = Report.new
14
- cchk = MethodChecker.new(rpt, 'Thing')
15
- cchk.check_source(src)
16
- rpt.length.should == expected.length
17
- (0...rpt.length).each do |smell|
18
- expected[smell].each { |patt| rpt[smell].detailed_report.should match(patt) }
19
- end
9
+ describe FeatureEnvy, 'with only messages to self' do
10
+ it 'should not report use of self' do
11
+ 'def simple() self.to_s + self.to_i end'.should_not reek
20
12
  end
21
- end
22
13
 
23
- describe FeatureEnvy, 'with only messages to self' do
24
- check 'should not report use of self', 'def simple() self.to_s + self.to_i end', []
25
- check 'should not report vcall with no argument', 'def simple() func + grunc end', []
26
- check 'should not report vcall with argument', 'def simple(arga) func(17) + grunc(arga) end', []
14
+ it 'should not report vcall with no argument' do
15
+ 'def simple() func; end'.should_not reek
16
+ end
17
+
18
+ it 'should not report vcall with argument' do
19
+ 'def simple(arga) func(17); end'.should_not reek
20
+ end
27
21
  end
28
22
 
29
23
  describe FeatureEnvy, 'when the receiver is a parameter' do
30
- check 'should not report single use', 'def no_envy(arga) arga.barg(@item) end', []
31
- check 'should not report return value', 'def no_envy(arga) arga.barg(@item); arga end', []
32
- check 'should report many calls to parameter', 'def envy(arga) arga.b(arga) + arga.c(@fred) end', [[/arga/]]
24
+ it 'should not report single use' do
25
+ 'def no_envy(arga) arga.barg(@item) end'.should_not reek
26
+ end
27
+
28
+ it 'should not report return value' do
29
+ 'def no_envy(arga) arga.barg(@item); arga end'.should_not reek
30
+ end
31
+
32
+ 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/)
34
+ end
33
35
  end
34
36
 
35
37
  describe FeatureEnvy, 'when there are many possible receivers' do
36
- check 'should report highest affinity',
37
- 'def total_envy
38
+ it 'should report highest affinity' do
39
+ ruby = 'def total_envy
38
40
  fred = @item
39
41
  total = 0
40
42
  total += fred.price
41
43
  total += fred.tax
42
44
  total *= 1.15
43
- end', [[/total/]]
44
- check 'should report multiple affinities',
45
- 'def total_envy
45
+ end'
46
+ ruby.should reek_only_of(:FeatureEnvy, /total/)
47
+ end
48
+
49
+ it 'should report multiple affinities' do
50
+ ruby = 'def total_envy
46
51
  fred = @item
47
52
  total = 0
48
53
  total += fred.price
49
54
  total += fred.tax
50
- end', [[/fred/], [/total/]]
55
+ end'
56
+ ruby.should reek_of(:FeatureEnvy, /total/)
57
+ ruby.should reek_of(:FeatureEnvy, /fred/)
58
+ end
51
59
  end
52
60
 
53
61
  describe FeatureEnvy, 'when the receiver is external' do
54
- check 'should ignore global variables', 'def no_envy() $s2.to_a; $s2[@item] end', []
55
- check 'should not report class methods', 'def simple() self.class.new.flatten_merge(self) end', []
62
+ it 'should ignore global variables' do
63
+ 'def no_envy() $s2.to_a; $s2[@item] end'.should_not reek
64
+ end
65
+
66
+ it 'should not report class methods' do
67
+ 'def simple() self.class.new.flatten_merge(self) end'.should_not reek
68
+ end
56
69
  end
57
70
 
58
71
  describe FeatureEnvy, 'when the receiver is an ivar' do
59
- check 'should not report single use of an ivar', 'def no_envy() @item.to_a end', []
60
- check 'should not report returning an ivar', 'def no_envy() @item.to_a; @item end', []
61
- check 'should not report ivar usage in a parameter',
62
- 'def no_envy; @item.price + tax(@item) - savings(@item) end', []
63
- end
72
+ it 'should not report single use of an ivar' do
73
+ 'def no_envy() @item.to_a end'.should_not reek
74
+ end
64
75
 
65
- describe FeatureEnvy, 'when the receiver is an lvar' do
66
- check 'should not report single use of an lvar', 'def no_envy() lv = @item; lv.to_a end', []
67
- check 'should not report returning an lvar', 'def no_envy() lv = @item; lv.to_a; lv end', []
68
- check 'should report many calls to lvar', 'def envy; lv = @item; lv.price + lv.tax end', [[/lv/]]
69
- check 'should not report lvar usage in a parameter', 'def no_envy; lv = @item; lv.price + tax(lv) - savings(lv) end', []
70
- end
76
+ it 'should not report returning an ivar' do
77
+ 'def no_envy() @item.to_a; @item end'.should_not reek
78
+ end
79
+
80
+ it 'should not report ivar usage in a parameter' do
81
+ 'def no_envy; @item.price + tax(@item) - savings(@item) end'.should_not reek
82
+ end
71
83
 
72
- require 'ostruct'
84
+ 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/)
87
+ end
88
+
89
+ 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/)
92
+ end
93
+ end
73
94
 
74
95
  describe FeatureEnvy, '#examine' do
75
96
 
76
97
  before :each do
77
- @mc = OpenStruct.new # SMELL: this is a mock!!
78
- @mc.name = 'cool'
79
- @mc.refs = ObjectRefs.new
98
+ @context = MethodContext.new(StopContext.new, [:defn, :cool])
99
+ @fe = FeatureEnvy.new
80
100
  end
81
101
 
82
102
  it 'should return true when reporting a smell' do
83
- @mc.refs.record_ref([:lvar, :thing])
84
- @mc.refs.record_ref([:lvar, :thing])
85
- FeatureEnvy.examine(@mc, []).should == true
103
+ @context.refs.record_ref([:lvar, :thing])
104
+ @context.refs.record_ref([:lvar, :thing])
105
+ @fe.examine(@context, []).should == true
86
106
  end
87
107
 
88
108
  it 'should return false when not reporting a smell' do
89
- FeatureEnvy.examine(@mc, []).should == false
109
+ @fe.examine(@context, []).should == false
110
+ end
111
+ 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
90
128
  end
91
129
  end