reek 1.1.3 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +44 -4
- data/License.txt +20 -0
- data/README.rdoc +83 -0
- data/Rakefile +0 -1
- data/bin/reek +3 -11
- data/config/defaults.reek +20 -1
- data/features/masking_smells.feature +111 -0
- data/features/options.feature +49 -0
- data/features/reports.feature +90 -0
- data/features/samples.feature +284 -0
- data/features/stdin.feature +43 -0
- data/features/step_definitions/reek_steps.rb +35 -0
- data/features/support/env.rb +38 -0
- data/lib/reek.rb +1 -1
- data/lib/reek/adapters/application.rb +47 -0
- data/lib/reek/adapters/config_file.rb +31 -0
- data/lib/reek/adapters/core_extras.rb +72 -0
- data/lib/reek/{object_source.rb → adapters/object_source.rb} +15 -19
- data/lib/reek/{rake_task.rb → adapters/rake_task.rb} +2 -2
- data/lib/reek/adapters/report.rb +91 -0
- data/lib/reek/adapters/source.rb +53 -0
- data/lib/reek/{spec.rb → adapters/spec.rb} +45 -60
- data/lib/reek/block_context.rb +1 -1
- data/lib/reek/class_context.rb +26 -6
- data/lib/reek/code_context.rb +8 -0
- data/lib/reek/code_parser.rb +82 -39
- data/lib/reek/command_line.rb +85 -0
- data/lib/reek/configuration.rb +51 -0
- data/lib/reek/detector_stack.rb +39 -0
- data/lib/reek/exceptions.reek +8 -1
- data/lib/reek/method_context.rb +53 -11
- data/lib/reek/module_context.rb +1 -2
- data/lib/reek/name.rb +8 -2
- data/lib/reek/sexp_formatter.rb +2 -0
- data/lib/reek/smell_warning.rb +26 -8
- data/lib/reek/smells/control_couple.rb +8 -4
- data/lib/reek/smells/data_clump.rb +88 -0
- data/lib/reek/smells/duplication.rb +11 -9
- data/lib/reek/smells/feature_envy.rb +3 -4
- data/lib/reek/smells/large_class.rb +17 -17
- data/lib/reek/smells/long_method.rb +10 -8
- data/lib/reek/smells/long_parameter_list.rb +16 -10
- data/lib/reek/smells/long_yield_list.rb +1 -1
- data/lib/reek/smells/nested_iterators.rb +3 -3
- data/lib/reek/smells/simulated_polymorphism.rb +58 -0
- data/lib/reek/smells/smell_detector.rb +94 -27
- data/lib/reek/smells/uncommunicative_name.rb +23 -23
- data/lib/reek/smells/utility_function.rb +27 -11
- data/lib/reek/sniffer.rb +183 -0
- data/reek.gemspec +5 -5
- data/spec/quality/reek_source_spec.rb +15 -0
- data/spec/reek/adapters/report_spec.rb +49 -0
- data/spec/reek/adapters/should_reek_of_spec.rb +108 -0
- data/spec/reek/adapters/should_reek_only_of_spec.rb +87 -0
- data/spec/reek/adapters/should_reek_spec.rb +92 -0
- data/spec/reek/block_context_spec.rb +7 -1
- data/spec/reek/class_context_spec.rb +39 -16
- data/spec/reek/code_context_spec.rb +7 -7
- data/spec/reek/code_parser_spec.rb +6 -1
- data/spec/reek/config_spec.rb +3 -3
- data/spec/reek/configuration_spec.rb +12 -0
- data/spec/reek/method_context_spec.rb +2 -2
- data/spec/reek/name_spec.rb +24 -0
- data/spec/reek/object_source_spec.rb +23 -0
- data/spec/reek/singleton_method_context_spec.rb +2 -2
- data/spec/reek/smell_warning_spec.rb +53 -0
- data/spec/reek/smells/data_clump_spec.rb +87 -0
- data/spec/reek/smells/duplication_spec.rb +13 -17
- data/spec/reek/smells/feature_envy_spec.rb +23 -28
- data/spec/reek/smells/large_class_spec.rb +109 -34
- data/spec/reek/smells/long_method_spec.rb +140 -3
- data/spec/reek/smells/long_parameter_list_spec.rb +1 -2
- data/spec/reek/smells/simulated_polymorphism_spec.rb +50 -0
- data/spec/reek/smells/smell_detector_spec.rb +53 -0
- data/spec/reek/smells/uncommunicative_name_spec.rb +20 -7
- data/spec/reek/smells/utility_function_spec.rb +76 -67
- data/spec/reek/sniffer_spec.rb +10 -0
- data/spec/samples/all_but_one_masked/clean_one.rb +6 -0
- data/spec/samples/all_but_one_masked/dirty.rb +7 -0
- data/spec/samples/all_but_one_masked/masked.reek +5 -0
- data/spec/samples/clean_due_to_masking/clean_one.rb +6 -0
- data/spec/samples/clean_due_to_masking/clean_three.rb +6 -0
- data/spec/samples/clean_due_to_masking/clean_two.rb +6 -0
- data/spec/samples/clean_due_to_masking/dirty_one.rb +7 -0
- data/spec/samples/clean_due_to_masking/dirty_two.rb +7 -0
- data/spec/samples/clean_due_to_masking/masked.reek +7 -0
- data/spec/samples/corrupt_config_file/corrupt.reek +1 -0
- data/spec/samples/corrupt_config_file/dirty.rb +7 -0
- data/spec/samples/empty_config_file/dirty.rb +7 -0
- data/spec/samples/empty_config_file/empty.reek +0 -0
- data/spec/samples/exceptions.reek +4 -0
- data/spec/{slow/samples → samples}/inline.rb +0 -0
- data/spec/samples/masked/dirty.rb +7 -0
- data/spec/samples/masked/masked.reek +3 -0
- data/spec/samples/mixed_results/clean_one.rb +6 -0
- data/spec/samples/mixed_results/clean_three.rb +6 -0
- data/spec/samples/mixed_results/clean_two.rb +6 -0
- data/spec/samples/mixed_results/dirty_one.rb +7 -0
- data/spec/samples/mixed_results/dirty_two.rb +7 -0
- data/spec/samples/not_quite_masked/dirty.rb +8 -0
- data/spec/samples/not_quite_masked/masked.reek +5 -0
- data/spec/{slow/samples → samples}/optparse.rb +0 -0
- data/spec/samples/overrides/masked/dirty.rb +7 -0
- data/spec/samples/overrides/masked/lower.reek +5 -0
- data/spec/samples/overrides/upper.reek +5 -0
- data/spec/{slow/samples → samples}/redcloth.rb +0 -0
- data/spec/samples/three_clean_files/clean_one.rb +6 -0
- data/spec/samples/three_clean_files/clean_three.rb +6 -0
- data/spec/samples/three_clean_files/clean_two.rb +6 -0
- data/spec/samples/two_smelly_files/dirty_one.rb +7 -0
- data/spec/samples/two_smelly_files/dirty_two.rb +7 -0
- data/spec/spec.opts +1 -1
- data/spec/spec_helper.rb +4 -4
- data/tasks/reek.rake +8 -5
- data/tasks/test.rake +51 -0
- metadata +75 -25
- data/README.txt +0 -6
- data/lib/reek/options.rb +0 -92
- data/lib/reek/report.rb +0 -81
- data/lib/reek/smells/smells.rb +0 -81
- data/lib/reek/source.rb +0 -127
- data/spec/reek/options_spec.rb +0 -13
- data/spec/reek/report_spec.rb +0 -48
- data/spec/reek/smells/smell_spec.rb +0 -24
- data/spec/slow/inline_spec.rb +0 -43
- data/spec/slow/optparse_spec.rb +0 -108
- data/spec/slow/redcloth_spec.rb +0 -101
- data/spec/slow/reek_source_spec.rb +0 -20
- data/spec/slow/script_spec.rb +0 -55
- data/spec/slow/source_list_spec.rb +0 -40
- data/tasks/rspec.rake +0 -21
@@ -0,0 +1,39 @@
|
|
1
|
+
|
2
|
+
module Reek
|
3
|
+
class DetectorStack
|
4
|
+
|
5
|
+
def initialize(default_detector)
|
6
|
+
@detectors = [default_detector]
|
7
|
+
end
|
8
|
+
|
9
|
+
def push(config)
|
10
|
+
clone = @detectors[-1].supersede_with(config)
|
11
|
+
@detectors << clone
|
12
|
+
end
|
13
|
+
|
14
|
+
def listen_to(hooks)
|
15
|
+
@detectors.each { |det| det.listen_to(hooks) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def report_on(report)
|
19
|
+
@detectors.each { |det| det.report_on(report) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def num_smells
|
23
|
+
total = 0
|
24
|
+
@detectors.each { |det| total += det.num_smells }
|
25
|
+
total
|
26
|
+
end
|
27
|
+
|
28
|
+
def has_smell?(patterns)
|
29
|
+
@detectors.each { |det| return true if det.has_smell?(patterns) }
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def smelly?
|
34
|
+
# SMELL: Duplication: look at al those loops!
|
35
|
+
@detectors.each { |det| return true if det.smelly? }
|
36
|
+
false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/reek/exceptions.reek
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
---
|
1
|
+
---
|
2
|
+
Duplication:
|
3
|
+
exclude:
|
4
|
+
- Reek::Options#set_options
|
2
5
|
FeatureEnvy:
|
3
6
|
exclude:
|
4
7
|
- examine_context
|
@@ -8,6 +11,10 @@ LargeClass:
|
|
8
11
|
LongMethod:
|
9
12
|
exclude:
|
10
13
|
- Reek::SexpFormatter#self.format
|
14
|
+
- Reek::Options#set_options
|
15
|
+
UncommunicativeName:
|
16
|
+
accept:
|
17
|
+
- process_op_asgn1
|
11
18
|
UtilityFunction:
|
12
19
|
exclude:
|
13
20
|
- Reek::Spec
|
data/lib/reek/method_context.rb
CHANGED
@@ -2,7 +2,55 @@ require 'reek/name'
|
|
2
2
|
require 'reek/code_context'
|
3
3
|
require 'reek/object_refs'
|
4
4
|
|
5
|
+
class Array
|
6
|
+
def power_set
|
7
|
+
self.inject([[]]) { |cum, element| cum.cross(element) }
|
8
|
+
end
|
9
|
+
|
10
|
+
def bounded_power_set(lower_bound)
|
11
|
+
power_set.select {|ps| ps.length > lower_bound}
|
12
|
+
end
|
13
|
+
|
14
|
+
def cross(element)
|
15
|
+
result = []
|
16
|
+
self.each do |set|
|
17
|
+
result << set
|
18
|
+
result << (set + [element])
|
19
|
+
end
|
20
|
+
result
|
21
|
+
end
|
22
|
+
|
23
|
+
def intersection
|
24
|
+
self.inject { |res, elem| elem & res }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
5
28
|
module Reek
|
29
|
+
|
30
|
+
module MethodParameters
|
31
|
+
def self.is_arg?(param)
|
32
|
+
return false if (Array === param and param[0] == :block)
|
33
|
+
return !(param.to_s =~ /^\&/)
|
34
|
+
end
|
35
|
+
|
36
|
+
def names
|
37
|
+
return @names if @names
|
38
|
+
@names = self[1..-1].select {|arg| MethodParameters.is_arg?(arg)}.map {|arg| Name.new(arg)}
|
39
|
+
end
|
40
|
+
|
41
|
+
def length
|
42
|
+
names.length
|
43
|
+
end
|
44
|
+
|
45
|
+
def include?(name)
|
46
|
+
names.include?(name)
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s
|
50
|
+
"[#{names.map{|nm| nm.to_s}.join(', ')}]"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
6
54
|
class MethodContext < CodeContext
|
7
55
|
attr_reader :parameters
|
8
56
|
attr_reader :calls
|
@@ -11,14 +59,16 @@ module Reek
|
|
11
59
|
|
12
60
|
def initialize(outer, exp, record = true)
|
13
61
|
super(outer, exp)
|
14
|
-
@parameters = []
|
62
|
+
@parameters = exp[exp[0] == :defn ? 2 : 3] # SMELL: SimulatedPolymorphism
|
63
|
+
@parameters ||= []
|
64
|
+
@parameters.extend(MethodParameters)
|
15
65
|
@local_variables = []
|
16
66
|
@name = Name.new(exp[1])
|
17
67
|
@num_statements = 0
|
18
68
|
@calls = Hash.new(0)
|
19
69
|
@depends_on_self = false
|
20
70
|
@refs = ObjectRefs.new
|
21
|
-
@outer.record_method(
|
71
|
+
@outer.record_method(self)
|
22
72
|
end
|
23
73
|
|
24
74
|
def count_statements(num)
|
@@ -63,14 +113,6 @@ module Reek
|
|
63
113
|
@local_variables << Name.new(sym)
|
64
114
|
end
|
65
115
|
|
66
|
-
def self.is_block_arg?(param)
|
67
|
-
(Array === param and param[0] == :block) or (param.to_s =~ /^\&/)
|
68
|
-
end
|
69
|
-
|
70
|
-
def record_parameter(param)
|
71
|
-
@parameters << Name.new(param) unless MethodContext.is_block_arg?(param)
|
72
|
-
end
|
73
|
-
|
74
116
|
def outer_name
|
75
117
|
"#{@outer.outer_name}#{@name}/"
|
76
118
|
end
|
@@ -85,7 +127,7 @@ module Reek
|
|
85
127
|
end
|
86
128
|
|
87
129
|
def variable_names
|
88
|
-
@parameters + @local_variables
|
130
|
+
@parameters.names + @local_variables
|
89
131
|
end
|
90
132
|
end
|
91
133
|
end
|
data/lib/reek/module_context.rb
CHANGED
data/lib/reek/name.rb
CHANGED
@@ -32,16 +32,22 @@ module Reek
|
|
32
32
|
@name.hash
|
33
33
|
end
|
34
34
|
|
35
|
+
def eql?(other)
|
36
|
+
self == other
|
37
|
+
end
|
38
|
+
|
35
39
|
def <=>(other) # :nodoc:
|
36
40
|
@name <=> other.to_s
|
37
41
|
end
|
38
42
|
|
39
|
-
alias eql? <=>
|
40
|
-
|
41
43
|
def effective_name
|
42
44
|
@name.gsub(/^@*/, '')
|
43
45
|
end
|
44
46
|
|
47
|
+
def inspect
|
48
|
+
@name.inspect
|
49
|
+
end
|
50
|
+
|
45
51
|
def to_s
|
46
52
|
@name
|
47
53
|
end
|
data/lib/reek/sexp_formatter.rb
CHANGED
data/lib/reek/smell_warning.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'reek/options
|
1
|
+
require 'reek/command_line' # SMELL: Global Variable used for options
|
2
2
|
|
3
3
|
module Reek
|
4
4
|
|
@@ -8,18 +8,19 @@ module Reek
|
|
8
8
|
class SmellWarning
|
9
9
|
include Comparable
|
10
10
|
|
11
|
-
def initialize(smell, context, warning)
|
12
|
-
@
|
11
|
+
def initialize(smell, context, warning, masked)
|
12
|
+
@detector = smell
|
13
13
|
@context = context
|
14
14
|
@warning = warning
|
15
|
+
@is_masked = masked
|
15
16
|
end
|
16
17
|
|
17
18
|
def hash # :nodoc:
|
18
|
-
|
19
|
+
basic_report.hash
|
19
20
|
end
|
20
21
|
|
21
22
|
def <=>(other)
|
22
|
-
|
23
|
+
basic_report <=> other.basic_report
|
23
24
|
end
|
24
25
|
|
25
26
|
alias eql? <=> # :nodoc:
|
@@ -29,21 +30,38 @@ module Reek
|
|
29
30
|
# +smell_class+ and its report string matches all of the +patterns+.
|
30
31
|
#
|
31
32
|
def matches?(smell_class, patterns)
|
32
|
-
return false unless smell_class.to_s == @
|
33
|
+
return false unless smell_class.to_s == @detector.class.class_name
|
34
|
+
contains_all?(patterns)
|
35
|
+
end
|
36
|
+
|
37
|
+
def contains_all?(patterns)
|
33
38
|
rpt = report
|
34
39
|
return patterns.all? {|exp| exp === rpt}
|
35
40
|
end
|
36
41
|
|
42
|
+
def basic_report
|
43
|
+
Options[:format].gsub(/\%s/, @detector.smell_name).gsub(/\%c/, @context.to_s).gsub(/\%w/, @warning)
|
44
|
+
end
|
45
|
+
|
37
46
|
#
|
38
47
|
# Returns a copy of the current report format (see +Options+)
|
39
48
|
# in which the following magic tokens have been substituted:
|
40
49
|
#
|
41
|
-
# * %s <-- the name of the smell that was detected
|
42
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
|
43
53
|
# * %w <-- the specific problem that was detected
|
44
54
|
#
|
45
55
|
def report
|
46
|
-
|
56
|
+
basic_report.gsub(/\%m/, @is_masked ? '(masked) ' : '')
|
57
|
+
end
|
58
|
+
|
59
|
+
def report_on(report)
|
60
|
+
if @is_masked
|
61
|
+
report.record_masked_smell(self)
|
62
|
+
else
|
63
|
+
report << self
|
64
|
+
end
|
47
65
|
end
|
48
66
|
end
|
49
67
|
end
|
@@ -49,12 +49,16 @@ module Reek
|
|
49
49
|
|
50
50
|
#
|
51
51
|
# Checks whether the given conditional statement relies on a control couple.
|
52
|
-
#
|
52
|
+
# Remembers any smells found.
|
53
53
|
#
|
54
|
-
def examine_context(cond
|
54
|
+
def examine_context(cond)
|
55
55
|
return unless cond.tests_a_parameter?
|
56
|
-
|
57
|
-
|
56
|
+
# SMELL: Duplication
|
57
|
+
# This smell is reported once for each conditional that tests the
|
58
|
+
# same parameter. Which means that the same smell can recur within
|
59
|
+
# a single sniffer. Which in turn means that the sniffer can't count
|
60
|
+
# its smells without knowing which are duplicates.
|
61
|
+
found(cond, "is controlled by argument #{SexpFormatter.format(cond.if_expr)}")
|
58
62
|
end
|
59
63
|
end
|
60
64
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'reek/smells/smell_detector'
|
2
|
+
require 'reek/smell_warning'
|
3
|
+
require 'reek/sexp_formatter'
|
4
|
+
|
5
|
+
module Reek
|
6
|
+
module Smells
|
7
|
+
|
8
|
+
#
|
9
|
+
# A Data Clump occurs when the same two or three items frequently
|
10
|
+
# appear together in classes and parameter lists, or when a group
|
11
|
+
# of instance variable names start or end with similar substrings.
|
12
|
+
#
|
13
|
+
# The recurrence of the items often means there is duplicate code
|
14
|
+
# spread around to handle them. There may be an abstraction missing
|
15
|
+
# from the code, making the system harder to understand.
|
16
|
+
#
|
17
|
+
# Currently Reek looks for a group of two or more parameters with
|
18
|
+
# the same names that are expected by three or more methods of a class.
|
19
|
+
#
|
20
|
+
class DataClump < SmellDetector
|
21
|
+
|
22
|
+
def self.contexts # :nodoc:
|
23
|
+
[:class]
|
24
|
+
end
|
25
|
+
|
26
|
+
# The name of the config field that sets the maximum allowed
|
27
|
+
# copies of any clump.
|
28
|
+
MAX_COPIES_KEY = 'max_copies'
|
29
|
+
|
30
|
+
DEFAULT_MAX_COPIES = 2
|
31
|
+
|
32
|
+
MIN_CLUMP_SIZE_KEY = 'min_clump_size'
|
33
|
+
DEFAULT_MIN_CLUMP_SIZE = 2
|
34
|
+
|
35
|
+
def self.default_config
|
36
|
+
super.adopt(
|
37
|
+
MAX_COPIES_KEY => DEFAULT_MAX_COPIES,
|
38
|
+
MIN_CLUMP_SIZE_KEY => DEFAULT_MIN_CLUMP_SIZE
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize(config = DataClump.default_config)
|
43
|
+
super(config)
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Checks the given ClassContext for multiple identical conditional tests.
|
48
|
+
# Remembers any smells found.
|
49
|
+
#
|
50
|
+
def examine_context(klass)
|
51
|
+
max_copies = value(MAX_COPIES_KEY, klass, DEFAULT_MAX_COPIES)
|
52
|
+
min_clump_size = value(MIN_CLUMP_SIZE_KEY, klass, DEFAULT_MIN_CLUMP_SIZE)
|
53
|
+
MethodGroup.new(klass, min_clump_size, max_copies).clumps.each do |clump, occurs|
|
54
|
+
found(klass, "takes parameters #{DataClump.print_clump(clump)} to #{occurs} methods")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.print_clump(clump)
|
59
|
+
"[#{clump.map {|name| name.to_s}.join(', ')}]"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Represents a group of methods
|
65
|
+
class MethodGroup # :nodoc:
|
66
|
+
|
67
|
+
def self.intersection_of_parameters_of(methods)
|
68
|
+
methods.map {|meth| meth.parameters.names.sort}.intersection
|
69
|
+
end
|
70
|
+
|
71
|
+
def initialize(klass, min_clump_size, max_copies)
|
72
|
+
@klass = klass
|
73
|
+
@min_clump_size = min_clump_size
|
74
|
+
@max_copies = max_copies
|
75
|
+
end
|
76
|
+
|
77
|
+
def clumps
|
78
|
+
results = Hash.new(0)
|
79
|
+
@klass.parameterized_methods(@min_clump_size).bounded_power_set(@max_copies).each do |methods|
|
80
|
+
clump = MethodGroup.intersection_of_parameters_of(methods)
|
81
|
+
if clump.length >= @min_clump_size
|
82
|
+
results[clump] = [methods.length, results[clump]].max
|
83
|
+
end
|
84
|
+
end
|
85
|
+
results
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -24,26 +24,28 @@ module Reek
|
|
24
24
|
# identical calls to be permitted within any single method.
|
25
25
|
MAX_ALLOWED_CALLS_KEY = 'max_calls'
|
26
26
|
|
27
|
+
DEFAULT_MAX_CALLS = 1
|
28
|
+
|
27
29
|
def self.default_config
|
28
|
-
super.adopt(MAX_ALLOWED_CALLS_KEY =>
|
30
|
+
super.adopt(MAX_ALLOWED_CALLS_KEY => DEFAULT_MAX_CALLS)
|
29
31
|
end
|
30
32
|
|
31
33
|
def initialize(config = Duplication.default_config)
|
32
|
-
super
|
33
|
-
@max_calls = config[MAX_ALLOWED_CALLS_KEY]
|
34
|
+
super(config)
|
34
35
|
end
|
35
36
|
|
36
|
-
def examine_context(method
|
37
|
-
smelly_calls(method).each do |
|
38
|
-
|
39
|
-
|
37
|
+
def examine_context(method)
|
38
|
+
smelly_calls(method).each do |call_data|
|
39
|
+
num = call_data[1]
|
40
|
+
multiple = num == 2 ? 'twice' : "#{num} times"
|
41
|
+
found(method, "calls #{SexpFormatter.format(call_data[0])} #{multiple}")
|
40
42
|
end
|
41
43
|
end
|
42
44
|
|
43
45
|
def smelly_calls(method) # :nodoc:
|
44
46
|
method.calls.select do |key,val|
|
45
|
-
val >
|
46
|
-
end
|
47
|
+
val > value(MAX_ALLOWED_CALLS_KEY, method, DEFAULT_MAX_CALLS) and key[2] != :new
|
48
|
+
end
|
47
49
|
end
|
48
50
|
end
|
49
51
|
end
|
@@ -45,12 +45,11 @@ module Reek
|
|
45
45
|
#
|
46
46
|
# Checks whether the given +context+ includes any code fragment that
|
47
47
|
# might "belong" on another class.
|
48
|
-
#
|
48
|
+
# Remembers any smells found.
|
49
49
|
#
|
50
|
-
def examine_context(context
|
50
|
+
def examine_context(context)
|
51
51
|
context.envious_receivers.each do |ref|
|
52
|
-
|
53
|
-
"refers to #{SexpFormatter.format(ref)} more than self")
|
52
|
+
found(context, "refers to #{SexpFormatter.format(ref)} more than self")
|
54
53
|
end
|
55
54
|
end
|
56
55
|
end
|
@@ -20,49 +20,49 @@ module Reek
|
|
20
20
|
# permitted in a class.
|
21
21
|
MAX_ALLOWED_METHODS_KEY = 'max_methods'
|
22
22
|
|
23
|
+
DEFAULT_MAX_METHODS = 25
|
24
|
+
|
23
25
|
# The name of the config field that sets the maximum number of instance
|
24
26
|
# variables permitted in a class.
|
25
27
|
MAX_ALLOWED_IVARS_KEY = 'max_instance_variables'
|
26
28
|
|
29
|
+
DEFAULT_MAX_IVARS = 9
|
30
|
+
|
27
31
|
def self.contexts # :nodoc:
|
28
32
|
[:class]
|
29
33
|
end
|
30
34
|
|
31
35
|
def self.default_config
|
32
36
|
super.adopt(
|
33
|
-
MAX_ALLOWED_METHODS_KEY =>
|
34
|
-
MAX_ALLOWED_IVARS_KEY =>
|
37
|
+
MAX_ALLOWED_METHODS_KEY => DEFAULT_MAX_METHODS,
|
38
|
+
MAX_ALLOWED_IVARS_KEY => DEFAULT_MAX_IVARS,
|
35
39
|
EXCLUDE_KEY => []
|
36
40
|
)
|
37
41
|
end
|
38
42
|
|
39
43
|
def initialize(config = LargeClass.default_config)
|
40
|
-
super
|
41
|
-
@max_methods = config[MAX_ALLOWED_METHODS_KEY]
|
42
|
-
@max_instance_variables = config[MAX_ALLOWED_IVARS_KEY]
|
44
|
+
super(config)
|
43
45
|
end
|
44
46
|
|
45
|
-
def check_num_methods(klass
|
47
|
+
def check_num_methods(klass) # :nodoc:
|
46
48
|
count = klass.num_methods
|
47
|
-
return if count <=
|
48
|
-
|
49
|
-
"has at least #{count} methods")
|
49
|
+
return if count <= value(MAX_ALLOWED_METHODS_KEY, klass, DEFAULT_MAX_METHODS)
|
50
|
+
found(klass, "has at least #{count} methods")
|
50
51
|
end
|
51
52
|
|
52
|
-
def check_num_ivars(klass
|
53
|
+
def check_num_ivars(klass) # :nodoc:
|
53
54
|
count = klass.variable_names.length
|
54
|
-
return if count <=
|
55
|
-
|
56
|
-
"has at least #{count} instance variables")
|
55
|
+
return if count <= value(MAX_ALLOWED_IVARS_KEY, klass, DEFAULT_MAX_IVARS)
|
56
|
+
found(klass, "has at least #{count} instance variables")
|
57
57
|
end
|
58
58
|
|
59
59
|
#
|
60
60
|
# Checks +klass+ for too many methods or too many instance variables.
|
61
|
-
#
|
61
|
+
# Remembers any smells found.
|
62
62
|
#
|
63
|
-
def examine_context(klass
|
64
|
-
check_num_methods(klass
|
65
|
-
check_num_ivars(klass
|
63
|
+
def examine_context(klass)
|
64
|
+
check_num_methods(klass)
|
65
|
+
check_num_ivars(klass)
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|