reek 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,12 @@
1
+ == 0.1.0 2008-09-09
2
+
3
+ * 1 minor enhancement:
4
+ * Added a check for nested iterators within a method
5
+ * Some tweaks:
6
+ * Begun adding some rdoc
7
+ * Split some of the specs into more meaningful chunks
8
+ * Updated the rakefile so that rcov is no longer the default
9
+
1
10
  == 0.0.1 2008-09-08
2
11
 
3
12
  * 1 major enhancement:
data/README.txt CHANGED
@@ -9,8 +9,19 @@
9
9
  Reek is a tool that examines Ruby classes, modules and methods and
10
10
  reports any code smells it finds.
11
11
 
12
+ === SUPPORTED SMELLS:
13
+
14
+ * Long Method
15
+ * Large Class
16
+ * Feature Envy
17
+ * Uncommunicative Name
18
+ * Long Parameter List
19
+ * Utility Function
20
+ * Nested Iterators
21
+
12
22
  == FEATURES/PROBLEMS:
13
23
 
24
+ * Most of the current checks are quite naive.
14
25
  * Not many smells checked right now; more coming soon.
15
26
  * The current Feature Envy check is probably over zealous.
16
27
  * There's no convenient programmer's API just yet.
@@ -32,7 +43,7 @@ reports any code smells it finds.
32
43
 
33
44
  (The MIT License)
34
45
 
35
- Copyright (c) Kevin Rutherford, Rutherford Software
46
+ Copyright (c) 2008 Kevin Rutherford, Rutherford Software
36
47
 
37
48
  Permission is hereby granted, free of charge, to any person obtaining
