reek 0.3.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +20 -0
- data/README.txt +4 -80
- data/Rakefile +15 -4
- data/bin/reek +10 -16
- data/config/defaults.reek +53 -0
- data/lib/reek.rb +1 -21
- data/lib/reek/block_context.rb +37 -0
- data/lib/reek/class_context.rb +73 -0
- data/lib/reek/code_context.rb +47 -0
- data/lib/reek/code_parser.rb +204 -0
- data/lib/reek/exceptions.reek +13 -0
- data/lib/reek/if_context.rb +25 -0
- data/lib/reek/method_context.rb +85 -0
- data/lib/reek/module_context.rb +34 -0
- data/lib/reek/name.rb +42 -0
- data/lib/reek/object_refs.rb +3 -6
- data/lib/reek/options.rb +60 -40
- data/lib/reek/rake_task.rb +20 -29
- data/lib/reek/report.rb +16 -27
- data/lib/reek/sexp_formatter.rb +52 -0
- data/lib/reek/singleton_method_context.rb +27 -0
- data/lib/reek/smell_warning.rb +49 -0
- data/lib/reek/smells/control_couple.rb +21 -13
- data/lib/reek/smells/duplication.rb +23 -27
- data/lib/reek/smells/feature_envy.rb +18 -25
- data/lib/reek/smells/large_class.rb +32 -17
- data/lib/reek/smells/long_method.rb +24 -16
- data/lib/reek/smells/long_parameter_list.rb +25 -18
- data/lib/reek/smells/long_yield_list.rb +7 -9
- data/lib/reek/smells/nested_iterators.rb +13 -9
- data/lib/reek/smells/smell_detector.rb +66 -0
- data/lib/reek/smells/smells.rb +71 -10
- data/lib/reek/smells/uncommunicative_name.rb +49 -41
- data/lib/reek/smells/utility_function.rb +18 -18
- data/lib/reek/source.rb +116 -0
- data/lib/reek/spec.rb +146 -0
- data/lib/reek/stop_context.rb +62 -0
- data/lib/reek/yield_call_context.rb +14 -0
- data/reek.gemspec +42 -0
- data/spec/integration/reek_source_spec.rb +20 -0
- data/spec/{script_spec.rb → integration/script_spec.rb} +11 -24
- data/spec/reek/class_context_spec.rb +198 -0
- data/spec/reek/code_context_spec.rb +92 -0
- data/spec/reek/code_parser_spec.rb +44 -0
- data/spec/reek/config_spec.rb +42 -0
- data/spec/reek/if_context_spec.rb +17 -0
- data/spec/reek/method_context_spec.rb +52 -0
- data/spec/reek/module_context_spec.rb +38 -0
- data/spec/reek/options_spec.rb +2 -28
- data/spec/reek/report_spec.rb +6 -40
- data/spec/reek/sexp_formatter_spec.rb +31 -0
- data/spec/reek/singleton_method_context_spec.rb +17 -0
- data/spec/reek/smells/control_couple_spec.rb +10 -18
- data/spec/reek/smells/duplication_spec.rb +53 -32
- data/spec/reek/smells/feature_envy_spec.rb +87 -49
- data/spec/reek/smells/large_class_spec.rb +45 -4
- data/spec/reek/smells/long_method_spec.rb +25 -41
- data/spec/reek/smells/long_parameter_list_spec.rb +30 -76
- data/spec/reek/smells/nested_iterators_spec.rb +19 -29
- data/spec/reek/smells/smell_spec.rb +9 -18
- data/spec/reek/smells/uncommunicative_name_spec.rb +88 -53
- data/spec/reek/smells/utility_function_spec.rb +45 -44
- data/spec/samples/inline_spec.rb +40 -0
- data/spec/samples/optparse_spec.rb +100 -0
- data/spec/samples/redcloth_spec.rb +93 -0
- data/spec/spec_helper.rb +3 -1
- data/tasks/reek.rake +1 -10
- data/tasks/rspec.rake +16 -35
- metadata +43 -46
- data/lib/reek/checker.rb +0 -66
- data/lib/reek/class_checker.rb +0 -25
- data/lib/reek/file_checker.rb +0 -20
- data/lib/reek/method_checker.rb +0 -198
- data/lib/reek/printer.rb +0 -154
- data/lib/reek/smells/smell.rb +0 -56
- data/lib/reek/version.rb +0 -9
- data/setup.rb +0 -1585
- data/spec/integration_spec.rb +0 -30
- data/spec/reek/class_checker_spec.rb +0 -48
- data/spec/reek/method_checker_spec.rb +0 -67
- data/spec/reek/printer_spec.rb +0 -30
- data/spec/reek_source_spec.rb +0 -12
- data/spec/samples/inline.reek +0 -27
- data/spec/samples/optparse.reek +0 -79
- data/spec/samples/optparse/date.rb +0 -17
- data/spec/samples/optparse/shellwords.rb +0 -6
- data/spec/samples/optparse/time.rb +0 -10
- data/spec/samples/optparse/uri.rb +0 -6
- data/spec/samples/optparse/version.rb +0 -70
- data/spec/samples/redcloth.reek +0 -65
- data/tasks/samples.rake +0 -17
- data/website/index.html +0 -71
- data/website/index.txt +0 -40
- data/website/javascripts/rounded_corners_lite.inc.js +0 -285
- data/website/stylesheets/screen.css +0 -138
- data/website/template.rhtml +0 -48
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'reek/code_context'
|
2
|
+
|
3
|
+
module Reek
|
4
|
+
class IfContext < CodeContext
|
5
|
+
attr_reader :if_expr
|
6
|
+
|
7
|
+
def initialize(outer, exp)
|
8
|
+
@outer = outer
|
9
|
+
@exp = exp
|
10
|
+
@if_expr = exp[1]
|
11
|
+
end
|
12
|
+
|
13
|
+
def tests_a_parameter?
|
14
|
+
@if_expr[0] == :lvar and has_parameter(@if_expr[1])
|
15
|
+
end
|
16
|
+
|
17
|
+
def outer_name
|
18
|
+
@outer.outer_name
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
@outer.to_s
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'reek/name'
|
2
|
+
require 'reek/code_context'
|
3
|
+
require 'reek/object_refs'
|
4
|
+
|
5
|
+
module Reek
|
6
|
+
class MethodContext < CodeContext
|
7
|
+
attr_reader :parameters
|
8
|
+
attr_reader :calls
|
9
|
+
attr_reader :refs
|
10
|
+
attr_reader :num_statements
|
11
|
+
|
12
|
+
def initialize(outer, exp, record = true)
|
13
|
+
super(outer, exp)
|
14
|
+
@parameters = []
|
15
|
+
@local_variables = []
|
16
|
+
@name = Name.new(exp[1])
|
17
|
+
@num_statements = 0
|
18
|
+
@calls = Hash.new(0)
|
19
|
+
@depends_on_self = false
|
20
|
+
@refs = ObjectRefs.new
|
21
|
+
@outer.record_method(@name) # TODO: should be children of outer?
|
22
|
+
end
|
23
|
+
|
24
|
+
def count_statements(num)
|
25
|
+
@num_statements += num
|
26
|
+
end
|
27
|
+
|
28
|
+
def depends_on_instance?
|
29
|
+
@depends_on_self || is_overriding_method?(@name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def has_parameter(sym)
|
33
|
+
@parameters.include?(sym.to_s)
|
34
|
+
end
|
35
|
+
|
36
|
+
def record_call_to(exp)
|
37
|
+
@calls[exp] += 1
|
38
|
+
receiver, meth = exp[1..2]
|
39
|
+
if receiver.nil?
|
40
|
+
record_depends_on_self
|
41
|
+
else
|
42
|
+
case receiver[0]
|
43
|
+
when :lvar
|
44
|
+
@refs.record_ref(receiver) unless meth == :new
|
45
|
+
when :ivar
|
46
|
+
record_depends_on_self
|
47
|
+
@refs.record_reference_to_self
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def record_depends_on_self
|
53
|
+
@depends_on_self = true
|
54
|
+
end
|
55
|
+
|
56
|
+
def record_local_variable(sym)
|
57
|
+
@local_variables << Name.new(sym)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.is_block_arg?(param)
|
61
|
+
Array === param and param[0] == :block
|
62
|
+
end
|
63
|
+
|
64
|
+
def record_parameter(param)
|
65
|
+
@parameters << Name.new(param) unless MethodContext.is_block_arg?(param)
|
66
|
+
end
|
67
|
+
|
68
|
+
def outer_name
|
69
|
+
"#{@outer.outer_name}#{@name}/"
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_s
|
73
|
+
"#{@outer.outer_name}#{@name}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def envious_receivers
|
77
|
+
return [] if @refs.self_is_max?
|
78
|
+
@refs.max_keys
|
79
|
+
end
|
80
|
+
|
81
|
+
def variable_names
|
82
|
+
@parameters + @local_variables
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'reek/code_context'
|
2
|
+
|
3
|
+
module Reek
|
4
|
+
class ModuleContext < CodeContext
|
5
|
+
|
6
|
+
def ModuleContext.create(outer, exp)
|
7
|
+
res = Name.resolve(exp[1], outer)
|
8
|
+
ModuleContext.new(res[0], res[1])
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(outer, name)
|
12
|
+
super(outer, nil)
|
13
|
+
@name = name
|
14
|
+
end
|
15
|
+
|
16
|
+
def myself
|
17
|
+
@myself ||= @outer.find_module(@name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_module(modname)
|
21
|
+
return nil unless myself
|
22
|
+
sym = modname.to_s
|
23
|
+
myself.const_defined?(sym) ? myself.const_get(sym) : nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def outer_name
|
27
|
+
"#{@outer.outer_name}#{@name}::"
|
28
|
+
end
|
29
|
+
|
30
|
+
def variable_names
|
31
|
+
[]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/reek/name.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module Reek
|
2
|
+
class Name
|
3
|
+
include Comparable
|
4
|
+
|
5
|
+
def self.resolve(exp, context)
|
6
|
+
return [context, new(exp)] unless Array === exp
|
7
|
+
name = exp[1]
|
8
|
+
case exp[0]
|
9
|
+
when :colon2
|
10
|
+
return [resolve(name, context)[0], new(exp[2])]
|
11
|
+
when :const
|
12
|
+
return [ModuleContext.create(context, exp), new(name)]
|
13
|
+
when :colon3
|
14
|
+
return [StopContext.new, new(name)]
|
15
|
+
else
|
16
|
+
return [context, new(name)]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(sym)
|
21
|
+
@name = sym.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def hash # :nodoc:
|
25
|
+
@name.hash
|
26
|
+
end
|
27
|
+
|
28
|
+
def <=>(other) # :nodoc:
|
29
|
+
@name <=> other.to_s
|
30
|
+
end
|
31
|
+
|
32
|
+
alias eql? <=>
|
33
|
+
|
34
|
+
def effective_name
|
35
|
+
@name.gsub(/^@*/, '')
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_s
|
39
|
+
@name
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/reek/object_refs.rb
CHANGED
@@ -1,12 +1,9 @@
|
|
1
|
-
$:.unshift File.dirname(__FILE__)
|
2
|
-
|
3
1
|
require 'rubygems'
|
4
|
-
require '
|
5
|
-
require 'reek/printer'
|
2
|
+
require 'sexp_processor'
|
6
3
|
|
7
4
|
module Reek
|
8
5
|
|
9
|
-
class ObjectRefs
|
6
|
+
class ObjectRefs # :nodoc:
|
10
7
|
def initialize
|
11
8
|
@refs = Hash.new(0)
|
12
9
|
record_reference_to_self
|
@@ -41,7 +38,7 @@ module Reek
|
|
41
38
|
# no way to turn off that report; which would therefore make the tests fail
|
42
39
|
def max_keys
|
43
40
|
max = max_refs
|
44
|
-
@refs.reject {|
|
41
|
+
@refs.reject {|key,val| val != max}.keys
|
45
42
|
end
|
46
43
|
|
47
44
|
def self_is_max?
|
data/lib/reek/options.rb
CHANGED
@@ -1,18 +1,16 @@
|
|
1
|
-
$:.unshift File.dirname(__FILE__)
|
2
|
-
|
3
|
-
require 'reek/report'
|
4
|
-
require 'reek/version'
|
5
1
|
require 'optparse'
|
6
|
-
|
7
|
-
include Reek
|
2
|
+
require 'reek/source'
|
8
3
|
|
9
4
|
module Reek
|
10
|
-
|
5
|
+
|
11
6
|
class Options
|
7
|
+
|
8
|
+
CTX_SORT = '%c %w (%s)'
|
9
|
+
SMELL_SORT = '[%s] %c %w'
|
10
|
+
|
12
11
|
def self.default_options
|
13
12
|
{
|
14
|
-
:
|
15
|
-
:expressions => []
|
13
|
+
:format => CTX_SORT
|
16
14
|
}
|
17
15
|
end
|
18
16
|
|
@@ -24,49 +22,71 @@ module Reek
|
|
24
22
|
|
25
23
|
def self.parse_args(args)
|
26
24
|
result = default_options
|
27
|
-
parser = OptionParser.new
|
28
|
-
|
29
|
-
|
25
|
+
parser = OptionParser.new { |opts| set_options(opts, result) }
|
26
|
+
parser.parse!(args)
|
27
|
+
result
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.set_options(opts, config)
|
31
|
+
opts.banner = <<EOB
|
32
|
+
Usage: #{opts.program_name} [options] files...
|
30
33
|
|
31
|
-
|
34
|
+
If no files are given, Reek reads source code from standard input.
|
35
|
+
See http://wiki.github.com/kevinrutherford/reek for detailed help.
|
32
36
|
EOB
|
37
|
+
|
38
|
+
opts.separator "\nOptions:"
|
39
|
+
set_help_option(opts)
|
40
|
+
set_sort_option(config, opts)
|
41
|
+
set_version_option(opts)
|
42
|
+
end
|
33
43
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
44
|
+
def self.parse(args)
|
45
|
+
begin
|
46
|
+
@@opts = parse_args(args)
|
47
|
+
if ARGV.length > 0
|
48
|
+
return Source.from_pathlist(ARGV)
|
49
|
+
else
|
50
|
+
return Source.from_io($stdin, 'stdin')
|
40
51
|
end
|
52
|
+
rescue OptionParser::ParseError, SystemCallError => err
|
53
|
+
fatal_error(err)
|
54
|
+
end
|
55
|
+
end
|
41
56
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
57
|
+
private
|
58
|
+
|
59
|
+
def self.set_version_option(opts)
|
60
|
+
opts.on("-v", "--version", "Show version") do
|
61
|
+
puts "#{opts.program_name} #{Reek::VERSION}"
|
62
|
+
exit(0)
|
63
|
+
end
|
64
|
+
end
|
46
65
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
66
|
+
def self.set_help_option(opts)
|
67
|
+
opts.on("-h", "--help", "Show this message") do
|
68
|
+
puts opts
|
69
|
+
exit(0)
|
51
70
|
end
|
71
|
+
end
|
52
72
|
|
53
|
-
|
54
|
-
|
73
|
+
def self.set_sort_option(config, opts)
|
74
|
+
opts.on('-f', "--format FORMAT", 'Specify the format of smell warnings') do |arg|
|
75
|
+
config[:format] = arg unless arg.nil?
|
76
|
+
end
|
77
|
+
opts.on('-c', '--context-first', "Sort by context; sets the format string to \"#{CTX_SORT}\"") do
|
78
|
+
config[:format] = CTX_SORT
|
79
|
+
end
|
80
|
+
opts.on('-s', '--smell-first', "Sort by smell; sets the format string to \"#{SMELL_SORT}\"") do
|
81
|
+
config[:format] = SMELL_SORT
|
82
|
+
end
|
55
83
|
end
|
56
84
|
|
57
|
-
def self.fatal_error(
|
58
|
-
puts "Error: #{
|
85
|
+
def self.fatal_error(err) # :nodoc:
|
86
|
+
puts "Error: #{err}"
|
59
87
|
puts "Use '-h' for help."
|
60
88
|
exit(1)
|
61
89
|
end
|
62
|
-
|
63
|
-
def self.parse(args)
|
64
|
-
begin
|
65
|
-
@@opts = parse_args(args)
|
66
|
-
ARGV
|
67
|
-
rescue OptionParser::ParseError => e
|
68
|
-
fatal_error(e)
|
69
|
-
end
|
70
|
-
end
|
90
|
+
|
71
91
|
end
|
72
92
|
end
|
data/lib/reek/rake_task.rb
CHANGED
@@ -19,37 +19,31 @@ module Reek
|
|
19
19
|
#
|
20
20
|
# rake reek
|
21
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
22
|
# Examples:
|
32
23
|
#
|
33
|
-
# rake reek
|
34
|
-
# rake reek
|
35
|
-
# rake reek
|
24
|
+
# rake reek # checks lib/**/*.rb
|
25
|
+
# rake reek REEK_SRC=just_one_file.rb # checks a single source file
|
26
|
+
# rake reek REEK_OPTS=-s # sorts the report by smell
|
36
27
|
#
|
37
28
|
class RakeTask < ::Rake::TaskLib
|
38
29
|
|
39
|
-
# Name of reek task.
|
30
|
+
# Name of reek task.
|
31
|
+
# Defaults to :reek.
|
40
32
|
attr_accessor :name
|
41
33
|
|
42
34
|
# Array of directories to be added to $LOAD_PATH before running reek.
|
43
35
|
# Defaults to ['<the absolute path to reek's lib directory>']
|
44
36
|
attr_accessor :libs
|
45
37
|
|
46
|
-
# Glob pattern to match source files.
|
38
|
+
# Glob pattern to match source files.
|
47
39
|
# Setting the REEK_SRC environment variable overrides this.
|
40
|
+
# Defaults to 'lib/**/*.rb'.
|
48
41
|
attr_accessor :source_files
|
49
42
|
|
50
|
-
#
|
51
|
-
# Setting the
|
52
|
-
|
43
|
+
# String containing commandline options to be passed to Reek.
|
44
|
+
# Setting the REEK_OPTS environment variable overrides this value.
|
45
|
+
# Defaults to ''.
|
46
|
+
attr_accessor :reek_opts
|
53
47
|
|
54
48
|
# Array of commandline options to pass to ruby. Defaults to [].
|
55
49
|
attr_accessor :ruby_opts
|
@@ -65,9 +59,10 @@ module Reek
|
|
65
59
|
# Defines a new task, using the name +name+.
|
66
60
|
def initialize(name = :reek)
|
67
61
|
@name = name
|
68
|
-
@libs = [File.expand_path(File.dirname(__FILE__) + '
|
62
|
+
@libs = [File.expand_path(File.dirname(__FILE__) + '/../../lib')]
|
69
63
|
@source_files = nil
|
70
64
|
@ruby_opts = []
|
65
|
+
@reek_opts = ''
|
71
66
|
@fail_on_error = true
|
72
67
|
@sort = nil
|
73
68
|
|
@@ -79,7 +74,7 @@ module Reek
|
|
79
74
|
private
|
80
75
|
|
81
76
|
def define # :nodoc:
|
82
|
-
desc
|
77
|
+
desc 'Check for code smells' unless ::Rake.application.last_comment
|
83
78
|
task(name) { run_task }
|
84
79
|
self
|
85
80
|
end
|
@@ -88,9 +83,9 @@ private
|
|
88
83
|
return if source_file_list.empty?
|
89
84
|
cmd = cmd_words.join(' ')
|
90
85
|
puts cmd if @verbose
|
91
|
-
raise(
|
86
|
+
raise('Smells found!') if !system(cmd) and fail_on_error
|
92
87
|
end
|
93
|
-
|
88
|
+
|
94
89
|
def self.reek_script
|
95
90
|
File.expand_path(File.dirname(__FILE__) + '/../../bin/reek')
|
96
91
|
end
|
@@ -113,17 +108,13 @@ private
|
|
113
108
|
end
|
114
109
|
|
115
110
|
def sort_option
|
116
|
-
|
117
|
-
return "--sort #{env_sort}" if env_sort
|
118
|
-
return "--sort #{@sort}" if @sort
|
119
|
-
''
|
111
|
+
ENV['REEK_OPTS'] || @reek_opts
|
120
112
|
end
|
121
113
|
|
122
114
|
def source_file_list # :nodoc:
|
123
|
-
|
124
|
-
return
|
125
|
-
return FileList[
|
126
|
-
[]
|
115
|
+
files = ENV['REEK_SRC'] || @source_files
|
116
|
+
return [] unless files
|
117
|
+
return FileList[files]
|
127
118
|
end
|
128
119
|
|
129
120
|
end
|