reek 0.1.0 → 0.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.
data/History.txt CHANGED
@@ -1,3 +1,11 @@
1
+ == 0.1.1 2008-09-09
2
+
3
+ * Some tweaks:
4
+ * Fixed report printing for Feature Envy when the receiver is a block
5
+ * Fixed: successive iterators reported as nested
6
+ * Fixed: Long Method now reports the total length of the method
7
+ * Fixed: each smell reported only once
8
+
1
9
  == 0.1.0 2008-09-09
2
10
 
3
11
  * 1 minor enhancement:
data/README.txt CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  * http://rubyforge.org/projects/reek/
4
4
  * http://www.refactoring-workbook.com/reek
5
- * mailto:kevin@rutherford-software.com
5
+ * kevin@rutherford-software.com
6
6
 
7
7
  == DESCRIPTION:
8
8
 
data/bin/reek CHANGED
File without changes
@@ -12,19 +12,20 @@ module Reek
12
12
  super(smells)
13
13
  @class_name = klass_name
14
14
  @description = klass_name
15
- @top_level_block = true
16
15
  @calls = Hash.new(0)
17
16
  @lvars = Set.new
18
17
  @inside_an_iter = false
19
18
  end
20
19
 
21
20
  def process_defn(exp)
21
+ @num_statements = 0
22
22
  @description = "#{@class_name}##{exp[1]}"
23
23
  UncommunicativeName.check(exp[1], self, 'method')
24
24
  process(exp[2])
25
25
  @lvars.each {|lvar| UncommunicativeName.check(lvar, self, 'local variable') }
26
26
  UtilityFunction.check(@calls, self)
27
27
  FeatureEnvy.check(@calls, self)
28
+ LongMethod.check(@num_statements, self)
28
29
  s(exp)
29
30
  end
30
31
 
@@ -41,18 +42,14 @@ module Reek
41
42
 
42
43
  def process_iter(exp)
43
44
  NestedIterators.check(@inside_an_iter, self)
44
- @top_level_block = false
45
45
  @inside_an_iter = true
46
46
  exp[1..-1].each { |s| process(s) }
47
+ @inside_an_iter = false
47
48
  s(exp)
48
49
  end
49
50
 
50
51
  def process_block(exp)
51
- if @top_level_block
52
- LongMethod.check(exp, self)
53
- else
54
- LongBlock.check(exp, self)
55
- end
52
+ @num_statements += count_statements(exp)
56
53
  exp[1..-1].each { |s| process(s) }
57
54
  s(exp)
58
55
  end
@@ -108,5 +105,13 @@ module Reek
108
105
  process(exp[2])
109
106
  s(exp)
110
107
  end
108
+
109
+ private
110
+
111
+ def count_statements(exp)
112
+ result = exp.length - 1
113
+ result -= 1 if Array === exp[1] and exp[1][0] == :args
114
+ result
115
+ end
111
116
  end
112
117
  end
data/lib/reek/printer.rb CHANGED
@@ -19,7 +19,11 @@ module Reek
19
19
 
20
20
  def print(sexp)
21
21
  @report = sexp.inspect
22
- process(sexp)
22
+ begin
23
+ process(sexp)
24
+ rescue
25
+ raise "Error in print, parsing:\n #{sexp.inspect}"
26
+ end
23
27
  @report
24
28
  end
25
29
 
@@ -43,6 +47,11 @@ module Reek
43
47
  s(exp)
44
48
  end
45
49
 
50
+ def process_iter(exp)
51
+ @report = 'block'
52
+ s(exp)
53
+ end
54
+
46
55
  def process_call(exp)
47
56
  @report = "#{exp[1]}.#{exp[2]}"
48
57
  @report += "(#{exp[3]})" if exp.length > 3
data/lib/reek/report.rb CHANGED
@@ -1,10 +1,12 @@
1
1
  $:.unshift File.dirname(__FILE__)
2
2
 
3
+ require 'set'
4
+
3
5
  module Reek
4
6
 
5
7
  class Report
6
8
  def initialize # :nodoc:
7
- @smells = []
9
+ @smells = SortedSet.new
8
10
  end
9
11
 
10
12
  def <<(smell) # :nodoc:
@@ -14,13 +16,17 @@ module Reek
14
16
  def empty? # :nodoc:
15
17
  @smells.empty?
16
18
  end
17
-
19
+
20
+ def include?(obj) # :nodoc:
21
+ @smells.include?(obj)
22
+ end
23
+
18
24
  def length # :nodoc:
19
25
  @smells.length
20
26
  end
21
-
27
+
22
28
  def [](i) # :nodoc:
23
- @smells[i]
29
+ @smells.to_a[i]
24
30
  end
25
31
 
26
32
  # Creates a formatted report of all the smells recorded in
data/lib/reek/smells.rb CHANGED
@@ -5,6 +5,8 @@ require 'reek/printer'
5
5
  module Reek
6
6
 
7
7
  class Smell