38
49
  a copy of this software and associated documentation files (the
data/lib/reek.rb CHANGED
@@ -3,14 +3,17 @@ $:.unshift File.dirname(__FILE__)
3
3
  require 'reek/class_checker'
4
4
  require 'reek/report'
5
5
 
6
- module Reek
6
+ module Reek # :doc:
7
7
 
8
- def self.analyse(*klasses)
9
- report = Report.new
10
- klasses.each do |klass|
11
- ClassChecker.new(report).check_object(klass)
12
- end
13
- report
14
- end
15
-
8
+ #
9
+ # Analyse the given instances of class Class, looking for code smells.
10
+ # Returns a +Report+ listing the smells found.
11
+ #
12
+ def self.analyse(*klasses) # :doc:
13
+ report = Report.new
14
+ klasses.each do |klass|
15
+ ClassChecker.new(report).check_object(klass)
16
+ end
17
+ report
18
+ end
16
19
  end
data/lib/reek/checker.rb CHANGED
@@ -9,31 +9,39 @@ module Reek
9
9
  class Checker < SexpProcessor
10
10
  attr_accessor :description
11
11
 
12
- def initialize(smells)
12
+ # Creates a new Ruby code checker. Any smells discovered by
13
+ # +check_source+ or +check_object+ will be stored in +report+.
14
+ def initialize(report)
13
15
  super()
14
16
  @require_empty = false
15
- @smells = smells
17
+ @smells = report
16
18
  @description = ''
17
19
  @unsupported -= [:cfunc]
18
20
  end
19
21
 
20
- def report(smell)
22
+ def report(smell) # :nodoc:
21
23
  @smells << smell
22
24
  end
23
25
 
26
+ # Analyses the given Ruby source +code+ looking for smells.
27
+ # Any smells found are saved in the +Report+ object that
28
+ # was passed to this object's constructor.
24
29
  def check_source(code)
25
30
  check_parse_tree ParseTree.new.parse_tree_for_string(code)
26
31
  end
27
32
 
33
+ # Analyses the given Ruby object +obj+ looking for smells.
34
+ # Any smells found are saved in the +Report+ object that
35
+ # was passed to this object's constructor.
28
36
  def check_object(obj)
29
37
  check_parse_tree ParseTree.new.parse_tree(obj)
30
38
  end
31
39
 
32
- def to_s
40
+ def to_s # :nodoc:
33
41
  description
34
42
  end
35
43
 
36
- def check_parse_tree(sexp)
44
+ def check_parse_tree(sexp) # :nodoc:
37
45
  sexp.each { |exp| process(exp) }
38
46
  end
39
47
  end
@@ -7,12 +7,12 @@ module Reek
7
7
 
8
8
  class ClassChecker < Checker
9
9
 
10
- def initialize(smells)
11
- super(smells)
10
+ def initialize(report)
11
+ super(report)
12
12
  @description = ''
13
13
  end
14
14
 
15
- def process_class(exp)
15
+ def process_class(exp) # :nodoc:
16
16
  @description = exp[1].to_s
17
17
  superclass = exp[2]
18
18
  LargeClass.check(@description, self)
@@ -20,7 +20,7 @@ module Reek
20
20
  s(exp)
21
21
  end
22
22
 
23
- def process_defn(exp)
23
+ def process_defn(exp) # :nodoc:
24
24
  bc = Reek::MethodChecker.new(@smells, @description)
25
25
  bc.process(exp)
26
26
  s(exp)
@@ -15,6 +15,7 @@ module Reek
15
15
  @top_level_block = true
16
16
  @calls = Hash.new(0)
17
17
  @lvars = Set.new
18
+ @inside_an_iter = false
18
19
  end
19
20
 
20
21
  def process_defn(exp)
@@ -39,7 +40,9 @@ module Reek
39
40
  end
40
41
 
41
42
  def process_iter(exp)
43
+ NestedIterators.check(@inside_an_iter, self)
42
44
  @top_level_block = false
45
+ @inside_an_iter = true
43
46
  exp[1..-1].each { |s| process(s) }
44
47
  s(exp)
45
48
  end
data/lib/reek/report.rb CHANGED
@@ -3,26 +3,28 @@ $:.unshift File.dirname(__FILE__)
3
3
  module Reek
4
4
 
5
5
  class Report
6
- def initialize
6
+ def initialize # :nodoc:
7
7
  @smells = []
8
8
  end
9
9
 
10
- def <<(smell)
10
+ def <<(smell) # :nodoc:
11
11
  @smells << smell
12
12
  end
13
13
 
14
- def empty?
14
+ def empty? # :nodoc:
15
15
  @smells.empty?
16
16
  end
17
17
 
18
- def length
18
+ def length # :nodoc:
19
19
  @smells.length
20
20
  end
21
21
 
22
- def [](i)
22
+ def [](i) # :nodoc:
23
23
  @smells[i]
24
24
  end
25
25
 
26
+ # Creates a formatted report of all the smells recorded in
27
+ # this report.
26
28
  def to_s
27
29
  @smells.map {|smell| smell.report}.join("\n")
28
30
  end
data/lib/reek/smells.rb CHANGED
@@ -103,15 +103,14 @@ module Reek
103
103
 
104
104
  def recognise?(calls)
105
105
  max = calls.empty? ? 0 : calls.values.max
106
- mine = calls[:self]
107
- return false unless max > mine
106
+ return false unless max > calls[:self]
108
107
  receivers = calls.keys.select { |key| calls[key] == max }
109
108
  @receiver = receivers.map {|r| Printer.print(r)}.sort.join(' or ')
110
109
  return true
111
110
  end
112
111
 
113
112
  def detailed_report
114
- "#{@context} could be moved to #{@receiver}"
113
+ "#{@context} uses #{@receiver} more than self"
115
114
  end
116
115
  end
117
116
 
@@ -156,4 +155,14 @@ module Reek
156
155
  "#{@context} uses the #{@symbol_type} name '#{@symbol}'"
157
156
  end
158
157
  end
158
+
159
+ class NestedIterators < Smell
160
+ def recognise?(already_in_iter)
161
+ already_in_iter
162
+ end
163
+
164
+ def detailed_report
165
+ "#{@context} has nested iterators"
166
+ end
167
+ end
159
168
  end
data/lib/reek/version.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  module Reek #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 0
5
- TINY = 1
4
+ MINOR = 1
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -0,0 +1,52 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ require 'reek/method_checker'
4
+ require 'reek/report'
5
+
6
+ include Reek
7
+
8
+ describe MethodChecker, "(Long Method)" do
9
+
10
+ before(:each) do
11
+ @rpt = Report.new
12
+ @cchk = MethodChecker.new(@rpt, 'Thing')
13
+ end
14
+
15
+ it 'should not report short methods' do
16
+ @cchk.check_source('def short(arga) alf = f(1);@bet = 2;@cut = 3;@dit = 4; @emp = 5;end')
17
+ @rpt.should be_empty
18
+ end
19
+
20
+ it 'should report long methods' do
21
+ @cchk.check_source('def long(arga) alf = f(1);@bet = 2;@cut = 3;@dit = 4; @emp = 5;@fry = 6;end')
22
+ @rpt.length.should == 1
23
+ @rpt[0].should == LongMethod.new(@cchk)
24
+ end
25
+ end
26
+
27
+ describe MethodChecker, "(Long Block)" do
28
+
29
+ before(:each) do
30
+ @rpt = Report.new
31
+ @cchk = MethodChecker.new(@rpt, 'Thing')
32
+ end
33
+
34
+ it 'should report long inner block' do
35
+ src = <<EOS
36
+ def long(arga)
37
+ f(3)
38
+ 37.each do |xyzero|
39
+ xyzero = 1
40
+ xyzero = 2
41
+ xyzero = 3
42
+ xyzero = 4
43
+ xyzero = 5
44
+ xyzero = 6
45
+ end
46
+ end
47
+ EOS
48
+ @cchk.check_source(src)
49
+ @rpt.length.should == 1
50
+ @rpt[0].report.should match(/block/)
51
+ end
52
+ end
@@ -149,44 +149,6 @@ describe LongParameterList, '#report' do
149
149
 
150
150
  end
151
151
 
152
- describe MethodChecker, "(Long Method)" do
153
-
154
- before(:each) do
155
- @rpt = Report.new
156
- @cchk = MethodChecker.new(@rpt, 'Thing')
157
- end
158
-
159
- it 'should not report short methods' do
160
- @cchk.check_source('def short(arga) alf = f(1);@bet = 2;@cut = 3;@dit = 4; @emp = 5;end')
161
- @rpt.should be_empty
162
- end
163
-
164
- it 'should report long methods' do
165
- @cchk.check_source('def long(arga) alf = f(1);@bet = 2;@cut = 3;@dit = 4; @emp = 5;@fry = 6;end')
166
- @rpt.length.should == 1
167
- @rpt[0].should == LongMethod.new(@cchk)
168
- end
169
-
170
- it 'should report long inner block' do
171
- src = <<EOS
172
- def long(arga)
173
- f(3)
174
- 37.each do |xyzero|
175
- xyzero = 1
176
- xyzero = 2
177
- xyzero = 3
178
- xyzero = 4
179
- xyzero = 5
180
- xyzero = 6
181
- end
182
- end
183
- EOS
184
- @cchk.check_source(src)
185
- @rpt.length.should == 1
186
- @rpt[0].report.should match(/block/)
187
- end
188
- end
189
-
190
152
  describe MethodChecker, 'when given a C extension' do
191
153
 
192
154
  before(:each) do
@@ -0,0 +1,20 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ require 'reek/method_checker'
4
+ require 'reek/report'
5
+
6
+ include Reek
7
+
8
+ describe MethodChecker, " nested iterators" do
9
+
10
+ before(:each) do
11
+ @rpt = Report.new
12
+ @chk = MethodChecker.new(@rpt, 'Thing')
13
+ end
14
+
15
+ it "should report nested iterators in a method" do
16
+ @chk.check_source('def bad(fred) @fred.each {|item| item.each {|ting| ting.ting} } end')
17
+ @rpt.length.should == 1
18
+ end
19
+ end
20
+
data/tasks/rspec.rake CHANGED
@@ -20,12 +20,12 @@ end
20
20
  REPORT_DIR = 'spec/output/coverage'
21
21
  CLEAN.include(REPORT_DIR)
22
22
 
23
- desc 'runs the specs in colour'
24
- Spec::Rake::SpecTask.new(:spec_colour) do |t|
25
- t.spec_opts = ['--options', "spec/spec.opts"]
26
- t.spec_files = FileList['spec/**/*_spec.rb']
23
+ desc "runs the specs"
24
+ Spec::Rake::SpecTask.new(:spec) do |t|
25
+ t.spec_files = FileList['spec/**/*.rb']
27
26
  end
28
27
 
28
+ desc "runs the specs and reports coverage in #{REPORT_DIR}"
29
29
  Spec::Rake::SpecTask.new(:spec_rcov) do |t|
30
30
  t.spec_files = FileList['spec/**/*.rb']
31
31
  t.rcov = true
@@ -33,8 +33,8 @@ Spec::Rake::SpecTask.new(:spec_rcov) do |t|
33
33
  t.rcov_opts = ['--exclude', 'spec,\.autotest']
34
34
  end
35
35
 
36
- "runs the specs and reports coverage in #{REPORT_DIR}"
37
- RCov::VerifyTask.new(:spec => :spec_rcov) do |t|
36
+ desc "runs the specs and checks for 100% coverage"
37
+ RCov::VerifyTask.new(:rcov => :spec_rcov) do |t|
38
38
  t.index_html = "#{REPORT_DIR}/index.html"
39
39
  t.threshold = 100
40
40
  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.0.1</a>
36
+ <a href="http://rubyforge.org/projects/reek" class="numbers">0.1.0</a>
37
37
  </div>
38
38
  <h1>&#x2192; &#8216;reek&#8217;</h1>
39
39
 
@@ -62,16 +62,21 @@
62
62
  <p>(More details coming soon&#8230;)</p>
63
63
 
64
64
 
65
- <h2>Demonstration of usage</h2>
65
+ <h2>Code Smells</h2>
66
66
 
67
67
 
68
- <p>(More details coming soon&#8230;)</p>
69
-
70
-
71
- <h2>Forum</h2>
68
+ <p>reek currently includes very naive checks for the following code smells:</p>
72
69
 
73
70
 
74
- <p>Coming soon&#8230;</p>
71
+ <ul>
72
+ <li>Long Method</li>
73
+ <li>Large Class</li>
74
+ <li>Feature Envy</li>
75
+ <li>Uncommunicative Name</li>
76
+ <li>Long Parameter List</li>
77
+ <li>Utility Function</li>
78
+ <li>Nested Iterators</li>
79
+ </ul>
75
80
 
76
81
 
77
82
  <h2>How to access the source code</h2>
@@ -91,7 +96,7 @@
91
96
 
92
97
  <p>Comments are welcome. Send an email to <a href="mailto:kevin@rutherford-software.com">Kevin Rutherford</a></p>
93
98
  <p class="coda">
94
- <a href="http://www.kevinrutherford.co.uk">Kevin Rutherford</a>, 8th September 2008<br>
99
+ <a href="http://www.kevinrutherford.co.uk">Kevin Rutherford</a>, 9th September 2008<br>
95
100
  Theme extended from <a href="http://rb2js.rubyforge.org/">Paul Battley</a>
96
101
  </p>
97
102
  </div>
data/website/index.txt CHANGED
@@ -20,13 +20,17 @@ h2. The basics
20
20
 
21
21
  (More details coming soon...)
22
22
 
23
- h2. Demonstration of usage
23
+ h2. Code Smells
24
24
 
25
- (More details coming soon...)
26
-
27
- h2. Forum
25
+ reek currently includes very naive checks for the following code smells:
28
26
 
29
- Coming soon...
27
+ * Long Method
28
+ * Large Class
29
+ * Feature Envy
30
+ * Uncommunicative Name
31
+ * Long Parameter List
32
+ * Utility Function
33
+ * Nested Iterators
30
34
 
31
35
  h2. How to access the source code
32
36
 
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.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Rutherford
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-09-08 00:00:00 +01:00
12
+ date: 2008-09-09 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -60,7 +60,9 @@ files:
60
60
  - spec/reek/class_checker_spec.rb
61
61
  - spec/reek/feature_envy_spec.rb
62
62
  - spec/reek/large_class_spec.rb
63
+ - spec/reek/long_method_spec.rb
63
64
  - spec/reek/method_checker_spec.rb
65
+ - spec/reek/nested_iterators_spec.rb
64
66
  - spec/reek/report_spec.rb
65
67
  - spec/reek/smell_spec.rb
66
68
  - spec/reek/uncommunicative_name_spec.rb