reek 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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