8
+ include Comparable
9
+
8
10
  def self.convert_camel_case(class_name)
9
11
  class_name.gsub(/([a-z])([A-Z])/) { |s| "#{$1} #{$2}"}
10
12
  end
@@ -18,8 +20,16 @@ module Reek
18
20
  context.report(smell) if smell.recognise?(exp)
19
21
  end
20
22
 
21
- def ==(other)
22
- self.report == other.report
23
+ def hash # :nodoc:
24
+ report.hash
25
+ end
26
+
27
+ def <=>(other) # :nodoc:
28
+ self.report <=> other.report
29
+ end
30
+
31
+ def eql?(other) # :nodoc:
32
+ self.report.eql?(other.report)
23
33
  end
24
34
 
25
35
  def name
@@ -62,36 +72,13 @@ module Reek
62
72
  class LongMethod < Smell
63
73
  MAX_ALLOWED = 5
64
74
 
65
- def count_statements(exp)
66
- result = exp.length - 1
67
- result -= 1 if Array === exp[1] and exp[1][0] == :args
68
- result
69
- end
70
-
71
- def recognise?(exp)
72
- count_statements(exp) > MAX_ALLOWED
73
- end
74
-
75
- def detailed_report
76
- "#{@context} has > #{MAX_ALLOWED} statements"
77
- end
78
- end
79
-
80
- class LongBlock < Smell
81
- MAX_ALLOWED = 5
82
-
83
- def count_statements(exp)
84
- result = exp.length - 1
85
- result -= 1 if Array === exp[1] and exp[1][0] == :args
86
- result
87
- end
88
-
89
- def recognise?(exp)
90
- count_statements(exp) > MAX_ALLOWED
75
+ def recognise?(num_stmts)
76
+ @num_stmts = num_stmts
77
+ num_stmts > MAX_ALLOWED
91
78
  end
92
79
 
93
80
  def detailed_report
94
- "#{@context} has a block with > #{MAX_ALLOWED} statements"
81
+ "#{@context} has approx #{@num_stmts} statements"
95
82
  end
96
83
  end
97
84
 
@@ -105,7 +92,7 @@ module Reek
105
92
  max = calls.empty? ? 0 : calls.values.max
106
93
  return false unless max > calls[:self]
107
94
  receivers = calls.keys.select { |key| calls[key] == max }
108
- @receiver = receivers.map {|r| Printer.print(r)}.sort.join(' or ')
95
+ @receiver = receivers.map {|r| Printer.print(r)}.sort.join(' and ')
109
96
  return true
110
97
  end
111
98
 
data/lib/reek/version.rb CHANGED
@@ -2,7 +2,7 @@ module Reek #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 1
5
- TINY = 0
5
+ TINY = 1
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -25,8 +25,8 @@ describe MethodChecker, "(Feature Envy)" do
25
25
  it 'should report simple parameter call' do
26
26
  @cchk.check_source('def simple(arga) arga[3] end')
27
27
  @rpt.length.should == 2
28
- @rpt[0].should == UtilityFunction.new(@cchk)
29
- @rpt[1].should == FeatureEnvy.new(@cchk, ':arga')
28
+ @rpt[1].should == UtilityFunction.new(@cchk)
29
+ @rpt[0].should == FeatureEnvy.new(@cchk, ':arga')
30
30
  end
31
31
 
32
32
  it 'should report highest affinity' do
@@ -38,7 +38,7 @@ describe MethodChecker, "(Feature Envy)" do
38
38
  it 'should report multiple affinities' do
39
39
  @cchk.check_source('def simple(arga) s1 = ""; s1.to_s; s2 = ""; s2.to_s; @m = 34; end')
40
40
  @rpt.length.should == 1
41
- @rpt[0].should == FeatureEnvy.new(@cchk, ':s1 or :s2')
41
+ @rpt[0].should == FeatureEnvy.new(@cchk, ':s1 and :s2')
42
42
  end
43
43
 
44
44
  it 'should not reference global variables' do
@@ -20,7 +20,31 @@ describe MethodChecker, "(Long Method)" do
20
20
  it 'should report long methods' do
21
21
  @cchk.check_source('def long(arga) alf = f(1);@bet = 2;@cut = 3;@dit = 4; @emp = 5;@fry = 6;end')
22
22
  @rpt.length.should == 1
23
- @rpt[0].should == LongMethod.new(@cchk)
23
+ @rpt[0].should be_instance_of(LongMethod)
24
+ end
25
+
26
+ it 'should only report a long method once' do
27
+ source =<<EOS
28
+ def standard_entries(rbconfig)
29
+ @abc = rbconfig
30
+ rubypath = File.join(@abc['bindir'], @abc['ruby_install_name'] + c['EXEEXT'])
31
+ major = c['MAJOR'].to_i
32
+ minor = c['MINOR'].to_i
33
+ teeny = c['TEENY'].to_i
34
+ version = ""
35
+ if c['rubylibdir']
36
+ libruby = "/lib/ruby"
37
+ librubyver = "/lib/ruby/"
38
+ librubyverarch = "/lib/ruby/"
39
+ siteruby = "lib/ruby/version/site_ruby"
40
+ siterubyver = siteruby
41
+ siterubyverarch = "$siterubyver/['arch']}"
42
+ end
43
+ end
44
+ EOS
45
+ @cchk.check_source(source)
46
+ @rpt.length.should == 1
47
+ @rpt[0].should be_instance_of(LongMethod)
24
48
  end
