reek 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +12 -0
- data/README.txt +10 -5
- data/bin/reek +19 -4
- data/lib/reek.rb +18 -12
- data/lib/reek/method_checker.rb +11 -1
- data/lib/reek/options.rb +17 -15
- data/lib/reek/rake_task.rb +128 -0
- data/lib/reek/smells.rb +22 -1
- data/lib/reek/version.rb +2 -2
- data/spec/integration_spec.rb +0 -10
- data/spec/reek/control_couple_spec.rb +31 -0
- data/spec/reek/feature_envy_spec.rb +24 -2
- data/spec/reek/options_spec.rb +3 -3
- data/spec/reek/report_spec.rb +2 -2
- data/spec/reek/smell_spec.rb +3 -3
- data/spec/reek/utility_function_spec.rb +22 -2
- data/spec/reek_source_spec.rb +2 -1
- data/spec/samples/inline.reek +2 -0
- data/spec/samples/optparse.reek +9 -2
- data/spec/samples/redcloth.reek +7 -1
- data/spec/script_spec.rb +68 -0
- data/tasks/reek.rake +7 -0
- data/website/index.html +1 -1
- metadata +21 -7
data/History.txt
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
== 0.2.x 2008-??-??
|
2
|
+
|
3
|
+
* Minor enhancements:
|
4
|
+
* New smell: first naive checks for Control Couple
|
5
|
+
* reek now only checks sources passed on the command line
|
6
|
+
* Code snippets can be supplied on the commandline
|
7
|
+
* Added headings and warnings count when smells in multiple files
|
8
|
+
* Added Reek::RakeTask to run reek from rakefiles
|
9
|
+
* Tweaks:
|
10
|
+
* Fixed: Returns exit status 2 when smells are reported
|
11
|
+
* Fixed: no longer claims an empty method is a Utility Function
|
12
|
+
|
1
13
|
== 0.2.3 2008-09-22
|
2
14
|
|
3
15
|
* Minor enhancements:
|
data/README.txt
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
= reek
|
2
2
|
|
3
3
|
* http://rubyforge.org/projects/reek/
|
4
|
-
*
|
5
|
-
* kevin@rutherford-software.com
|
4
|
+
* mailto:kevin@rutherford-software.com
|
6
5
|
|
7
6
|
== DESCRIPTION:
|
8
7
|
|
@@ -11,6 +10,8 @@ reports any code smells it finds.
|
|
11
10
|
|
12
11
|
=== SUPPORTED CODE SMELLS:
|
13
12
|
|
13
|
+
Reek currently includes very naive checks for the following smells:
|
14
|
+
|
14
15
|
* Long Method
|
15
16
|
* Large Class
|
16
17
|
* Feature Envy
|
@@ -18,6 +19,7 @@ reports any code smells it finds.
|
|
18
19
|
* Long Parameter List
|
19
20
|
* Utility Function
|
20
21
|
* Nested Iterators
|
22
|
+
* Control Couple
|
21
23
|
|
22
24
|
== FEATURES/PROBLEMS:
|
23
25
|
|
@@ -28,13 +30,16 @@ reports any code smells it finds.
|
|
28
30
|
|
29
31
|
== SYNOPSIS:
|
30
32
|
|
31
|
-
$ reek [options]
|
33
|
+
$ reek [options] sources
|
32
34
|
|
33
35
|
(See `reek --help` for details.)
|
34
36
|
|
35
|
-
==
|
37
|
+
== DEPENDENCIES:
|
38
|
+
|
39
|
+
Reek makes use of the following other gems:
|
36
40
|
|
37
41
|
* ParseTree
|
42
|
+
* sexp_processor
|
38
43
|
|
39
44
|
== INSTALL:
|
40
45
|
|
@@ -42,7 +47,7 @@ Get the latest version of the gem:
|
|
42
47
|
|
43
48
|
$ gem install reek
|
44
49
|
|
45
|
-
Or get the latest
|
50
|
+
Or get the latest source code from:
|
46
51
|
|
47
52
|
$ git clone git://github.com/kevinrutherford/reek.git
|
48
53
|
|
data/bin/reek
CHANGED
@@ -2,9 +2,24 @@
|
|
2
2
|
#
|
3
3
|
# Created on 2008-2-17.
|
4
4
|
# Copyright (c) 2008 Kevin Rutherford, Rutherford Software Ltd. All rights reserved.
|
5
|
+
#
|
6
|
+
|
7
|
+
require File.join(File.dirname(__FILE__), '../lib/', 'reek')
|
8
|
+
require File.join(File.dirname(__FILE__), '../lib/', 'reek/options')
|
5
9
|
|
6
|
-
|
7
|
-
|
10
|
+
sources = Reek::Options.parse(ARGV)
|
11
|
+
exitstatus = 0
|
12
|
+
sources.each do |src|
|
13
|
+
smells = Reek.analyse(src)
|
14
|
+
next if smells.empty?
|
15
|
+
if sources.size == 1
|
16
|
+
puts smells.to_s
|
17
|
+
else
|
18
|
+
puts "\"#{src}\" -- #{smells.length} warnings:"
|
19
|
+
puts smells.to_s
|
20
|
+
puts
|
21
|
+
end
|
22
|
+
exitstatus = 2
|
23
|
+
end
|
8
24
|
|
9
|
-
|
10
|
-
puts Reek.analyse(*files).to_s
|
25
|
+
exit(exitstatus)
|
data/lib/reek.rb
CHANGED
@@ -5,16 +5,22 @@ require 'reek/report'
|
|
5
5
|
|
6
6
|
module Reek # :doc:
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
8
|
+
#
|
9
|
+
# Analyse the given source files, looking for code smells.
|
10
|
+
# Returns a +Report+ listing the smells found.
|
11
|
+
#
|
12
|
+
def self.analyse(*files) # :doc:
|
13
|
+
report = Report.new
|
14
|
+
files.each do |file|
|
15
|
+
source = Reek.read(file)
|
16
|
+
FileChecker.new(report).check_source(source)
|
17
|
+
end
|
18
|
+
report
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def self.read(file)
|
24
|
+
File.exists?(file) ? IO.readlines(file).join : file
|
25
|
+
end
|
20
26
|
end
|
data/lib/reek/method_checker.rb
CHANGED
@@ -29,6 +29,7 @@ module Reek
|
|
29
29
|
def process_args(exp)
|
30
30
|
LongParameterList.check(exp, self)
|
31
31
|
exp.each { |arg| UncommunicativeName.check(arg, self, 'parameter') }
|
32
|
+
@args = exp[1..-1]
|
32
33
|
s(exp)
|
33
34
|
end
|
34
35
|
|
@@ -95,6 +96,14 @@ module Reek
|
|
95
96
|
s(exp)
|
96
97
|
end
|
97
98
|
|
99
|
+
def process_if(exp)
|
100
|
+
process(exp[1])
|
101
|
+
process(exp[2])
|
102
|
+
process(exp[3]) if exp[3]
|
103
|
+
ControlCouple.check(exp[1], self, @args)
|
104
|
+
s(exp)
|
105
|
+
end
|
106
|
+
|
98
107
|
def process_ivar(exp)
|
99
108
|
UncommunicativeName.check(exp[1], self, 'field')
|
100
109
|
@depends_on_self = true
|
@@ -127,6 +136,7 @@ module Reek
|
|
127
136
|
def self.count_statements(exp)
|
128
137
|
result = exp.length - 1
|
129
138
|
result -= 1 if Array === exp[1] and exp[1][0] == :args
|
139
|
+
result -= 1 if exp[2] == s(:nil)
|
130
140
|
result
|
131
141
|
end
|
132
142
|
|
@@ -155,7 +165,7 @@ module Reek
|
|
155
165
|
def check_method_properties
|
156
166
|
@lvars.each {|lvar| UncommunicativeName.check(lvar, self, 'local variable') }
|
157
167
|
@depends_on_self = true if is_override?
|
158
|
-
FeatureEnvy.check(@refs, self) unless UtilityFunction.check(@depends_on_self, self)
|
168
|
+
FeatureEnvy.check(@refs, self) unless UtilityFunction.check(@depends_on_self, self, @num_statements)
|
159
169
|
LongMethod.check(@num_statements, self) unless method_name == 'initialize'
|
160
170
|
end
|
161
171
|
end
|
data/lib/reek/options.rb
CHANGED
@@ -10,7 +10,10 @@ module Reek
|
|
10
10
|
|
11
11
|
class Options
|
12
12
|
def self.default_options
|
13
|
-
{
|
13
|
+
{
|
14
|
+
:sort_order => Report::SORT_ORDERS[:context],
|
15
|
+
:expressions => []
|
16
|
+
}
|
14
17
|
end
|
15
18
|
|
16
19
|
@@opts = default_options
|
@@ -22,24 +25,26 @@ module Reek
|
|
22
25
|
def self.parse_args(args)
|
23
26
|
result = default_options
|
24
27
|
parser = OptionParser.new do |opts|
|
25
|
-
opts.banner =
|
28
|
+
opts.banner = <<EOB
|
29
|
+
Usage: #{File.basename($0)} [options] SOURCES
|
30
|
+
|
31
|
+
The SOURCES may be any combination of file paths and Ruby source code.
|
32
|
+
EOB
|
26
33
|
|
27
34
|
opts.separator ""
|
28
|
-
opts.separator "
|
35
|
+
opts.separator "Options:"
|
36
|
+
|
37
|
+
opts.on("-h", "--help", "Show this message") do
|
38
|
+
puts opts
|
39
|
+
exit(0)
|
40
|
+
end
|
29
41
|
|
30
42
|
opts.on('-s', "--sort ORDER", Report::SORT_ORDERS.keys,
|
31
43
|
"Select sort order for report (#{Report::SORT_ORDERS.keys.join(', ')})") do |arg|
|
32
44
|
result[:sort_order] = Report::SORT_ORDERS[arg] unless arg.nil?
|
33
45
|
end
|
34
46
|
|
35
|
-
opts.
|
36
|
-
opts.separator "Common options:"
|
37
|
-
opts.on_tail("-h", "--help", "Show this message") do
|
38
|
-
puts opts
|
39
|
-
exit(0)
|
40
|
-
end
|
41
|
-
|
42
|
-
opts.on_tail("-v", "--version", "Show version") do
|
47
|
+
opts.on("-v", "--version", "Show version") do
|
43
48
|
puts "#{File.basename($0)} #{Reek::VERSION::STRING}"
|
44
49
|
exit(0)
|
45
50
|
end
|
@@ -58,10 +63,7 @@ module Reek
|
|
58
63
|
def self.parse(args)
|
59
64
|
begin
|
60
65
|
@@opts = parse_args(args)
|
61
|
-
|
62
|
-
files = Dir['**/*.rb'] if files.empty?
|
63
|
-
fatal_error('no source files specified') if files.empty?
|
64
|
-
files
|
66
|
+
ARGV
|
65
67
|
rescue OptionParser::ParseError => e
|
66
68
|
fatal_error(e)
|
67
69
|
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Define a task library for running reek.
|
4
|
+
|
5
|
+
require 'rake'
|
6
|
+
require 'rake/tasklib'
|
7
|
+
|
8
|
+
module Reek
|
9
|
+
|
10
|
+
# A Rake task that runs reek on a set of source files.
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
#
|
14
|
+
# Reek::RakeTask.new do |t|
|
15
|
+
# t.fail_on_error = false
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# This will create a task that can be run with:
|
19
|
+
#
|
20
|
+
# rake reek
|
21
|
+
#
|
22
|
+
# If rake is invoked with a "SPEC=filename" command line option,
|
23
|
+
# then the list of spec files will be overridden to include only the
|
24
|
+
# filename specified on the command line. This provides an easy way
|
25
|
+
# to run just one spec.
|
26
|
+
#
|
27
|
+
# If rake is invoked with a "REEK_SORT=order" command line option,
|
28
|
+
# then the given sort order will override the value of the +sort+
|
29
|
+
# attribute.
|
30
|
+
#
|
31
|
+
# Examples:
|
32
|
+
#
|
33
|
+
# rake reek # run specs normally
|
34
|
+
# rake reek SPEC=just_one_file.rb # run just one spec file.
|
35
|
+
# rake reek REEK_SORT=smell # sort warnings by smell
|
36
|
+
#
|
37
|
+
class RakeTask < ::Rake::TaskLib
|
38
|
+
|
39
|
+
# Name of reek task. (default is :reek)
|
40
|
+
attr_accessor :name
|
41
|
+
|
42
|
+
# Array of directories to be added to $LOAD_PATH before running reek.
|
43
|
+
# Defaults to ['<the absolute path to reek's lib directory>']
|
44
|
+
attr_accessor :libs
|
45
|
+
|
46
|
+
# Glob pattern to match source files. (default is 'lib/**/*.rb')
|
47
|
+
# Setting the REEK_SRC environment variable overrides this.
|
48
|
+
attr_accessor :source_files
|
49
|
+
|
50
|
+
# Set the sort order for reported smells (see 'reek --help' for possible values).
|
51
|
+
# Setting the REEK_SORT environment variable overrides this.
|
52
|
+
attr_accessor :sort
|
53
|
+
|
54
|
+
# Array of commandline options to pass to ruby. Defaults to [].
|
55
|
+
attr_accessor :ruby_opts
|
56
|
+
|
57
|
+
# Whether or not to fail Rake when an error occurs (typically when specs fail).
|
58
|
+
# Defaults to true.
|
59
|
+
attr_accessor :fail_on_error
|
60
|
+
|
61
|
+
# Use verbose output. If this is set to true, the task will print
|
62
|
+
# the reek command to stdout. Defaults to false.
|
63
|
+
attr_accessor :verbose
|
64
|
+
|
65
|
+
# Defines a new task, using the name +name+.
|
66
|
+
def initialize(name = :reek)
|
67
|
+
@name = name
|
68
|
+
@libs = [File.expand_path(File.dirname(__FILE__) + '/../../../lib')]
|
69
|
+
@source_files = nil
|
70
|
+
@ruby_opts = []
|
71
|
+
@fail_on_error = true
|
72
|
+
@sort = nil
|
73
|
+
|
74
|
+
yield self if block_given?
|
75
|
+
@source_files ||= 'lib/**/*.rb'
|
76
|
+
define
|
77
|
+
end
|
78
|
+
|
79
|
+
def define # :nodoc:
|
80
|
+
desc "Check for code smells" unless ::Rake.application.last_comment
|
81
|
+
task(name) { run_task }
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
def run_task
|
86
|
+
return if source_file_list.empty?
|
87
|
+
cmd = cmd_words.join(' ')
|
88
|
+
puts cmd if @verbose
|
89
|
+
raise("Smells found!") if !system(cmd) and fail_on_error
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.reek_script
|
93
|
+
File.expand_path(File.dirname(__FILE__) + '/../../bin/reek')
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.ruby_exe
|
97
|
+
File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
|
98
|
+
end
|
99
|
+
|
100
|
+
def cmd_words
|
101
|
+
[self.class.ruby_exe] +
|
102
|
+
ruby_options +
|
103
|
+
[ %Q|"#{self.class.reek_script}"| ] +
|
104
|
+
[sort_option] +
|
105
|
+
source_file_list.collect { |fn| %["#{fn}"] }
|
106
|
+
end
|
107
|
+
|
108
|
+
def ruby_options
|
109
|
+
lib_path = @libs.join(File::PATH_SEPARATOR)
|
110
|
+
@ruby_opts.clone << "-I\"#{lib_path}\""
|
111
|
+
end
|
112
|
+
|
113
|
+
def sort_option
|
114
|
+
env_sort = ENV['REEK_SORT']
|
115
|
+
return "--sort #{env_sort}" if env_sort
|
116
|
+
return "--sort #{@sort}" if @sort
|
117
|
+
''
|
118
|
+
end
|
119
|
+
|
120
|
+
def source_file_list # :nodoc:
|
121
|
+
env_src = ENV['REEK_SRC']
|
122
|
+
return env_src if env_src
|
123
|
+
return FileList[@source_files] if @source_files
|
124
|
+
[]
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
end
|
data/lib/reek/smells.rb
CHANGED
@@ -109,8 +109,13 @@ module Reek
|
|
109
109
|
end
|
110
110
|
|
111
111
|
class UtilityFunction < Smell
|
112
|
+
def initialize(context, num_stmts)
|
113
|
+
super
|
114
|
+
@num_stmts = num_stmts
|
115
|
+
end
|
116
|
+
|
112
117
|
def recognise?(depends_on_self)
|
113
|
-
!depends_on_self
|
118
|
+
@num_stmts > 0 and !depends_on_self
|
114
119
|
end
|
115
120
|
|
116
121
|
def detailed_report
|
@@ -168,4 +173,20 @@ module Reek
|
|
168
173
|
"#{@context} has nested iterators"
|
169
174
|
end
|
170
175
|
end
|
176
|
+
|
177
|
+
class ControlCouple < Smell
|
178
|
+
def initialize(context, args)
|
179
|
+
super
|
180
|
+
@args = args
|
181
|
+
end
|
182
|
+
|
183
|
+
def recognise?(cond)
|
184
|
+
@couple = cond
|
185
|
+
cond[0] == :lvar and @args.include?(@couple[1])
|
186
|
+
end
|
187
|
+
|
188
|
+
def detailed_report
|
189
|
+
"#{@context} is controlled by argument #{Printer.print(@couple)}"
|
190
|
+
end
|
191
|
+
end
|
171
192
|
end
|
data/lib/reek/version.rb
CHANGED
data/spec/integration_spec.rb
CHANGED
@@ -1,15 +1,5 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
2
|
|
3
|
-
require 'reek/version'
|
4
|
-
|
5
|
-
describe 'Reek version number' do
|
6
|
-
it 'should report the correct value' do
|
7
|
-
actual = `ruby -Ilib bin/reek --version`.split
|
8
|
-
actual[0].should == 'reek'
|
9
|
-
actual[1].should == Reek::VERSION::STRING
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
3
|
describe 'Integration test:' do
|
14
4
|
Dir['spec/samples/*.rb'].each do |source|
|
15
5
|
describe source do
|
@@ -0,0 +1,31 @@
|
|
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, "(Control Couple)" do
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
@rpt = Report.new
|
12
|
+
@cchk = MethodChecker.new(@rpt, 'Thing')
|
13
|
+
end
|
14
|
+
|
15
|
+
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/)
|
20
|
+
end
|
21
|
+
|
22
|
+
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
|
25
|
+
end
|
26
|
+
|
27
|
+
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
|
30
|
+
end
|
31
|
+
end
|
@@ -186,8 +186,8 @@ describe FeatureEnvy, 'when the receiver is a dvar' do
|
|
186
186
|
@cchk = MethodChecker.new(@rpt, 'Thing')
|
187
187
|
end
|
188
188
|
|
189
|
-
it "should
|
190
|
-
pending('
|
189
|
+
it "should count each scope separately" do
|
190
|
+
pending('bug')
|
191
191
|
source =<<EOS
|
192
192
|
def bad(fred)
|
193
193
|
@fred.each {|item| item.each }
|
@@ -198,3 +198,25 @@ EOS
|
|
198
198
|
@rpt.should be_empty
|
199
199
|
end
|
200
200
|
end
|
201
|
+
|
202
|
+
describe FeatureEnvy, 'when the receiver is a message chain keyed on self' do
|
203
|
+
before(:each) do
|
204
|
+
@rpt = Report.new
|
205
|
+
@cchk = MethodChecker.new(@rpt, 'Thing')
|
206
|
+
end
|
207
|
+
|
208
|
+
it "count the references correctly" do
|
209
|
+
pending('bug')
|
210
|
+
source =<<EOS
|
211
|
+
class Parcel
|
212
|
+
def addressee
|
213
|
+
"#{self.person.first_name} #{self.person.last_name}"
|
214
|
+
end
|
215
|
+
end
|
216
|
+
EOS
|
217
|
+
@chk.check_source(source)
|
218
|
+
@rpt.length.should == 1
|
219
|
+
FeatureEnvy.should === @rpt[0]
|
220
|
+
@rpt[0].to_s.should match(/self.person/)
|
221
|
+
end
|
222
|
+
end
|
data/spec/reek/options_spec.rb
CHANGED
@@ -7,7 +7,7 @@ include Reek
|
|
7
7
|
describe Options, ' when given no arguments' do
|
8
8
|
it "should retain the default sort order" do
|
9
9
|
default_order = Options[:sort_order]
|
10
|
-
Options.parse []
|
10
|
+
Options.parse ['nosuchfile.rb']
|
11
11
|
Options[:sort_order].should == default_order
|
12
12
|
end
|
13
13
|
end
|
@@ -23,12 +23,12 @@ describe Options, ' when --sort_order is specified' do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
it "should allow sort by smell" do
|
26
|
-
Options.parse %w{-s smell}
|
26
|
+
Options.parse %w{-s smell xx}
|
27
27
|
Options[:sort_order].should == Report::SORT_ORDERS[:smell]
|
28
28
|
end
|
29
29
|
|
30
30
|
it "should allow sort by context" do
|
31
|
-
Options.parse %w{-s context}
|
31
|
+
Options.parse %w{-s context xx}
|
32
32
|
Options[:sort_order].should == Report::SORT_ORDERS[:context]
|
33
33
|
end
|
34
34
|
|
data/spec/reek/report_spec.rb
CHANGED
@@ -41,9 +41,9 @@ end
|
|
41
41
|
describe Report, " as a SortedSet" do
|
42
42
|
it 'should only add a smell once' do
|
43
43
|
rpt = Report.new
|
44
|
-
rpt << UtilityFunction.new(self)
|
44
|
+
rpt << UtilityFunction.new(self, 1)
|
45
45
|
rpt.length.should == 1
|
46
|
-
rpt << UtilityFunction.new(self)
|
46
|
+
rpt << UtilityFunction.new(self, 1)
|
47
47
|
rpt.length.should == 1
|
48
48
|
end
|
49
49
|
end
|
data/spec/reek/smell_spec.rb
CHANGED
@@ -17,17 +17,17 @@ end
|
|
17
17
|
|
18
18
|
describe Smell, ' in comparisons' do
|
19
19
|
it 'should hash equal when the smell is the same' do
|
20
|
-
UtilityFunction.new(self).hash.should == UtilityFunction.new(self).hash
|
20
|
+
UtilityFunction.new(self, 2).hash.should == UtilityFunction.new(self, 2).hash
|
21
21
|
NestedIterators.new(self).hash.should == NestedIterators.new(self).hash
|
22
22
|
end
|
23
23
|
|
24
24
|
it 'should compare equal when the smell is the same' do
|
25
|
-
UtilityFunction.new(self).should == UtilityFunction.new(self)
|
25
|
+
UtilityFunction.new(self, 2).should == UtilityFunction.new(self, 2)
|
26
26
|
NestedIterators.new(self).should == NestedIterators.new(self)
|
27
27
|
end
|
28
28
|
|
29
29
|
it 'should compare equal when using <=>' do
|
30
|
-
(UtilityFunction.new(self) <=> UtilityFunction.new(self)).should == 0
|
30
|
+
(UtilityFunction.new(self, 2) <=> UtilityFunction.new(self, 2)).should == 0
|
31
31
|
(NestedIterators.new(self) <=> NestedIterators.new(self)).should == 0
|
32
32
|
end
|
33
33
|
end
|
@@ -12,6 +12,11 @@ describe MethodChecker, "(Utility Function)" do
|
|
12
12
|
@cchk = MethodChecker.new(@rpt, 'Thing')
|
13
13
|
end
|
14
14
|
|
15
|
+
it 'should not report empty method' do
|
16
|
+
@cchk.check_source('def simple(arga) end')
|
17
|
+
@rpt.should be_empty
|
18
|
+
end
|
19
|
+
|
15
20
|
it 'should not report instance variable reference' do
|
16
21
|
@cchk.check_source('def simple(arga) @yellow end')
|
17
22
|
@rpt.should be_empty
|
@@ -29,7 +34,7 @@ describe MethodChecker, "(Utility Function)" do
|
|
29
34
|
@cchk.check_object(Fred)
|
30
35
|
@rpt.should be_empty
|
31
36
|
end
|
32
|
-
|
37
|
+
|
33
38
|
it 'should not report references to self' do
|
34
39
|
@cchk.check_source('def into; self; end')
|
35
40
|
@rpt.should be_empty
|
@@ -54,7 +59,7 @@ describe MethodChecker, "(Utility Function)" do
|
|
54
59
|
it 'should report simple parameter call' do
|
55
60
|
@cchk.check_source('def simple(arga) arga.to_s end')
|
56
61
|
@rpt.length.should == 1
|
57
|
-
@rpt[0].should == UtilityFunction.new(@cchk)
|
62
|
+
@rpt[0].should == UtilityFunction.new(@cchk, 1)
|
58
63
|
end
|
59
64
|
|
60
65
|
it 'should report message chain' do
|
@@ -72,4 +77,19 @@ describe MethodChecker, "(Utility Function)" do
|
|
72
77
|
ClassChecker.new(@rpt).check_object(Son)
|
73
78
|
@rpt.should be_empty
|
74
79
|
end
|
80
|
+
|
81
|
+
it 'should not report class method' do
|
82
|
+
pending('bug')
|
83
|
+
source = <<EOS
|
84
|
+
class Cache
|
85
|
+
class << self
|
86
|
+
def create_unless_known(attributes)
|
87
|
+
Cache.create(attributes) unless Cache.known?
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
EOS
|
92
|
+
@cchk.check_source(source)
|
93
|
+
@rpt.should be_empty
|
94
|
+
end
|
75
95
|
end
|
data/spec/reek_source_spec.rb
CHANGED
@@ -4,7 +4,8 @@ describe 'Reek source code:' do
|
|
4
4
|
Dir['lib/**/*.rb'].each do |source|
|
5
5
|
describe source do
|
6
6
|
it 'should report no smells' do
|
7
|
-
`ruby -Ilib bin/reek #{source}`.should ==
|
7
|
+
`ruby -Ilib bin/reek #{source}`.should == ''
|
8
|
+
$?.exitstatus.should == 0
|
8
9
|
end
|
9
10
|
end
|
10
11
|
end
|
data/spec/samples/inline.reek
CHANGED
@@ -11,7 +11,9 @@
|
|
11
11
|
[Nested Iterators] C#module_name has nested iterators
|
12
12
|
[Feature Envy] C#module_name uses md5 more than self
|
13
13
|
[Long Method] C#parse_signature has approx 15 statements
|
14
|
+
[Control Couple] C#parse_signature is controlled by argument raw
|
14
15
|
[Utility Function] C#strip_comments doesn't depend on instance state
|
15
16
|
[Large Class] Dir has 31 methods
|
16
17
|
[Large Class] Module has 34 methods
|
17
18
|
[Long Method] Module#inline has approx 12 statements
|
19
|
+
[Control Couple] Module#inline is controlled by argument options
|
data/spec/samples/optparse.reek
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
[Large Class] AmbiguousArgument has 52 methods
|
2
|
-
[Long Method] AmbiguousArgument#options has approx 6 statements
|
3
2
|
[Long Parameter List] CompletingHash#complete has 4 parameters
|
4
3
|
[Nested Iterators] CompletingHash#complete has nested iterators
|
5
4
|
[Long Method] CompletingHash#getopts has approx 17 statements
|
@@ -13,11 +12,13 @@
|
|
13
12
|
[Nested Iterators] CompletingHash#match has nested iterators
|
14
13
|
[Feature Envy] CompletingHash#order uses argv more than self
|
15
14
|
[Feature Envy] CompletingHash#parse uses argv more than self
|
16
|
-
[Long Method] CompletingHash#parse_in_order has approx
|
15
|
+
[Long Method] CompletingHash#parse_in_order has approx 30 statements
|
17
16
|
[Nested Iterators] CompletingHash#parse_in_order has nested iterators
|
17
|
+
[Control Couple] CompletingHash#parse_in_order is controlled by argument setter
|
18
18
|
[Feature Envy] CompletingHash#permute uses argv more than self
|
19
19
|
[Long Parameter List] CompletingHash#summarize has 4 parameters
|
20
20
|
[Uncommunicative Name] CompletingHash#ver uses the local variable name 'v'
|
21
|
+
[Control Couple] List#accept is controlled by argument pat
|
21
22
|
[Feature Envy] List#accept uses pat more than self
|
22
23
|
[Uncommunicative Name] List#accept uses the parameter name 't'
|
23
24
|
[Feature Envy] List#add_banner uses opt more than self
|
@@ -27,13 +28,19 @@
|
|
27
28
|
[Feature Envy] List#summarize uses opt more than self
|
28
29
|
[Long Parameter List] List#update has 5 parameters
|
29
30
|
[Long Method] List#update has approx 6 statements
|
31
|
+
[Control Couple] List#update is controlled by argument lopts
|
32
|
+
[Control Couple] List#update is controlled by argument sopts
|
30
33
|
[Uncommunicative Name] List#update uses the local variable name 'o'
|
34
|
+
[Control Couple] NoArgument#parse is controlled by argument arg
|
31
35
|
[Long Method] OptionParser#complete has approx 23 statements
|
32
36
|
[Feature Envy] OptionParser#complete uses candidates more than self
|
33
37
|
[Uncommunicative Name] OptionParser#complete uses the local variable name 'k'
|
34
38
|
[Uncommunicative Name] OptionParser#complete uses the local variable name 'v'
|
35
39
|
[Utility Function] OptionParser#convert doesn't depend on instance state
|
40
|
+
[Control Couple] OptionalArgument#parse is controlled by argument arg
|
41
|
+
[Control Couple] ParseError#set_option is controlled by argument eq
|
36
42
|
[Long Method] PlacedArgument#parse has approx 6 statements
|
43
|
+
[Control Couple] RequiredArgument#parse is controlled by argument arg
|
37
44
|
[Uncommunicative Name] Switch#add_banner uses the local variable name 's'
|
38
45
|
[Long Parameter List] Switch#initialize has 7 parameters
|
39
46
|
[Long Method] Switch#parse_arg has approx 12 statements
|
data/spec/samples/redcloth.reek
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
[Long Method] RedCloth#block_markdown_bq has approx 6 statements
|
2
|
-
[Utility Function] RedCloth#block_markdown_lists doesn't depend on instance state
|
3
2
|
[Utility Function] RedCloth#block_markdown_rule doesn't depend on instance state
|
4
3
|
[Long Method] RedCloth#block_textile_lists has approx 21 statements
|
5
4
|
[Nested Iterators] RedCloth#block_textile_lists has nested iterators
|
@@ -8,6 +7,8 @@
|
|
8
7
|
[Nested Iterators] RedCloth#block_textile_table has nested iterators
|
9
8
|
[Long Method] RedCloth#blocks has approx 17 statements
|
10
9
|
[Nested Iterators] RedCloth#blocks has nested iterators
|
10
|
+
[Control Couple] RedCloth#blocks is controlled by argument deep_code
|
11
|
+
[Control Couple] RedCloth#check_refs is controlled by argument text
|
11
12
|
[Utility Function] RedCloth#clean_html doesn't depend on instance state
|
12
13
|
[Long Method] RedCloth#clean_html has approx 14 statements
|
13
14
|
[Nested Iterators] RedCloth#clean_html has nested iterators
|
@@ -29,13 +30,18 @@
|
|
29
30
|
[Utility Function] RedCloth#lT doesn't depend on instance state
|
30
31
|
[Utility Function] RedCloth#no_textile doesn't depend on instance state
|
31
32
|
[Long Method] RedCloth#pba has approx 22 statements
|
33
|
+
[Control Couple] RedCloth#pba is controlled by argument text_in
|
32
34
|
[Feature Envy] RedCloth#pba uses style and text more than self
|
33
35
|
[Long Method] RedCloth#rip_offtags has approx 22 statements
|
34
36
|
[Feature Envy] RedCloth#rip_offtags uses codepre more than self
|
35
37
|
[Feature Envy] RedCloth#shelve uses @shelf more than self
|
36
38
|
[Feature Envy] RedCloth#smooth_offtags uses @pre_list more than self
|
37
39
|
[Long Parameter List] RedCloth#textile_bq has 4 parameters
|
40
|
+
[Control Couple] RedCloth#textile_bq is controlled by argument atts
|
41
|
+
[Control Couple] RedCloth#textile_bq is controlled by argument cite
|
38
42
|
[Long Parameter List] RedCloth#textile_fn_ has 5 parameters
|
43
|
+
[Control Couple] RedCloth#textile_fn_ is controlled by argument atts
|
39
44
|
[Long Parameter List] RedCloth#textile_p has 4 parameters
|
45
|
+
[Control Couple] RedCloth#textile_p is controlled by argument atts
|
40
46
|
[Long Method] RedCloth#to_html has approx 24 statements
|
41
47
|
[Utility Function] RedCloth#v_align doesn't depend on instance state
|
data/spec/script_spec.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
require 'reek/version'
|
4
|
+
|
5
|
+
describe 'version number' do
|
6
|
+
it 'should report the correct value' do
|
7
|
+
actual = `ruby -Ilib bin/reek --version`.split
|
8
|
+
$?.exitstatus.should == 0
|
9
|
+
actual[0].should == 'reek'
|
10
|
+
actual[1].should == Reek::VERSION::STRING
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'exit status', 'when reek is used incorrectly' do
|
15
|
+
it 'should return non-zero status on bad option' do
|
16
|
+
`ruby -Ilib bin/reek --no-such-option`
|
17
|
+
$?.exitstatus.should == 1
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should not complain about missing file' do
|
21
|
+
`ruby -Ilib bin/reek nosuchfile.rb 2>/dev/null`
|
22
|
+
$?.exitstatus.should == 0
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should return non-zero status on missing argument' do
|
26
|
+
`ruby -Ilib bin/reek -s 2>/dev/null`
|
27
|
+
$?.exitstatus.should == 1
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should not complain when no source given' do
|
31
|
+
`ruby -Ilib bin/reek 2>/dev/null`
|
32
|
+
$?.exitstatus.should == 0
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'exit status', 'when reek is used correctly' do
|
37
|
+
it 'should return non-zero status when smells are reported' do
|
38
|
+
`ruby -Ilib bin/reek "def x() 3; end"`
|
39
|
+
$?.exitstatus.should == 2
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should return zero status with no smells' do
|
43
|
+
`ruby -Ilib bin/reek "def simple() @fred = 3 end"`
|
44
|
+
$?.exitstatus.should == 0
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe 'report format', 'with no sources' do
|
49
|
+
it 'should output nothing' do
|
50
|
+
`ruby -Ilib bin/reek`.should be_empty
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe 'report format', 'with one source' do
|
55
|
+
it 'should output nothing with empty source' do
|
56
|
+
`ruby -Ilib bin/reek ""`.should be_empty
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should output nothing when no smells' do
|
60
|
+
`ruby -Ilib bin/reek "def simple() @fred = 3; end"`.should be_empty
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should not adorn the list of warnings' do
|
64
|
+
report = `ruby -Ilib bin/reek "def y() @x = 3; end"`
|
65
|
+
report.split(/\n/).length.should == 1
|
66
|
+
report.should_not match(/\n\n/)
|
67
|
+
end
|
68
|
+
end
|
data/tasks/reek.rake
ADDED
data/website/index.html
CHANGED
@@ -33,7 +33,7 @@
|
|
33
33
|
<h1>reek</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.
|
36
|
+
<a href="http://rubyforge.org/projects/reek" class="numbers">0.3.0</a>
|
37
37
|
</div>
|
38
38
|
<p>Reek is a tool that examines Ruby classes, modules and methods and reports any code smells it finds.</p>
|
39
39
|
<h2>Installing</h2>
|
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.
|
4
|
+
version: 0.3.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-
|
12
|
+
date: 2008-11-02 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -18,9 +18,19 @@ dependencies:
|
|
18
18
|
version_requirement:
|
19
19
|
version_requirements: !ruby/object:Gem::Requirement
|
20
20
|
requirements:
|
21
|
-
- -
|
21
|
+
- - ~>
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "3.0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: sexp_processor
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
22
32
|
- !ruby/object:Gem::Version
|
23
|
-
version:
|
33
|
+
version: "3.0"
|
24
34
|
version:
|
25
35
|
- !ruby/object:Gem::Dependency
|
26
36
|
name: hoe
|
@@ -30,9 +40,9 @@ dependencies:
|
|
30
40
|
requirements:
|
31
41
|
- - ">="
|
32
42
|
- !ruby/object:Gem::Version
|
33
|
-
version: 1.
|
43
|
+
version: 1.8.2
|
34
44
|
version:
|
35
|
-
description: detects smells in
|
45
|
+
description: detects smells in Ruby code
|
36
46
|
email:
|
37
47
|
- kevin@rutherford-software.com
|
38
48
|
executables:
|
@@ -56,12 +66,14 @@ files:
|
|
56
66
|
- lib/reek/object_refs.rb
|
57
67
|
- lib/reek/options.rb
|
58
68
|
- lib/reek/printer.rb
|
69
|
+
- lib/reek/rake_task.rb
|
59
70
|
- lib/reek/report.rb
|
60
71
|
- lib/reek/smells.rb
|
61
72
|
- lib/reek/version.rb
|
62
73
|
- setup.rb
|
63
74
|
- spec/integration_spec.rb
|
64
75
|
- spec/reek/class_checker_spec.rb
|
76
|
+
- spec/reek/control_couple_spec.rb
|
65
77
|
- spec/reek/feature_envy_spec.rb
|
66
78
|
- spec/reek/large_class_spec.rb
|
67
79
|
- spec/reek/long_method_spec.rb
|
@@ -87,8 +99,10 @@ files:
|
|
87
99
|
- spec/samples/optparse/version.rb
|
88
100
|
- spec/samples/redcloth.rb
|
89
101
|
- spec/samples/redcloth.reek
|
102
|
+
- spec/script_spec.rb
|
90
103
|
- spec/spec.opts
|
91
104
|
- spec/spec_helper.rb
|
105
|
+
- tasks/reek.rake
|
92
106
|
- tasks/rspec.rake
|
93
107
|
- tasks/samples.rake
|
94
108
|
- website/index.html
|
@@ -122,6 +136,6 @@ rubyforge_project: reek
|
|
122
136
|
rubygems_version: 1.2.0
|
123
137
|
signing_key:
|
124
138
|
specification_version: 2
|
125
|
-
summary: detects smells in
|
139
|
+
summary: detects smells in Ruby code
|
126
140
|
test_files: []
|
127
141
|
|