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 +9 -0
- data/README.txt +12 -1
- data/lib/reek.rb +12 -9
- data/lib/reek/checker.rb +13 -5
- data/lib/reek/class_checker.rb +4 -4
- data/lib/reek/method_checker.rb +3 -0
- data/lib/reek/report.rb +7 -5
- data/lib/reek/smells.rb +12 -3
- data/lib/reek/version.rb +2 -2
- data/spec/reek/long_method_spec.rb +52 -0
- data/spec/reek/method_checker_spec.rb +0 -38
- data/spec/reek/nested_iterators_spec.rb +20 -0
- data/tasks/rspec.rake +6 -6
- data/website/index.html +13 -8
- data/website/index.txt +9 -5
- metadata +4 -2
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
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
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 =
|
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
|
data/lib/reek/class_checker.rb
CHANGED
@@ -7,12 +7,12 @@ module Reek
|
|
7
7
|
|
8
8
|
class ClassChecker < Checker
|
9
9
|
|
10
|
-
def initialize(
|
11
|
-
super(
|
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)
|
data/lib/reek/method_checker.rb
CHANGED
@@ -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
|
-
|
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}
|
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
@@ -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
|
24
|
-
Spec::Rake::SpecTask.new(:
|
25
|
-
t.
|
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
|
37
|
-
RCov::VerifyTask.new(:
|
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
|
36
|
+
<a href="http://rubyforge.org/projects/reek" class="numbers">0.1.0</a>
|
37
37
|
</div>
|
38
38
|
<h1>→ ‘reek’</h1>
|
39
39
|
|
@@ -62,16 +62,21 @@
|
|
62
62
|
<p>(More details coming soon…)</p>
|
63
63
|
|
64
64
|
|
65
|
-
<h2>
|
65
|
+
<h2>Code Smells</h2>
|
66
66
|
|
67
67
|
|
68
|
-
<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
|
-
<
|
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>,
|
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.
|
23
|
+
h2. Code Smells
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
h2. Forum
|
25
|
+
reek currently includes very naive checks for the following code smells:
|
28
26
|
|
29
|
-
|
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
|
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-
|
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
|