25
49
  end
26
50
 
@@ -47,6 +71,5 @@ end
47
71
  EOS
48
72
  @cchk.check_source(src)
49
73
  @rpt.length.should == 1
50
- @rpt[0].report.should match(/block/)
51
74
  end
52
75
  end
@@ -16,5 +16,27 @@ describe MethodChecker, " nested iterators" do
16
16
  @chk.check_source('def bad(fred) @fred.each {|item| item.each {|ting| ting.ting} } end')
17
17
  @rpt.length.should == 1
18
18
  end
19
+
20
+ it "should not report method with successive iterators" do
21
+ source =<<EOS
22
+ def bad(fred)
23
+ @fred.each {|item| item.each }
24
+ @jim.each {|item| item.each }
25
+ end
26
+ EOS
27
+ @chk.check_source(source)
28
+ @rpt.should be_empty
29
+ end
30
+
31
+ it "should report nested iterators only once per method" do
32
+ source =<<EOS
33
+ def bad(fred)
34
+ @fred.each {|item| item.each {|part| @jim.send} }
35
+ @jim.each {|item| item.each {|piece| @fred.send} }
36
+ end
37
+ EOS
38
+ @chk.check_source(source)
39
+ @rpt.length.should == 1
40
+ end
19
41
  end
20
42
 
@@ -1,12 +1,26 @@
1
1
  require File.dirname(__FILE__) + '/../spec_helper.rb'
2
2
 
3
3
  require 'reek/method_checker'
4
+ require 'reek/smells'
4
5
  require 'reek/report'
5
6
 
6
7
  include Reek
7
8
 
8
- describe Report, "to_s" do
9
+ describe Report, " when empty" do
10
+ before(:each) do
11
+ @rpt = Report.new
12
+ end
13
+
14
+ it 'should have zero length' do
15
+ @rpt.length.should == 0
16
+ end
17
+
18
+ it 'should claim to be empty' do
19
+ @rpt.should be_empty
20
+ end
21
+ end
9
22
 
23
+ describe Report, "to_s" do
10
24
  before(:each) do
11
25
  rpt = Report.new
12
26
  chk = MethodChecker.new(rpt, 'Thing')
@@ -24,3 +38,12 @@ describe Report, "to_s" do
24
38
  end
25
39
  end
26
40
 
41
+ describe Report, " as a SortedSet" do
42
+ it 'should only add a smell once' do
43
+ rpt = Report.new
44
+ rpt << UtilityFunction.new(self)
45
+ rpt.length.should == 1
46
+ rpt << UtilityFunction.new(self)
47
+ rpt.length.should == 1
48
+ end
49
+ end
@@ -10,8 +10,24 @@ describe Smell, "camel case converter" do
10
10
  end
11
11
 
12
12
  it "should display correct name in report" do
13
- mchk = MethodChecker.new([], 'Class')
14
- smell = FeatureEnvy.new(mchk, [:lvar, :fred])
13
+ smell = FeatureEnvy.new(self, [:lvar, :fred])
15
14
  smell.report.should match(/[#{smell.name}]/)
16
15
  end
17
16
  end
17
+
18
+ describe Smell, ' in comparisons' do
19
+ it 'should hash equal when the smell is the same' do
20
+ UtilityFunction.new(self).hash.should == UtilityFunction.new(self).hash
21
+ NestedIterators.new(self).hash.should == NestedIterators.new(self).hash
22
+ end
23
+
24
+ it 'should compare equal when the smell is the same' do
25
+ UtilityFunction.new(self).should == UtilityFunction.new(self)
26
+ NestedIterators.new(self).should == NestedIterators.new(self)
27
+ end
28
+
29
+ it 'should compare equal when using <=>' do
30
+ (UtilityFunction.new(self) <=> UtilityFunction.new(self)).should == 0
31
+ (NestedIterators.new(self) <=> NestedIterators.new(self)).should == 0
32
+ end
33
+ end
data/website/index.html CHANGED
@@ -33,7 +33,7 @@
33
33
  <h1>Code smell detector</h1>
34
34
  <div id="version" class="clickable" onclick='document.location = "http://rubyforge.org/projects/reek"; return false'>
35
35
  <p>Get Version</p>
36
- <a href="http://rubyforge.org/projects/reek" class="numbers">0.1.0</a>
36
+ <a href="http://rubyforge.org/projects/reek" class="numbers">0.1.1</a>
37
37
  </div>
38
38
  <h1>&#x2192; &#8216;reek&#8217;</h1>
39
39
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reek
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Rutherford