reek 1.3.1 → 1.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG +6 -0
- data/README.md +15 -9
- data/bin/reek +1 -1
- data/config/defaults.reek +71 -86
- data/features/command_line_interface/options.feature +0 -15
- data/features/reports/reports.feature +23 -0
- data/features/samples.feature +3 -12
- data/lib/reek.rb +3 -3
- data/lib/reek/cli/application.rb +1 -1
- data/lib/reek/cli/command_line.rb +10 -8
- data/lib/reek/cli/reek_command.rb +6 -7
- data/lib/reek/cli/report.rb +34 -38
- data/lib/reek/cli/version_command.rb +1 -1
- data/lib/reek/cli/yaml_command.rb +1 -1
- data/lib/reek/core/code_parser.rb +4 -4
- data/lib/reek/core/hash_extensions.rb +2 -2
- data/lib/reek/core/method_context.rb +2 -2
- data/lib/reek/core/module_context.rb +4 -4
- data/lib/reek/core/singleton_method_context.rb +1 -1
- data/lib/reek/core/smell_repository.rb +7 -6
- data/lib/reek/core/sniffer.rb +4 -4
- data/lib/reek/examiner.rb +10 -3
- data/lib/reek/smell_warning.rb +0 -2
- data/lib/reek/smells.rb +22 -21
- data/lib/reek/smells/attribute.rb +4 -8
- data/lib/reek/smells/boolean_parameter.rb +2 -2
- data/lib/reek/smells/class_variable.rb +3 -2
- data/lib/reek/smells/{control_couple.rb → control_parameter.rb} +5 -5
- data/lib/reek/smells/data_clump.rb +13 -29
- data/lib/reek/smells/{duplication.rb → duplicate_method_call.rb} +9 -11
- data/lib/reek/smells/feature_envy.rb +2 -2
- data/lib/reek/smells/irresponsible_module.rb +3 -2
- data/lib/reek/smells/long_parameter_list.rb +6 -10
- data/lib/reek/smells/long_yield_list.rb +4 -8
- data/lib/reek/smells/nested_iterators.rb +31 -25
- data/lib/reek/smells/nil_check.rb +11 -12
- data/lib/reek/smells/{simulated_polymorphism.rb → repeated_conditional.rb} +6 -10
- data/lib/reek/smells/smell_detector.rb +3 -6
- data/lib/reek/smells/too_many_instance_variables.rb +60 -0
- data/lib/reek/smells/too_many_methods.rb +62 -0
- data/lib/reek/smells/{long_method.rb → too_many_statements.rb} +7 -12
- data/lib/reek/smells/uncommunicative_method_name.rb +3 -7
- data/lib/reek/smells/uncommunicative_module_name.rb +3 -7
- data/lib/reek/smells/uncommunicative_parameter_name.rb +4 -8
- data/lib/reek/smells/uncommunicative_variable_name.rb +5 -9
- data/lib/reek/smells/unused_parameters.rb +62 -13
- data/lib/reek/smells/utility_function.rb +3 -7
- data/lib/reek/source.rb +8 -8
- data/lib/reek/source/core_extras.rb +1 -1
- data/lib/reek/source/source_code.rb +2 -2
- data/lib/reek/source/source_file.rb +2 -2
- data/lib/reek/source/source_locator.rb +1 -1
- data/lib/reek/source/source_repository.rb +4 -2
- data/lib/reek/spec.rb +9 -3
- data/lib/reek/spec/should_reek.rb +2 -2
- data/lib/reek/spec/should_reek_of.rb +1 -1
- data/lib/reek/spec/should_reek_only_of.rb +2 -2
- data/lib/reek/version.rb +1 -1
- data/reek.gemspec +3 -1
- data/spec/gem/updates_spec.rb +1 -1
- data/spec/gem/yard_spec.rb +1 -1
- data/spec/matchers/smell_of_matcher.rb +53 -19
- data/spec/reek/cli/help_command_spec.rb +2 -2
- data/spec/reek/cli/reek_command_spec.rb +6 -6
- data/spec/reek/cli/report_spec.rb +6 -6
- data/spec/reek/cli/version_command_spec.rb +2 -2
- data/spec/reek/cli/yaml_command_spec.rb +2 -2
- data/spec/reek/core/code_context_spec.rb +4 -4
- data/spec/reek/core/code_parser_spec.rb +2 -2
- data/spec/reek/core/config_spec.rb +4 -4
- data/spec/reek/core/method_context_spec.rb +3 -3
- data/spec/reek/core/module_context_spec.rb +3 -3
- data/spec/reek/core/object_refs_spec.rb +3 -3
- data/spec/reek/core/singleton_method_context_spec.rb +4 -4
- data/spec/reek/core/smell_configuration_spec.rb +2 -2
- data/spec/reek/core/stop_context_spec.rb +2 -2
- data/spec/reek/core/warning_collector_spec.rb +3 -3
- data/spec/reek/examiner_spec.rb +13 -4
- data/spec/reek/smell_warning_spec.rb +2 -2
- data/spec/reek/smells/attribute_spec.rb +4 -4
- data/spec/reek/smells/boolean_parameter_spec.rb +3 -3
- data/spec/reek/smells/class_variable_spec.rb +4 -4
- data/spec/reek/smells/{control_couple_spec.rb → control_parameter_spec.rb} +10 -10
- data/spec/reek/smells/data_clump_spec.rb +3 -3
- data/spec/reek/smells/{duplication_spec.rb → duplicate_method_call_spec.rb} +42 -26
- data/spec/reek/smells/feature_envy_spec.rb +3 -3
- data/spec/reek/smells/irresponsible_module_spec.rb +3 -3
- data/spec/reek/smells/long_parameter_list_spec.rb +3 -3
- data/spec/reek/smells/long_yield_list_spec.rb +3 -3
- data/spec/reek/smells/nested_iterators_spec.rb +42 -4
- data/spec/reek/smells/nil_check_spec.rb +23 -11
- data/spec/reek/smells/{simulated_polymorphism_spec.rb → repeated_conditional_spec.rb} +6 -6
- data/spec/reek/smells/smell_detector_shared.rb +2 -2
- data/spec/reek/smells/too_many_instance_variables_spec.rb +62 -0
- data/spec/reek/smells/{large_class_spec.rb → too_many_methods_spec.rb} +11 -56
- data/spec/reek/smells/{long_method_spec.rb → too_many_statements_spec.rb} +17 -17
- data/spec/reek/smells/uncommunicative_method_name_spec.rb +5 -5
- data/spec/reek/smells/uncommunicative_module_name_spec.rb +5 -5
- data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +4 -4
- data/spec/reek/smells/uncommunicative_variable_name_spec.rb +5 -5
- data/spec/reek/smells/unused_parameters_spec.rb +19 -4
- data/spec/reek/smells/utility_function_spec.rb +3 -3
- data/spec/reek/source/code_comment_spec.rb +2 -2
- data/spec/reek/source/object_source_spec.rb +1 -1
- data/spec/reek/source/reference_collector_spec.rb +2 -2
- data/spec/reek/source/sexp_formatter_spec.rb +2 -2
- data/spec/reek/source/source_code_spec.rb +2 -2
- data/spec/reek/source/tree_dresser_spec.rb +2 -2
- data/spec/reek/spec/should_reek_of_spec.rb +2 -2
- data/spec/reek/spec/should_reek_only_of_spec.rb +2 -2
- data/spec/reek/spec/should_reek_spec.rb +2 -2
- data/spec/samples/all_but_one_masked/masked.reek +1 -1
- data/spec/samples/clean_due_to_masking/masked.reek +1 -1
- data/spec/samples/config/allow_duplication.reek +2 -2
- data/spec/samples/inline_config/dirty.rb +2 -2
- data/spec/samples/mask_some/some.reek +1 -1
- data/spec/samples/masked_by_dotfile/dirty.rb +8 -0
- data/spec/samples/not_quite_masked/smelly.rb +3 -0
- data/spec/samples/overrides/masked/lower.reek +1 -1
- data/spec/samples/overrides/upper.reek +1 -1
- data/spec/spec_helper.rb +4 -9
- data/tasks/test.rake +0 -2
- metadata +253 -263
- data/lib/reek/smells/large_class.rb +0 -87
- data/lib/xp.reek +0 -66
- data/spec/gem/manifest_spec.rb +0 -22
- data/spec/spec.opts +0 -1
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'reek/smells/smell_detector'
|
2
|
+
require 'reek/smell_warning'
|
3
3
|
|
4
4
|
module Reek
|
5
5
|
module Smells
|
@@ -9,7 +9,7 @@ module Reek
|
|
9
9
|
# or when two fragments of code have nearly identical effects
|
10
10
|
# at some conceptual level.
|
11
11
|
#
|
12
|
-
#
|
12
|
+
# +DuplicateMethodCall+ checks for repeated identical method calls
|
13
13
|
# within any one method definition. For example, the following method
|
14
14
|
# will report a warning:
|
15
15
|
#
|
@@ -17,10 +17,11 @@ module Reek
|
|
17
17
|
# @other.thing + @other.thing
|
18
18
|
# end
|
19
19
|
#
|
20
|
-
class
|
20
|
+
class DuplicateMethodCall < SmellDetector
|
21
|
+
|
22
|
+
SMELL_CLASS = 'Duplication'
|
23
|
+
SMELL_SUBCLASS = self.name.split(/::/)[-1]
|
21
24
|
|
22
|
-
SMELL_CLASS = self.name.split(/::/)[-1]
|
23
|
-
SMELL_SUBCLASS = 'DuplicateMethodCall'
|
24
25
|
CALL_KEY = 'call'
|
25
26
|
OCCURRENCES_KEY = 'occurrences'
|
26
27
|
|
@@ -44,10 +45,6 @@ module Reek
|
|
44
45
|
)
|
45
46
|
end
|
46
47
|
|
47
|
-
def initialize(source, config = Duplication.default_config)
|
48
|
-
super(source, config)
|
49
|
-
end
|
50
|
-
|
51
48
|
#
|
52
49
|
# Looks for duplicate calls within the body of the method +ctx+.
|
53
50
|
#
|
@@ -76,12 +73,13 @@ module Reek
|
|
76
73
|
result = Hash.new {|hash,key| hash[key] = []}
|
77
74
|
method_ctx.local_nodes(:call) do |call_node|
|
78
75
|
next if call_node.method_name == :new
|
76
|
+
next if call_node.receiver.nil? && call_node.args.empty?
|
79
77
|
result[call_node].push(call_node)
|
80
78
|
end
|
81
79
|
method_ctx.local_nodes(:attrasgn) do |asgn_node|
|
82
80
|
result[asgn_node].push(asgn_node) unless asgn_node.args.nil?
|
83
81
|
end
|
84
|
-
result
|
82
|
+
result.to_a.sort_by {|call_exp, _| call_exp.format_ruby}
|
85
83
|
end
|
86
84
|
|
87
85
|
def allow_calls?(method)
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'reek/smells/smell_detector'
|
2
|
+
require 'reek/smell_warning'
|
3
3
|
|
4
4
|
module Reek
|
5
5
|
module Smells
|
@@ -1,5 +1,6 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'reek/smells/smell_detector'
|
2
|
+
require 'reek/smell_warning'
|
3
|
+
require 'reek/source/code_comment'
|
3
4
|
|
4
5
|
module Reek
|
5
6
|
module Smells
|
@@ -1,6 +1,6 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require 'reek/smells/smell_detector'
|
2
|
+
require 'reek/smell_warning'
|
3
|
+
require 'reek/core/smell_configuration'
|
4
4
|
|
5
5
|
module Reek
|
6
6
|
module Smells
|
@@ -15,8 +15,8 @@ module Reek
|
|
15
15
|
#
|
16
16
|
class LongParameterList < SmellDetector
|
17
17
|
|
18
|
-
SMELL_CLASS =
|
19
|
-
SMELL_SUBCLASS =
|
18
|
+
SMELL_CLASS = 'LongParameterList'
|
19
|
+
SMELL_SUBCLASS = self.name.split(/::/)[-1]
|
20
20
|
|
21
21
|
PARAMETER_COUNT_KEY = 'parameter_count'
|
22
22
|
|
@@ -33,14 +33,10 @@ module Reek
|
|
33
33
|
MAX_ALLOWED_PARAMS_KEY => DEFAULT_MAX_ALLOWED_PARAMS,
|
34
34
|
Core::SmellConfiguration::OVERRIDES_KEY => {
|
35
35
|
"initialize" => {MAX_ALLOWED_PARAMS_KEY => 5}
|
36
|
-
|
36
|
+
}
|
37
37
|
)
|
38
38
|
end
|
39
39
|
|
40
|
-
def initialize(source, config = LongParameterList.default_config)
|
41
|
-
super(source, config)
|
42
|
-
end
|
43
|
-
|
44
40
|
#
|
45
41
|
# Checks the number of parameters in the given method.
|
46
42
|
#
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'reek/smells/smell_detector'
|
2
|
+
require 'reek/smell_warning'
|
3
3
|
|
4
4
|
module Reek
|
5
5
|
module Smells
|
@@ -10,8 +10,8 @@ module Reek
|
|
10
10
|
#
|
11
11
|
class LongYieldList < SmellDetector
|
12
12
|
|
13
|
-
SMELL_SUBCLASS = self.name.split(/::/)[-1]
|
14
13
|
SMELL_CLASS = 'LongParameterList'
|
14
|
+
SMELL_SUBCLASS = self.name.split(/::/)[-1]
|
15
15
|
|
16
16
|
# The name of the config field that sets the maximum number of
|
17
17
|
# parameters permitted in any method or block.
|
@@ -25,14 +25,10 @@ module Reek
|
|
25
25
|
|
26
26
|
def self.default_config
|
27
27
|
super.adopt(
|
28
|
-
|
28
|
+
MAX_ALLOWED_PARAMS_KEY => DEFAULT_MAX_ALLOWED_PARAMS
|
29
29
|
)
|
30
30
|
end
|
31
31
|
|
32
|
-
def initialize(source, config = LongYieldList.default_config)
|
33
|
-
super(source, config)
|
34
|
-
end
|
35
|
-
|
36
32
|
#
|
37
33
|
# Checks the number of parameters in the given scope.
|
38
34
|
#
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'reek/smells/smell_detector'
|
2
|
+
require 'reek/smell_warning'
|
3
3
|
|
4
4
|
module Reek
|
5
5
|
module Smells
|
@@ -35,53 +35,59 @@ module Reek
|
|
35
35
|
)
|
36
36
|
end
|
37
37
|
|
38
|
-
def initialize(source, config = NestedIterators.default_config)
|
39
|
-
super(source, config)
|
40
|
-
end
|
41
|
-
|
42
38
|
#
|
43
39
|
# Checks whether the given +block+ is inside another.
|
44
40
|
#
|
45
41
|
# @return [Array<SmellWarning>]
|
46
42
|
#
|
47
43
|
def examine_context(ctx)
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
SmellWarning.new(SMELL_CLASS, ctx.full_name, [iter[0].line],
|
44
|
+
exp, depth = *find_deepest_iterator(ctx)
|
45
|
+
|
46
|
+
if depth && depth > value(MAX_ALLOWED_NESTING_KEY, ctx, DEFAULT_MAX_ALLOWED_NESTING)
|
47
|
+
smell = SmellWarning.new(SMELL_CLASS, ctx.full_name, [exp.line],
|
53
48
|
"contains iterators nested #{depth} deep",
|
54
49
|
@source, SMELL_SUBCLASS,
|
55
50
|
{NESTING_DEPTH_KEY => depth})
|
51
|
+
[smell]
|
52
|
+
else
|
53
|
+
[]
|
56
54
|
end
|
57
55
|
# BUG: no longer reports nesting outside methods (eg. in Optparse)
|
58
56
|
end
|
59
57
|
|
60
58
|
private
|
61
59
|
|
62
|
-
def
|
63
|
-
|
64
|
-
|
65
|
-
|
60
|
+
def find_deepest_iterator(ctx)
|
61
|
+
@ignore_iterators = value(IGNORE_ITERATORS_KEY, ctx, DEFAULT_IGNORE_ITERATORS)
|
62
|
+
|
63
|
+
find_iters(ctx.exp, 1).sort_by {|item| item[1]}.last
|
66
64
|
end
|
67
65
|
|
68
|
-
def find_iters(exp, depth
|
69
|
-
exp.
|
66
|
+
def find_iters(exp, depth)
|
67
|
+
exp.map do |elem|
|
70
68
|
next unless Sexp === elem
|
71
69
|
case elem.first
|
72
70
|
when :iter
|
73
|
-
|
74
|
-
current = result.length
|
75
|
-
call = Source::SexpFormatter.format(elem.call)
|
76
|
-
ignored = @ignore_iterators.any? { |ignore| /#{ignore}/ === call }
|
77
|
-
find_iters([elem.block], depth + (ignored ? 0 : 1), result)
|
78
|
-
result << [elem, depth] if result.length == current unless ignored
|
71
|
+
find_iters_for_iter_node(elem, depth)
|
79
72
|
when :class, :defn, :defs, :module
|
80
73
|
next
|
81
74
|
else
|
82
|
-
find_iters(elem, depth
|
75
|
+
find_iters(elem, depth)
|
83
76
|
end
|
84
|
-
end
|
77
|
+
end.flatten(1).compact
|
78
|
+
end
|
79
|
+
|
80
|
+
def find_iters_for_iter_node(exp, depth)
|
81
|
+
ignored = ignored_iterator? exp
|
82
|
+
result = find_iters([exp.call], depth) +
|
83
|
+
find_iters([exp.block], depth + (ignored ? 0 : 1))
|
84
|
+
result << [exp, depth] unless ignored
|
85
|
+
result
|
86
|
+
end
|
87
|
+
|
88
|
+
def ignored_iterator?(exp)
|
89
|
+
name = exp.call.method_name.to_s
|
90
|
+
@ignore_iterators.any? { |pattern| /#{pattern}/ === name }
|
85
91
|
end
|
86
92
|
end
|
87
93
|
end
|
@@ -1,17 +1,15 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'reek/smells/smell_detector'
|
2
|
+
require 'reek/smell_warning'
|
3
3
|
|
4
4
|
module Reek
|
5
5
|
module Smells
|
6
6
|
|
7
|
+
# Checking for nil is a special kind of type check, and therefore a case of
|
8
|
+
# SimulatedPolymorphism.
|
7
9
|
class NilCheck < SmellDetector
|
8
10
|
|
9
|
-
SMELL_CLASS = '
|
10
|
-
SMELL_SUBCLASS =
|
11
|
-
|
12
|
-
def initialize(source, config = NilCheck.default_config)
|
13
|
-
super(source, config)
|
14
|
-
end
|
11
|
+
SMELL_CLASS = 'SimulatedPolymorphism'
|
12
|
+
SMELL_SUBCLASS = self.name.split(/::/)[-1]
|
15
13
|
|
16
14
|
def examine_context(ctx)
|
17
15
|
|
@@ -23,9 +21,9 @@ module Reek
|
|
23
21
|
smelly_nodes = smelly_calls + smelly_cases
|
24
22
|
|
25
23
|
smelly_nodes.map do |node|
|
26
|
-
SmellWarning.new(SMELL_CLASS, ctx.full_name, node.line,
|
24
|
+
SmellWarning.new(SMELL_CLASS, ctx.full_name, Array(node.line),
|
27
25
|
"performs a nil-check.",
|
28
|
-
@source, SMELL_SUBCLASS
|
26
|
+
@source, SMELL_SUBCLASS)
|
29
27
|
end
|
30
28
|
end
|
31
29
|
|
@@ -66,13 +64,14 @@ module Reek
|
|
66
64
|
|
67
65
|
class CaseNodeFinder < NodeFinder
|
68
66
|
CASE_NIL_NODE = Sexp.new(:array, SEXP_NIL)
|
67
|
+
|
69
68
|
def initialize(ctx)
|
70
69
|
super(ctx, :when)
|
71
|
-
|
70
|
+
end
|
72
71
|
|
73
72
|
def smelly
|
74
73
|
@nodes.select{ |when_node|
|
75
|
-
nil_chk?(when_node)
|
74
|
+
nil_chk?(when_node)
|
76
75
|
}
|
77
76
|
end
|
78
77
|
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'reek/smells/smell_detector'
|
2
|
+
require 'reek/smell_warning'
|
3
3
|
|
4
4
|
module Reek
|
5
5
|
module Smells
|
@@ -18,13 +18,13 @@ module Reek
|
|
18
18
|
# classes to change. Tests for the type of an object may indicate that the
|
19
19
|
# abstraction represented by that type is not completely defined (or understood).
|
20
20
|
#
|
21
|
-
#
|
21
|
+
# +RepeatedConditional+ checks for multiple conditionals
|
22
22
|
# testing the same value throughout a single class.
|
23
23
|
#
|
24
|
-
class
|
24
|
+
class RepeatedConditional < SmellDetector
|
25
25
|
|
26
|
-
SMELL_CLASS =
|
27
|
-
SMELL_SUBCLASS =
|
26
|
+
SMELL_CLASS = 'SimulatedPolymorphism'
|
27
|
+
SMELL_SUBCLASS = self.name.split(/::/)[-1]
|
28
28
|
|
29
29
|
def self.contexts # :nodoc:
|
30
30
|
[:class]
|
@@ -40,10 +40,6 @@ module Reek
|
|
40
40
|
super.adopt(MAX_IDENTICAL_IFS_KEY => DEFAULT_MAX_IFS)
|
41
41
|
end
|
42
42
|
|
43
|
-
def initialize(source, config = SimulatedPolymorphism.default_config)
|
44
|
-
super(source, config)
|
45
|
-
end
|
46
|
-
|
47
43
|
#
|
48
44
|
# Checks the given class for multiple identical conditional tests.
|
49
45
|
#
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'set'
|
2
|
-
require
|
3
|
-
require
|
2
|
+
require 'reek/smell_warning'
|
3
|
+
require 'reek/core/smell_configuration'
|
4
4
|
|
5
5
|
module Reek
|
6
6
|
module Smells
|
@@ -9,9 +9,6 @@ module Reek
|
|
9
9
|
def self.default_config
|
10
10
|
super.adopt(EXCLUDE_KEY => ['initialize'])
|
11
11
|
end
|
12
|
-
def initialize(source, config = self.class.default_config)
|
13
|
-
super(source, config)
|
14
|
-
end
|
15
12
|
end
|
16
13
|
|
17
14
|
#
|
@@ -36,7 +33,7 @@ module Reek
|
|
36
33
|
def default_config
|
37
34
|
{
|
38
35
|
Core::SmellConfiguration::ENABLED_KEY => true,
|
39
|
-
EXCLUDE_KEY => DEFAULT_EXCLUDE_SET
|
36
|
+
EXCLUDE_KEY => DEFAULT_EXCLUDE_SET.dup
|
40
37
|
}
|
41
38
|
end
|
42
39
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'reek/smells/smell_detector'
|
2
|
+
require 'reek/smell_warning'
|
3
|
+
|
4
|
+
module Reek
|
5
|
+
module Smells
|
6
|
+
|
7
|
+
#
|
8
|
+
# A Large Class is a class or module that has a large number of
|
9
|
+
# instance variables, methods or lines of code.
|
10
|
+
#
|
11
|
+
# +TooManyInstanceVariables' reports classes having more than a
|
12
|
+
# configurable number of instance variables.
|
13
|
+
#
|
14
|
+
class TooManyInstanceVariables < SmellDetector
|
15
|
+
|
16
|
+
SMELL_CLASS = 'LargeClass'
|
17
|
+
SMELL_SUBCLASS = self.name.split(/::/)[-1]
|
18
|
+
IVAR_COUNT_KEY = 'ivar_count'
|
19
|
+
|
20
|
+
# The name of the config field that sets the maximum number of instance
|
21
|
+
# variables permitted in a class.
|
22
|
+
MAX_ALLOWED_IVARS_KEY = 'max_instance_variables'
|
23
|
+
|
24
|
+
DEFAULT_MAX_IVARS = 9
|
25
|
+
|
26
|
+
def self.contexts # :nodoc:
|
27
|
+
[:class]
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.default_config
|
31
|
+
super.adopt(
|
32
|
+
MAX_ALLOWED_IVARS_KEY => DEFAULT_MAX_IVARS,
|
33
|
+
EXCLUDE_KEY => []
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Checks +klass+ for too many instance variables.
|
39
|
+
#
|
40
|
+
# @return [Array<SmellWarning>]
|
41
|
+
#
|
42
|
+
def examine_context(ctx)
|
43
|
+
@max_allowed_ivars = value(MAX_ALLOWED_IVARS_KEY, ctx, DEFAULT_MAX_IVARS)
|
44
|
+
check_num_ivars(ctx)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def check_num_ivars(ctx) # :nodoc:
|
50
|
+
count = ctx.local_nodes(:iasgn).map {|iasgn| iasgn[1]}.uniq.length
|
51
|
+
return [] if count <= @max_allowed_ivars
|
52
|
+
smell = SmellWarning.new(SMELL_CLASS, ctx.full_name, [ctx.exp.line],
|
53
|
+
"has at least #{count} instance variables",
|
54
|
+
@source, SMELL_SUBCLASS,
|
55
|
+
{IVAR_COUNT_KEY => count})
|
56
|
+
[smell]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'reek/smells/smell_detector'
|
2
|
+
require 'reek/smell_warning'
|
3
|
+
|
4
|
+
module Reek
|
5
|
+
module Smells
|
6
|
+
|
7
|
+
#
|
8
|
+
# A Large Class is a class or module that has a large number of
|
9
|
+
# instance variables, methods or lines of code.
|
10
|
+
#
|
11
|
+
# +TooManyMethods+ reports classes having more than a configurable number
|
12
|
+
# of methods. The method count includes public, protected and private
|
13
|
+
# methods, and excludes methods inherited from superclasses or included
|
14
|
+
# modules.
|
15
|
+
#
|
16
|
+
class TooManyMethods < SmellDetector
|
17
|
+
|
18
|
+
SMELL_CLASS = 'LargeClass'
|
19
|
+
SMELL_SUBCLASS = self.name.split(/::/)[-1]
|
20
|
+
METHOD_COUNT_KEY = 'method_count'
|
21
|
+
|
22
|
+
# The name of the config field that sets the maximum number of methods
|
23
|
+
# permitted in a class.
|
24
|
+
MAX_ALLOWED_METHODS_KEY = 'max_methods'
|
25
|
+
|
26
|
+
DEFAULT_MAX_METHODS = 25
|
27
|
+
|
28
|
+
def self.contexts # :nodoc:
|
29
|
+
[:class]
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.default_config
|
33
|
+
super.adopt(
|
34
|
+
MAX_ALLOWED_METHODS_KEY => DEFAULT_MAX_METHODS,
|
35
|
+
EXCLUDE_KEY => []
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# Checks +ctx+ for too many methods
|
41
|
+
#
|
42
|
+
# @return [Array<SmellWarning>]
|
43
|
+
#
|
44
|
+
def examine_context(ctx)
|
45
|
+
@max_allowed_methods = value(MAX_ALLOWED_METHODS_KEY, ctx, DEFAULT_MAX_METHODS)
|
46
|
+
check_num_methods(ctx)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def check_num_methods(ctx) # :nodoc:
|
52
|
+
actual = ctx.local_nodes(:defn).length
|
53
|
+
return [] if actual <= @max_allowed_methods
|
54
|
+
smell = SmellWarning.new(SMELL_CLASS, ctx.full_name, [ctx.exp.line],
|
55
|
+
"has at least #{actual} methods",
|
56
|
+
@source, SMELL_SUBCLASS,
|
57
|
+
{METHOD_COUNT_KEY => actual})
|
58
|
+
[smell]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|