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
@@ -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