reek 1.3.1 → 1.3.2
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.
- 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
|