reek 1.2.0 → 1.2.1
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 +7 -0
- data/README.rdoc +1 -0
- data/config/defaults.reek +5 -2
- data/features/samples.feature +4 -1
- data/lib/reek.rb +1 -1
- data/lib/reek/adapters/application.rb +4 -7
- data/lib/reek/{command_line.rb → adapters/command_line.rb} +13 -20
- data/lib/reek/adapters/object_source.rb +3 -0
- data/lib/reek/adapters/report.rb +15 -14
- data/lib/reek/adapters/spec.rb +6 -1
- data/lib/reek/class_context.rb +6 -1
- data/lib/reek/code_parser.rb +15 -1
- data/lib/reek/module_context.rb +13 -0
- data/lib/reek/smell_warning.rb +9 -18
- data/lib/reek/smells/class_variable.rb +31 -0
- data/lib/reek/sniffer.rb +2 -0
- data/reek.gemspec +4 -4
- data/spec/quality/reek_source_spec.rb +1 -1
- data/spec/reek/adapters/report_spec.rb +4 -4
- data/spec/reek/adapters/should_reek_of_spec.rb +118 -94
- data/spec/reek/adapters/should_reek_only_of_spec.rb +2 -2
- data/spec/reek/adapters/should_reek_spec.rb +3 -3
- data/spec/reek/class_context_spec.rb +0 -10
- data/spec/reek/code_parser_spec.rb +105 -1
- data/spec/reek/smell_warning_spec.rb +100 -34
- data/spec/reek/smells/class_variable_spec.rb +81 -0
- data/spec/reek/stop_context_spec.rb +27 -0
- data/spec/spec_helper.rb +2 -0
- data/tasks/reek.rake +5 -0
- metadata +7 -4
data/History.txt
CHANGED
data/README.rdoc
CHANGED
data/config/defaults.reek
CHANGED
@@ -16,6 +16,10 @@ LongParameterList:
|
|
16
16
|
FeatureEnvy:
|
17
17
|
exclude:
|
18
18
|
- initialize
|
19
|
+
enabled: true
|
20
|
+
ClassVariable:
|
21
|
+
exclude: &id001 []
|
22
|
+
|
19
23
|
enabled: true
|
20
24
|
UncommunicativeName:
|
21
25
|
accept:
|
@@ -27,8 +31,7 @@ UncommunicativeName:
|
|
27
31
|
- !ruby/regexp /^.$/
|
28
32
|
- !ruby/regexp /[0-9]$/
|
29
33
|
NestedIterators:
|
30
|
-
exclude:
|
31
|
-
|
34
|
+
exclude: *id001
|
32
35
|
enabled: true
|
33
36
|
LongMethod:
|
34
37
|
max_statements: 5
|
data/features/samples.feature
CHANGED
@@ -9,7 +9,10 @@ Feature: Basic smell detection
|
|
9
9
|
Then it fails with exit status 2
|
10
10
|
And it reports:
|
11
11
|
"""
|
12
|
-
spec/samples/inline.rb --
|
12
|
+
spec/samples/inline.rb -- 39 warnings (+1 masked):
|
13
|
+
Inline declares the class variable @@directory (Class Variable)
|
14
|
+
Inline declares the class variable @@rootdir (Class Variable)
|
15
|
+
Inline::C declares the class variable @@type_map (Class Variable)
|
13
16
|
Inline::C has at least 13 instance variables (Large Class)
|
14
17
|
Inline::C takes parameters [options, src] to 5 methods (Data Clump)
|
15
18
|
Inline::C tests $DEBUG at least 7 times (Simulated Polymorphism)
|
data/lib/reek.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
require 'reek/command_line'
|
1
|
+
require 'reek/adapters/command_line'
|
2
2
|
require 'reek/adapters/source'
|
3
3
|
require 'reek/adapters/core_extras'
|
4
|
-
require 'reek/adapters/report'
|
5
4
|
|
6
5
|
module Reek
|
7
6
|
#
|
@@ -11,13 +10,13 @@ module Reek
|
|
11
10
|
#
|
12
11
|
class Application
|
13
12
|
def initialize(argv)
|
14
|
-
@
|
13
|
+
@options = Options.new(argv)
|
15
14
|
end
|
16
15
|
|
17
16
|
def examine_sources
|
18
17
|
# SMELL: Greedy Method
|
19
18
|
# Options.parse executes the -v and -h commands and throws a SystemExit
|
20
|
-
args =
|
19
|
+
args = @options.parse
|
21
20
|
if args.length > 0
|
22
21
|
@sniffer = args.sniff
|
23
22
|
else
|
@@ -27,9 +26,7 @@ module Reek
|
|
27
26
|
|
28
27
|
def reek
|
29
28
|
examine_sources
|
30
|
-
|
31
|
-
# This should use the actual type of report selected by the user's options
|
32
|
-
puts Report.new(@sniffer.sniffers).full_report
|
29
|
+
puts @options.create_report(@sniffer.sniffers).report
|
33
30
|
return @sniffer.smelly? ? 2 : 0
|
34
31
|
end
|
35
32
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'optparse'
|
2
2
|
require 'reek'
|
3
|
+
require 'reek/adapters/report'
|
3
4
|
|
4
5
|
module Reek
|
5
6
|
|
@@ -12,24 +13,12 @@ module Reek
|
|
12
13
|
CTX_SORT = '%m%c %w (%s)'
|
13
14
|
SMELL_SORT = '%m[%s] %c %w'
|
14
15
|
|
15
|
-
def self.default_options
|
16
|
-
{
|
17
|
-
:format => CTX_SORT,
|
18
|
-
:show_all => false,
|
19
|
-
:quiet => false
|
20
|
-
}
|
21
|
-
end
|
22
|
-
|
23
|
-
# SMELL: Global Variable
|
24
|
-
@@opts = default_options
|
25
|
-
|
26
|
-
def self.[](key)
|
27
|
-
@@opts[key]
|
28
|
-
end
|
29
|
-
|
30
16
|
def initialize(argv)
|
31
17
|
@argv = argv
|
32
18
|
@parser = OptionParser.new
|
19
|
+
@quiet = false
|
20
|
+
@show_all = false
|
21
|
+
@format = CTX_SORT
|
33
22
|
set_options
|
34
23
|
end
|
35
24
|
|
@@ -66,20 +55,24 @@ EOB
|
|
66
55
|
@parser.separator "\nReport formatting:"
|
67
56
|
|
68
57
|
@parser.on("-a", "--[no-]show-all", "Show all smells, including those masked by config settings") do |opt|
|
69
|
-
|
58
|
+
@show_all = opt
|
70
59
|
end
|
71
60
|
@parser.on("-q", "--quiet", "Suppress headings for smell-free source files") do
|
72
|
-
|
61
|
+
@quiet = true
|
73
62
|
end
|
74
63
|
@parser.on('-f', "--format FORMAT", 'Specify the format of smell warnings') do |arg|
|
75
|
-
|
64
|
+
@format = arg unless arg.nil?
|
76
65
|
end
|
77
66
|
@parser.on('-c', '--context-first', "Sort by context; sets the format string to \"#{CTX_SORT}\"") do
|
78
|
-
|
67
|
+
@format = CTX_SORT
|
79
68
|
end
|
80
69
|
@parser.on('-s', '--smell-first', "Sort by smell; sets the format string to \"#{SMELL_SORT}\"") do
|
81
|
-
|
70
|
+
@format = SMELL_SORT
|
82
71
|
end
|
83
72
|
end
|
73
|
+
|
74
|
+
def create_report(sniffers)
|
75
|
+
@quiet ? QuietReport.new(sniffers, @format, @show_all) : FullReport.new(sniffers, @format, @show_all)
|
76
|
+
end
|
84
77
|
end
|
85
78
|
end
|
data/lib/reek/adapters/report.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
require 'set'
|
2
|
-
require 'reek/command_line' # SMELL: Global Variable
|
2
|
+
require 'reek/adapters/command_line' # SMELL: Global Variable
|
3
3
|
|
4
4
|
module Reek
|
5
5
|
class ReportSection
|
6
6
|
|
7
|
-
def initialize(sniffer) # :nodoc:
|
7
|
+
def initialize(sniffer, display_masked_warnings, format) # :nodoc:
|
8
|
+
@format = format
|
8
9
|
@masked_warnings = SortedSet.new
|
9
10
|
@warnings = SortedSet.new
|
10
11
|
@desc = sniffer.desc
|
12
|
+
@display_masked_warnings = display_masked_warnings
|
11
13
|
sniffer.report_on(self)
|
12
14
|
end
|
13
15
|
|
@@ -27,7 +29,6 @@ module Reek
|
|
27
29
|
# Creates a formatted report of all the +Smells::SmellWarning+ objects recorded in
|
28
30
|
# this report, with a heading.
|
29
31
|
def full_report
|
30
|
-
return quiet_report if Options[:quiet]
|
31
32
|
result = header
|
32
33
|
result += ":\n#{smell_list}" if should_report
|
33
34
|
result += "\n"
|
@@ -48,14 +49,14 @@ module Reek
|
|
48
49
|
# Creates a formatted report of all the +Smells::SmellWarning+ objects recorded in
|
49
50
|
# this report.
|
50
51
|
def smell_list
|
51
|
-
smells =
|
52
|
-
smells.map {|smell| " #{smell.report}"}.join("\n")
|
52
|
+
smells = @display_masked_warnings ? @all_warnings : @warnings
|
53
|
+
smells.map {|smell| " #{smell.report(@format)}"}.join("\n")
|
53
54
|
end
|
54
55
|
|
55
56
|
private
|
56
57
|
|
57
58
|
def should_report
|
58
|
-
@warnings.length > 0 or (
|
59
|
+
@warnings.length > 0 or (@display_masked_warnings and @masked_warnings.length > 0)
|
59
60
|
end
|
60
61
|
|
61
62
|
def visible_header
|
@@ -72,19 +73,19 @@ module Reek
|
|
72
73
|
end
|
73
74
|
|
74
75
|
class Report
|
75
|
-
|
76
|
-
|
77
|
-
@partials = Array(sniffers).map {|sn| ReportSection.new(sn)}
|
76
|
+
def initialize(sniffers, format, display_masked_warnings = false)
|
77
|
+
@partials = Array(sniffers).map {|sn| ReportSection.new(sn, display_masked_warnings, format)}
|
78
78
|
end
|
79
|
+
end
|
79
80
|
|
80
|
-
|
81
|
-
|
82
|
-
# kind of report.
|
83
|
-
def full_report
|
81
|
+
class FullReport < Report
|
82
|
+
def report
|
84
83
|
@partials.map { |rpt| rpt.full_report }.join
|
85
84
|
end
|
85
|
+
end
|
86
86
|
|
87
|
-
|
87
|
+
class QuietReport < Report
|
88
|
+
def report
|
88
89
|
@partials.map { |rpt| rpt.quiet_report }.join
|
89
90
|
end
|
90
91
|
end
|
data/lib/reek/adapters/spec.rb
CHANGED
@@ -44,9 +44,14 @@ module Reek
|
|
44
44
|
#
|
45
45
|
module Spec
|
46
46
|
module ReekMatcher
|
47
|
+
def create_reporter(sniffers)
|
48
|
+
QuietReport.new(sniffers, '%c %w (%s)', false)
|
49
|
+
end
|
47
50
|
def report
|
48
|
-
|
51
|
+
create_reporter(@sniffer.sniffers).report
|
49
52
|
end
|
53
|
+
|
54
|
+
module_function :create_reporter
|
50
55
|
end
|
51
56
|
|
52
57
|
class ShouldReek # :nodoc:
|
data/lib/reek/class_context.rb
CHANGED
@@ -24,7 +24,7 @@ module Reek
|
|
24
24
|
CodeParser.new(sniffer).process_class(source.syntax_tree)
|
25
25
|
end
|
26
26
|
|
27
|
-
attr_reader :conditionals, :parsed_methods
|
27
|
+
attr_reader :conditionals, :parsed_methods, :class_variables
|
28
28
|
|
29
29
|
# SMELL: inconsistent with other contexts (not linked to the sexp)
|
30
30
|
def initialize(outer, name, superclass = nil)
|
@@ -34,6 +34,7 @@ module Reek
|
|
34
34
|
@parsed_methods = []
|
35
35
|
@instance_variables = Set.new
|
36
36
|
@conditionals = []
|
37
|
+
@class_variables = Set.new
|
37
38
|
end
|
38
39
|
|
39
40
|
def myself
|
@@ -58,6 +59,10 @@ module Reek
|
|
58
59
|
@parsed_methods.length
|
59
60
|
end
|
60
61
|
|
62
|
+
def record_class_variable(cvar)
|
63
|
+
@class_variables << Name.new(cvar)
|
64
|
+
end
|
65
|
+
|
61
66
|
def record_instance_variable(sym)
|
62
67
|
@instance_variables << Name.new(sym)
|
63
68
|
end
|
data/lib/reek/code_parser.rb
CHANGED
@@ -51,10 +51,12 @@ module Reek
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def process_module(exp)
|
54
|
-
|
54
|
+
scope = ModuleContext.create(@element, exp)
|
55
|
+
push(scope) do
|
55
56
|
process_default(exp)
|
56
57
|
check_smells(:module)
|
57
58
|
end
|
59
|
+
scope
|
58
60
|
end
|
59
61
|
|
60
62
|
def process_class(exp)
|
@@ -66,6 +68,18 @@ module Reek
|
|
66
68
|
scope
|
67
69
|
end
|
68
70
|
|
71
|
+
def process_cvar(exp)
|
72
|
+
@element.record_class_variable(exp[1])
|
73
|
+
end
|
74
|
+
|
75
|
+
def process_cvasgn(exp)
|
76
|
+
process_cvar(exp)
|
77
|
+
end
|
78
|
+
|
79
|
+
def process_cvdecl(exp)
|
80
|
+
process_cvar(exp)
|
81
|
+
end
|
82
|
+
|
69
83
|
def process_defn(exp)
|
70
84
|
handle_context(MethodContext, :defn, exp)
|
71
85
|
end
|
data/lib/reek/module_context.rb
CHANGED
@@ -8,9 +8,18 @@ module Reek
|
|
8
8
|
ModuleContext.new(res[0], res[1])
|
9
9
|
end
|
10
10
|
|
11
|
+
def ModuleContext.from_s(src)
|
12
|
+
source = src.to_reek_source
|
13
|
+
sniffer = Sniffer.new(source)
|
14
|
+
CodeParser.new(sniffer).process_module(source.syntax_tree)
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :class_variables
|
18
|
+
|
11
19
|
def initialize(outer, name)
|
12
20
|
super(outer, nil)
|
13
21
|
@name = name
|
22
|
+
@class_variables = Set.new
|
14
23
|
end
|
15
24
|
|
16
25
|
def myself
|
@@ -26,6 +35,10 @@ module Reek
|
|
26
35
|
"#{@outer.outer_name}#{@name}::"
|
27
36
|
end
|
28
37
|
|
38
|
+
def record_class_variable(cvar)
|
39
|
+
@class_variables << Name.new(cvar)
|
40
|
+
end
|
41
|
+
|
29
42
|
def variable_names
|
30
43
|
[]
|
31
44
|
end
|
data/lib/reek/smell_warning.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'reek/command_line' # SMELL: Global Variable used for options
|
2
|
-
|
3
1
|
module Reek
|
4
2
|
|
5
3
|
#
|
@@ -16,11 +14,11 @@ module Reek
|
|
16
14
|
end
|
17
15
|
|
18
16
|
def hash # :nodoc:
|
19
|
-
|
17
|
+
sort_key.hash
|
20
18
|
end
|
21
19
|
|
22
20
|
def <=>(other)
|
23
|
-
|
21
|
+
sort_key <=> other.sort_key
|
24
22
|
end
|
25
23
|
|
26
24
|
alias eql? <=> # :nodoc:
|
@@ -35,25 +33,18 @@ module Reek
|
|
35
33
|
end
|
36
34
|
|
37
35
|
def contains_all?(patterns)
|
38
|
-
rpt =
|
36
|
+
rpt = sort_key.to_s
|
39
37
|
return patterns.all? {|exp| exp === rpt}
|
40
38
|
end
|
41
39
|
|
42
|
-
def
|
43
|
-
|
40
|
+
def sort_key
|
41
|
+
[@context.to_s, @warning, @detector.smell_name]
|
44
42
|
end
|
45
43
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
# * %c <-- a description of the +CodeContext+ containing the smell
|
51
|
-
# * %m <-- "(is_masked) " if this is a is_masked smell
|
52
|
-
# * %s <-- the name of the smell that was detected
|
53
|
-
# * %w <-- the specific problem that was detected
|
54
|
-
#
|
55
|
-
def report
|
56
|
-
basic_report.gsub(/\%m/, @is_masked ? '(masked) ' : '')
|
44
|
+
protected :sort_key
|
45
|
+
|
46
|
+
def report(format)
|
47
|
+
format.gsub(/\%s/, @detector.smell_name).gsub(/\%c/, @context.to_s).gsub(/\%w/, @warning).gsub(/\%m/, @is_masked ? '(masked) ' : '')
|
57
48
|
end
|
58
49
|
|
59
50
|
def report_on(report)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'reek/smells/smell_detector'
|
2
|
+
|
3
|
+
module Reek
|
4
|
+
module Smells
|
5
|
+
|
6
|
+
#
|
7
|
+
# Class variables form part of the global runtime state, and as such make
|
8
|
+
# it easy for one part of the system to accidentally or inadvertently
|
9
|
+
# depend on another part of the system. So the system becomes more prone to
|
10
|
+
# problems where changing something over here breaks something over there.
|
11
|
+
# In particular, class variables can make it hard to set up tests (because
|
12
|
+
# the context of the test includes all global state).
|
13
|
+
#
|
14
|
+
class ClassVariable < SmellDetector
|
15
|
+
|
16
|
+
def self.contexts # :nodoc:
|
17
|
+
[:class, :module]
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# Checks whether the given class declares any class variables.
|
22
|
+
# Remembers any smells found.
|
23
|
+
#
|
24
|
+
def examine_context(klass)
|
25
|
+
klass.class_variables.each do |cvar|
|
26
|
+
found(klass, "declares the class variable #{cvar}")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/reek/sniffer.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'reek/detector_stack'
|
2
2
|
|
3
3
|
# SMELL: Duplication -- all these should be found automagically
|
4
|
+
require 'reek/smells/class_variable'
|
4
5
|
require 'reek/smells/control_couple'
|
5
6
|
require 'reek/smells/data_clump'
|
6
7
|
require 'reek/smells/duplication'
|
@@ -48,6 +49,7 @@ module Reek
|
|
48
49
|
def self.smell_classes
|
49
50
|
# SMELL: Duplication -- these should be loaded by listing the files
|
50
51
|
[
|
52
|
+
Smells::ClassVariable,
|
51
53
|
Smells::ControlCouple,
|
52
54
|
Smells::DataClump,
|
53
55
|
Smells::Duplication,
|