reek 0.2.3 → 0.3.0
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 +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
|
|