reek 5.4.0 → 6.0.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +27 -6
- data/.rubocop_todo.yml +4 -4
- data/.simplecov +1 -0
- data/.travis.yml +13 -11
- data/CHANGELOG.md +27 -1
- data/Dockerfile +2 -1
- data/Gemfile +15 -17
- data/README.md +15 -11
- data/bin/code_climate_reek +12 -2
- data/docs/Attribute.md +1 -1
- data/docs/Control-Couple.md +1 -1
- data/docs/Nil-Check.md +4 -1
- data/features/command_line_interface/options.feature +2 -3
- data/features/reports/codeclimate.feature +2 -2
- data/features/reports/json.feature +3 -3
- data/features/reports/reports.feature +4 -4
- data/features/reports/yaml.feature +3 -3
- data/features/step_definitions/reek_steps.rb +4 -0
- data/features/support/env.rb +1 -2
- data/lib/reek/ast/sexp_extensions/arguments.rb +11 -0
- data/lib/reek/cli/command/todo_list_command.rb +7 -2
- data/lib/reek/cli/options.rb +2 -2
- data/lib/reek/code_comment.rb +45 -38
- data/lib/reek/configuration/configuration_converter.rb +2 -2
- data/lib/reek/configuration/directory_directives.rb +7 -1
- data/lib/reek/errors/legacy_comment_separator_error.rb +36 -0
- data/lib/reek/examiner.rb +3 -3
- data/lib/reek/report.rb +5 -7
- data/lib/reek/report/code_climate/code_climate_report.rb +2 -1
- data/lib/reek/report/simple_warning_formatter.rb +0 -7
- data/lib/reek/report/text_report.rb +2 -2
- data/lib/reek/smell_detectors/base_detector.rb +2 -10
- data/lib/reek/smell_detectors/data_clump.rb +23 -56
- data/lib/reek/smell_detectors/nil_check.rb +1 -12
- data/lib/reek/smell_detectors/subclassed_from_core_class.rb +3 -7
- data/lib/reek/smell_detectors/too_many_constants.rb +1 -1
- data/lib/reek/smell_warning.rb +18 -14
- data/lib/reek/source/source_code.rb +3 -2
- data/lib/reek/spec/smell_matcher.rb +2 -1
- data/lib/reek/version.rb +1 -1
- data/reek.gemspec +5 -6
- data/spec/reek/ast/sexp_extensions_spec.rb +15 -33
- data/spec/reek/cli/application_spec.rb +1 -1
- data/spec/reek/code_comment_spec.rb +41 -42
- data/spec/reek/configuration/directory_directives_spec.rb +6 -0
- data/spec/reek/context_builder_spec.rb +110 -113
- data/spec/reek/examiner_spec.rb +1 -0
- data/spec/reek/report/code_climate/code_climate_configuration_spec.rb +1 -3
- data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +26 -26
- data/spec/reek/report/code_climate/code_climate_formatter_spec.rb +6 -6
- data/spec/reek/report/code_climate/code_climate_report_spec.rb +1 -1
- data/spec/reek/report/json_report_spec.rb +1 -1
- data/spec/reek/report/location_formatter_spec.rb +3 -3
- data/spec/reek/report/text_report_spec.rb +1 -7
- data/spec/reek/report/yaml_report_spec.rb +1 -1
- data/spec/reek/smell_configuration_spec.rb +2 -0
- data/spec/reek/smell_detectors/base_detector_spec.rb +3 -16
- data/spec/reek/smell_detectors/data_clump_spec.rb +14 -0
- data/spec/reek/smell_detectors/missing_safe_method_spec.rb +8 -2
- data/spec/reek/smell_detectors/nil_check_spec.rb +3 -3
- data/spec/reek/smell_warning_spec.rb +17 -28
- data/spec/reek/source/source_code_spec.rb +13 -0
- data/spec/reek/spec/should_reek_of_spec.rb +0 -1
- data/spec/reek/spec/should_reek_only_of_spec.rb +6 -13
- data/spec/reek/spec/smell_matcher_spec.rb +1 -2
- data/spec/spec_helper.rb +20 -6
- metadata +11 -26
- data/spec/factories/factories.rb +0 -48
@@ -19,6 +19,7 @@ module Reek
|
|
19
19
|
# @quality :reek:TooManyMethods { max_methods: 18 }
|
20
20
|
class BaseDetector
|
21
21
|
attr_reader :config
|
22
|
+
|
22
23
|
# The name of the config field that lists the names of code contexts
|
23
24
|
# that should not be checked. Add this field to the config for each
|
24
25
|
# smell that should ignore this code element.
|
@@ -79,7 +80,7 @@ module Reek
|
|
79
80
|
end
|
80
81
|
|
81
82
|
def smell_warning(**options)
|
82
|
-
SmellWarning.new(
|
83
|
+
SmellWarning.new(smell_type,
|
83
84
|
source: expression.source,
|
84
85
|
context: context.full_name,
|
85
86
|
lines: options.fetch(:lines),
|
@@ -121,15 +122,6 @@ module Reek
|
|
121
122
|
@descendants ||= []
|
122
123
|
end
|
123
124
|
|
124
|
-
#
|
125
|
-
# @param detector [String] the detector in question, e.g. 'DuplicateMethodCall'
|
126
|
-
# @return [Boolean]
|
127
|
-
#
|
128
|
-
def valid_detector?(detector)
|
129
|
-
descendants.map { |descendant| descendant.to_s.split('::').last }.
|
130
|
-
include?(detector)
|
131
|
-
end
|
132
|
-
|
133
125
|
#
|
134
126
|
# Transform a detector name to the corresponding constant.
|
135
127
|
# Note that we assume a valid name - exceptions are not handled here.
|
@@ -51,7 +51,7 @@ module Reek
|
|
51
51
|
# @return [Array<SmellWarning>]
|
52
52
|
#
|
53
53
|
def sniff
|
54
|
-
|
54
|
+
clumps.map do |clump, methods|
|
55
55
|
methods_length = methods.length
|
56
56
|
smell_warning(
|
57
57
|
lines: methods.map(&:line),
|
@@ -72,72 +72,39 @@ module Reek
|
|
72
72
|
private
|
73
73
|
|
74
74
|
def max_copies
|
75
|
-
value(MAX_COPIES_KEY, context)
|
75
|
+
@max_copies ||= value(MAX_COPIES_KEY, context)
|
76
76
|
end
|
77
77
|
|
78
78
|
def min_clump_size
|
79
|
-
value(MIN_CLUMP_SIZE_KEY, context)
|
79
|
+
@min_clump_size ||= value(MIN_CLUMP_SIZE_KEY, context)
|
80
80
|
end
|
81
|
-
end
|
82
|
-
end
|
83
81
|
|
84
|
-
|
85
|
-
|
86
|
-
class MethodGroup
|
87
|
-
def initialize(ctx, min_clump_size, max_copies)
|
88
|
-
@min_clump_size = min_clump_size
|
89
|
-
@max_copies = max_copies
|
90
|
-
@candidate_methods = ctx.node_instance_methods.map do |defn_node|
|
91
|
-
CandidateMethod.new(defn_node)
|
82
|
+
def candidate_methods
|
83
|
+
@candidate_methods ||= context.node_instance_methods
|
92
84
|
end
|
93
|
-
end
|
94
85
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
end
|
102
|
-
|
103
|
-
# @quality :reek:UtilityFunction
|
104
|
-
def common_argument_names_for(methods)
|
105
|
-
methods.map(&:arg_names).inject(:&)
|
106
|
-
end
|
107
|
-
|
108
|
-
def methods_containing_clump(clump)
|
109
|
-
candidate_methods.select { |method| clump & method.arg_names == clump }
|
110
|
-
end
|
111
|
-
|
112
|
-
def clumps
|
113
|
-
candidate_clumps.map do |clump|
|
114
|
-
[clump, methods_containing_clump(clump)]
|
86
|
+
def candidate_clumps
|
87
|
+
candidate_methods.each_cons(max_copies + 1).map do |methods|
|
88
|
+
common_argument_names_for(methods)
|
89
|
+
end.select do |clump|
|
90
|
+
clump.length >= min_clump_size
|
91
|
+
end.uniq
|
115
92
|
end
|
116
|
-
end
|
117
|
-
|
118
|
-
private
|
119
93
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
# @private
|
125
|
-
class CandidateMethod
|
126
|
-
extend Forwardable
|
127
|
-
|
128
|
-
def_delegators :defn, :line, :name
|
94
|
+
# @quality :reek:UtilityFunction
|
95
|
+
def common_argument_names_for(methods)
|
96
|
+
methods.map(&:arg_names).inject(:&).compact.sort
|
97
|
+
end
|
129
98
|
|
130
|
-
|
131
|
-
|
132
|
-
|
99
|
+
def methods_containing_clump(clump)
|
100
|
+
candidate_methods.select { |method| clump & method.arg_names == clump }
|
101
|
+
end
|
133
102
|
|
134
|
-
|
135
|
-
|
136
|
-
|
103
|
+
def clumps
|
104
|
+
candidate_clumps.map do |clump|
|
105
|
+
[clump, methods_containing_clump(clump)]
|
106
|
+
end
|
107
|
+
end
|
137
108
|
end
|
138
|
-
|
139
|
-
private
|
140
|
-
|
141
|
-
attr_reader :defn
|
142
109
|
end
|
143
110
|
end
|
@@ -24,8 +24,7 @@ module Reek
|
|
24
24
|
|
25
25
|
def detect_nodes
|
26
26
|
finders = [NodeFinder.new(context, :send, NilCallNodeDetector),
|
27
|
-
NodeFinder.new(context, :when, NilWhenNodeDetector)
|
28
|
-
NodeFinder.new(context, :csend, SafeNavigationNodeDetector)]
|
27
|
+
NodeFinder.new(context, :when, NilWhenNodeDetector)]
|
29
28
|
finders.flat_map(&:smelly_nodes)
|
30
29
|
end
|
31
30
|
|
@@ -88,16 +87,6 @@ module Reek
|
|
88
87
|
node.condition_list.any? { |it| it.type == :nil }
|
89
88
|
end
|
90
89
|
end
|
91
|
-
|
92
|
-
# Detect safe navigation. Returns true for all nodes, since all :csend
|
93
|
-
# nodes are considered smelly.
|
94
|
-
module SafeNavigationNodeDetector
|
95
|
-
module_function
|
96
|
-
|
97
|
-
def detect(_node)
|
98
|
-
true
|
99
|
-
end
|
100
|
-
end
|
101
90
|
end
|
102
91
|
end
|
103
92
|
end
|
@@ -43,13 +43,9 @@ module Reek
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def build_smell_warning(ancestor_name)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
parameters: { ancestor: ancestor_name }
|
50
|
-
}
|
51
|
-
|
52
|
-
smell_warning(smell_attributes)
|
46
|
+
smell_warning(lines: [source_line],
|
47
|
+
message: "inherits from core class '#{ancestor_name}'",
|
48
|
+
parameters: { ancestor: ancestor_name })
|
53
49
|
end
|
54
50
|
end
|
55
51
|
end
|
@@ -35,7 +35,7 @@ module Reek
|
|
35
35
|
# @return [Array<SmellWarning>]
|
36
36
|
#
|
37
37
|
def sniff
|
38
|
-
count = context.local_nodes(:casgn).
|
38
|
+
count = context.local_nodes(:casgn).count { |it| !it.defines_module? }
|
39
39
|
|
40
40
|
return [] if count <= max_allowed_constants
|
41
41
|
|
data/lib/reek/smell_warning.rb
CHANGED
@@ -15,22 +15,30 @@ module Reek
|
|
15
15
|
extend Forwardable
|
16
16
|
|
17
17
|
# @public
|
18
|
-
attr_reader :context, :lines, :message, :parameters, :
|
19
|
-
|
20
|
-
|
18
|
+
attr_reader :context, :lines, :message, :parameters, :smell_type, :source
|
19
|
+
|
20
|
+
# @param smell_type [String] type of detected smell; corresponds to
|
21
|
+
# detector#smell_type
|
22
|
+
# @param context [String] name of the context in which the smell occured
|
23
|
+
# @param lines [Array<Integer>] list of lines on which the smell occured
|
24
|
+
# @param message [String] text describing the smell in more detail
|
25
|
+
# @param source [String] name of the source (e.g., the file name) in which
|
26
|
+
# the smell occured
|
27
|
+
# @param parameters [Hash] smell-specific parameters
|
28
|
+
#
|
21
29
|
# @note When using Reek's public API, you should not create SmellWarning
|
22
30
|
# objects yourself. This is why the initializer is not part of the
|
23
31
|
# public API.
|
24
32
|
#
|
25
33
|
# @quality :reek:LongParameterList { max_params: 6 }
|
26
|
-
def initialize(
|
34
|
+
def initialize(smell_type, context: '', lines:, message:,
|
27
35
|
source:, parameters: {})
|
28
|
-
@
|
29
|
-
@source
|
30
|
-
@context
|
31
|
-
@lines
|
32
|
-
@message
|
33
|
-
@parameters
|
36
|
+
@smell_type = smell_type
|
37
|
+
@source = source
|
38
|
+
@context = context.to_s
|
39
|
+
@lines = lines
|
40
|
+
@message = message
|
41
|
+
@parameters = parameters
|
34
42
|
|
35
43
|
freeze
|
36
44
|
end
|
@@ -64,10 +72,6 @@ module Reek
|
|
64
72
|
"#{smell_type}: #{context} #{message}"
|
65
73
|
end
|
66
74
|
|
67
|
-
def smell_class
|
68
|
-
smell_detector.class
|
69
|
-
end
|
70
|
-
|
71
75
|
def explanatory_link
|
72
76
|
DocumentationLink.build(smell_type)
|
73
77
|
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative '../cli/silencer'
|
4
|
-
|
4
|
+
# Silence Parser's warnings about Ruby micro version differences
|
5
|
+
Reek::CLI::Silencer.silently { require 'parser/current' }
|
5
6
|
require_relative '../tree_dresser'
|
6
7
|
require_relative '../ast/node'
|
7
8
|
require_relative '../ast/builder'
|
@@ -53,7 +54,7 @@ module Reek
|
|
53
54
|
end
|
54
55
|
|
55
56
|
def self.default_parser
|
56
|
-
Parser::
|
57
|
+
Parser::CurrentRuby.new(AST::Builder.new).tap do |parser|
|
57
58
|
diagnostics = parser.diagnostics
|
58
59
|
diagnostics.all_errors_are_fatal = true
|
59
60
|
diagnostics.ignore_warnings = true
|
@@ -43,8 +43,9 @@ module Reek
|
|
43
43
|
raise ArgumentError, "The attribute '#{extra_keys.first}' is not available for comparison"
|
44
44
|
end
|
45
45
|
|
46
|
+
# :reek:FeatureEnvy
|
46
47
|
def common_parameters_equal?(other_parameters)
|
47
|
-
smell_warning.parameters.
|
48
|
+
smell_warning.parameters.values_at(*other_parameters.keys) == other_parameters.values
|
48
49
|
end
|
49
50
|
|
50
51
|
def common_attributes_equal?(attributes)
|
data/lib/reek/version.rb
CHANGED
data/reek.gemspec
CHANGED
@@ -16,12 +16,11 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.executables = s.files.grep(%r{^bin/}).map { |path| File.basename(path) }
|
17
17
|
s.homepage = 'https://github.com/troessner/reek'
|
18
18
|
s.rdoc_options = %w(--main README.md -x assets/|bin/|config/|features/|spec/|tasks/)
|
19
|
-
s.required_ruby_version = '>= 2.
|
19
|
+
s.required_ruby_version = '>= 2.4.0'
|
20
20
|
s.summary = 'Code smell detector for Ruby'
|
21
21
|
|
22
|
-
s.add_runtime_dependency '
|
23
|
-
s.add_runtime_dependency '
|
24
|
-
s.add_runtime_dependency '
|
25
|
-
s.add_runtime_dependency '
|
26
|
-
s.add_runtime_dependency 'rainbow', '>= 2.0', '< 4.0'
|
22
|
+
s.add_runtime_dependency 'kwalify', '~> 0.7.0'
|
23
|
+
s.add_runtime_dependency 'parser', '< 2.8', '>= 2.5.0.0', '!= 2.5.1.1'
|
24
|
+
s.add_runtime_dependency 'psych', '~> 3.1.0'
|
25
|
+
s.add_runtime_dependency 'rainbow', '>= 2.0', '< 4.0'
|
27
26
|
end
|
@@ -443,73 +443,55 @@ end
|
|
443
443
|
|
444
444
|
RSpec.describe Reek::AST::SexpExtensions::CasgnNode do
|
445
445
|
describe '#defines_module?' do
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
expect(exp).not_to be_defines_module
|
450
|
-
end
|
446
|
+
it 'is false for single assignment' do
|
447
|
+
exp = sexp(:casgn, nil, :Foo)
|
448
|
+
expect(exp).not_to be_defines_module
|
451
449
|
end
|
452
450
|
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
expect(exp).not_to be_defines_module
|
457
|
-
end
|
451
|
+
it 'is false for implicit receiver to new' do
|
452
|
+
exp = sexp(:casgn, nil, :Foo, sexp(:send, nil, :new))
|
453
|
+
expect(exp).not_to be_defines_module
|
458
454
|
end
|
459
455
|
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
expect(exp).to be_defines_module
|
465
|
-
end
|
456
|
+
it 'is true for explicit receiver to new' do
|
457
|
+
exp = Reek::Source::SourceCode.from('Foo = Class.new(Bar)').syntax_tree
|
458
|
+
expect(exp).to be_defines_module
|
466
459
|
end
|
467
460
|
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
expect(exp).not_to be_defines_module
|
473
|
-
end
|
461
|
+
it 'is false for assigning a lambda to a constant' do
|
462
|
+
exp = Reek::Source::SourceCode.from('C = ->{}').syntax_tree
|
463
|
+
expect(exp).not_to be_defines_module
|
474
464
|
end
|
475
465
|
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
expect(exp).not_to be_defines_module
|
481
|
-
end
|
466
|
+
it 'is false for assigning a string to a constant' do
|
467
|
+
exp = Reek::Source::SourceCode.from('C = "hello"').syntax_tree
|
468
|
+
expect(exp).not_to be_defines_module
|
482
469
|
end
|
483
470
|
end
|
484
471
|
|
485
472
|
describe '#superclass' do
|
486
473
|
it 'returns the superclass from the class definition' do
|
487
474
|
exp = Reek::Source::SourceCode.from('Foo = Class.new(Bar)').syntax_tree
|
488
|
-
|
489
475
|
expect(exp.superclass).to eq sexp(:const, nil, :Bar)
|
490
476
|
end
|
491
477
|
|
492
478
|
it 'returns nil in case of no class definition' do
|
493
479
|
exp = Reek::Source::SourceCode.from('Foo = 23').syntax_tree
|
494
|
-
|
495
480
|
expect(exp.superclass).to be_nil
|
496
481
|
end
|
497
482
|
|
498
483
|
it 'returns nil in case of no superclass' do
|
499
484
|
exp = Reek::Source::SourceCode.from('Foo = Class.new').syntax_tree
|
500
|
-
|
501
485
|
expect(exp.superclass).to be_nil
|
502
486
|
end
|
503
487
|
|
504
488
|
it 'returns nothing for a class definition using Struct.new' do
|
505
489
|
exp = Reek::Source::SourceCode.from('Foo = Struct.new("Bar")').syntax_tree
|
506
|
-
|
507
490
|
expect(exp.superclass).to be_nil
|
508
491
|
end
|
509
492
|
|
510
493
|
it 'returns nothing for a constant assigned with a bare method call' do
|
511
494
|
exp = Reek::Source::SourceCode.from('Foo = foo("Bar")').syntax_tree
|
512
|
-
|
513
495
|
expect(exp.superclass).to be_nil
|
514
496
|
end
|
515
497
|
end
|
@@ -9,7 +9,7 @@ RSpec.describe Reek::CLI::Application do
|
|
9
9
|
described_class.new ['--foo']
|
10
10
|
end
|
11
11
|
end
|
12
|
-
expect(call).to raise_error(SystemExit) do |error|
|
12
|
+
expect(&call).to raise_error(SystemExit) do |error|
|
13
13
|
expect(error.status).to eq Reek::CLI::Status::DEFAULT_ERROR_EXIT_CODE
|
14
14
|
end
|
15
15
|
end
|
@@ -3,7 +3,7 @@ require_lib 'reek/code_comment'
|
|
3
3
|
|
4
4
|
RSpec.describe Reek::CodeComment do
|
5
5
|
context 'with an empty comment' do
|
6
|
-
let(:comment) {
|
6
|
+
let(:comment) { build_code_comment(comment: '') }
|
7
7
|
|
8
8
|
it 'is not descriptive' do
|
9
9
|
expect(comment).not_to be_descriptive
|
@@ -16,77 +16,63 @@ RSpec.describe Reek::CodeComment do
|
|
16
16
|
|
17
17
|
describe '#descriptive' do
|
18
18
|
it 'rejects an empty comment' do
|
19
|
-
comment =
|
19
|
+
comment = build_code_comment(comment: '#')
|
20
20
|
expect(comment).not_to be_descriptive
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'rejects a 1-word comment' do
|
24
|
-
comment =
|
24
|
+
comment = build_code_comment(comment: "# alpha\n# ")
|
25
25
|
expect(comment).not_to be_descriptive
|
26
26
|
end
|
27
27
|
|
28
28
|
it 'accepts a 2-word comment' do
|
29
|
-
comment =
|
29
|
+
comment = build_code_comment(comment: '# alpha bravo ')
|
30
30
|
expect(comment).to be_descriptive
|
31
31
|
end
|
32
32
|
|
33
33
|
it 'accepts a multi-word comment' do
|
34
|
-
comment =
|
34
|
+
comment = build_code_comment(comment: "# alpha bravo \n# charlie \n # delta ")
|
35
35
|
expect(comment).to be_descriptive
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
39
|
describe 'good comment config' do
|
40
40
|
it 'parses hashed options' do
|
41
|
-
comment = '# :reek:DuplicateMethodCall {
|
42
|
-
config =
|
43
|
-
comment: comment).config
|
41
|
+
comment = '# :reek:DuplicateMethodCall { max_calls: 3 }'
|
42
|
+
config = build_code_comment(comment: comment).config
|
44
43
|
|
45
44
|
expect(config).to include('DuplicateMethodCall')
|
46
|
-
expect(config['DuplicateMethodCall']).to
|
47
|
-
expect(config['DuplicateMethodCall']['
|
48
|
-
end
|
49
|
-
|
50
|
-
it "supports hashed options with the legacy separator ':' after the smell detector" do
|
51
|
-
comment = '# :reek:DuplicateMethodCall: { enabled: false }'
|
52
|
-
config = build(:code_comment,
|
53
|
-
comment: comment).config
|
54
|
-
|
55
|
-
expect(config).to include('DuplicateMethodCall')
|
56
|
-
expect(config['DuplicateMethodCall']).to include('enabled')
|
57
|
-
expect(config['DuplicateMethodCall']['enabled']).to be_falsey
|
45
|
+
expect(config['DuplicateMethodCall']).to have_key 'max_calls'
|
46
|
+
expect(config['DuplicateMethodCall']['max_calls']).to eq 3
|
58
47
|
end
|
59
48
|
|
60
49
|
it 'parses multiple hashed options' do
|
61
50
|
comment = <<-RUBY
|
62
|
-
# :reek:DuplicateMethodCall {
|
51
|
+
# :reek:DuplicateMethodCall { max_calls: 3 }
|
63
52
|
# :reek:NestedIterators { enabled: true }
|
64
53
|
RUBY
|
65
|
-
config =
|
54
|
+
config = build_code_comment(comment: comment).config
|
66
55
|
|
67
56
|
expect(config).to include('DuplicateMethodCall', 'NestedIterators')
|
68
|
-
expect(config['DuplicateMethodCall']).to
|
69
|
-
expect(config['DuplicateMethodCall']['enabled']).to be_falsey
|
70
|
-
expect(config['NestedIterators']).to include('enabled')
|
57
|
+
expect(config['DuplicateMethodCall']['max_calls']).to eq 3
|
71
58
|
expect(config['NestedIterators']['enabled']).to be_truthy
|
72
59
|
end
|
73
60
|
|
74
61
|
it 'parses multiple hashed options on the same line' do
|
75
62
|
comment = <<-RUBY
|
76
|
-
#:reek:DuplicateMethodCall {
|
63
|
+
#:reek:DuplicateMethodCall { max_calls: 3 } and :reek:NestedIterators { enabled: true }
|
77
64
|
RUBY
|
78
|
-
config =
|
65
|
+
config = build_code_comment(comment: comment).config
|
79
66
|
|
80
67
|
expect(config).to include('DuplicateMethodCall', 'NestedIterators')
|
81
|
-
expect(config['DuplicateMethodCall']).to
|
82
|
-
expect(config['DuplicateMethodCall']['enabled']).to be_falsey
|
68
|
+
expect(config['DuplicateMethodCall']['max_calls']).to eq 3
|
83
69
|
expect(config['NestedIterators']).to include('enabled')
|
84
70
|
expect(config['NestedIterators']['enabled']).to be_truthy
|
85
71
|
end
|
86
72
|
|
87
73
|
it 'parses multiple unhashed options on the same line' do
|
88
74
|
comment = '# :reek:DuplicateMethodCall and :reek:NestedIterators'
|
89
|
-
config =
|
75
|
+
config = build_code_comment(comment: comment).config
|
90
76
|
|
91
77
|
expect(config).to include('DuplicateMethodCall', 'NestedIterators')
|
92
78
|
expect(config['DuplicateMethodCall']).to include('enabled')
|
@@ -97,27 +83,33 @@ RSpec.describe Reek::CodeComment do
|
|
97
83
|
|
98
84
|
it 'disables the smell if no options are specifed' do
|
99
85
|
comment = '# :reek:DuplicateMethodCall'
|
100
|
-
config =
|
86
|
+
config = build_code_comment(comment: comment).config
|
101
87
|
|
102
88
|
expect(config).to include('DuplicateMethodCall')
|
103
89
|
expect(config['DuplicateMethodCall']).to include('enabled')
|
104
90
|
expect(config['DuplicateMethodCall']['enabled']).to be_falsey
|
105
91
|
end
|
106
92
|
|
93
|
+
it 'does not disable the smell if options are specifed' do
|
94
|
+
comment = '# :reek:DuplicateMethodCall { max_calls: 3 }'
|
95
|
+
config = build_code_comment(comment: comment).config
|
96
|
+
|
97
|
+
expect(config['DuplicateMethodCall']).not_to include('enabled')
|
98
|
+
end
|
99
|
+
|
107
100
|
it 'ignores smells after a space' do
|
108
|
-
config =
|
109
|
-
comment: '# :reek: DuplicateMethodCall').config
|
101
|
+
config = build_code_comment(comment: '# :reek: DuplicateMethodCall').config
|
110
102
|
expect(config).not_to include('DuplicateMethodCall')
|
111
103
|
end
|
112
104
|
|
113
105
|
it 'removes the configuration options from the comment' do
|
114
106
|
original_comment = <<-RUBY
|
115
107
|
# Actual
|
116
|
-
# :reek:DuplicateMethodCall {
|
108
|
+
# :reek:DuplicateMethodCall { max_calls: 3 }
|
117
109
|
# :reek:NestedIterators { enabled: true }
|
118
110
|
# comment
|
119
111
|
RUBY
|
120
|
-
comment =
|
112
|
+
comment = build_code_comment(comment: original_comment)
|
121
113
|
|
122
114
|
expect(comment.send(:sanitized_comment)).to eq('Actual comment')
|
123
115
|
end
|
@@ -128,8 +120,7 @@ RSpec.describe Reek::CodeComment::CodeCommentValidator do
|
|
128
120
|
context 'when the comment contains an unknown detector name' do
|
129
121
|
it 'raises BadDetectorInCommentError' do
|
130
122
|
expect do
|
131
|
-
|
132
|
-
comment: '# :reek:DoesNotExist')
|
123
|
+
build_code_comment(comment: '# :reek:DoesNotExist')
|
133
124
|
end.to raise_error(Reek::Errors::BadDetectorInCommentError)
|
134
125
|
end
|
135
126
|
end
|
@@ -138,18 +129,26 @@ RSpec.describe Reek::CodeComment::CodeCommentValidator do
|
|
138
129
|
it 'raises GarbageDetectorConfigurationInCommentError' do
|
139
130
|
expect do
|
140
131
|
comment = '# :reek:UncommunicativeMethodName { thats: a: bad: config }'
|
141
|
-
|
132
|
+
build_code_comment(comment: comment)
|
142
133
|
end.to raise_error(Reek::Errors::GarbageDetectorConfigurationInCommentError)
|
143
134
|
end
|
144
135
|
end
|
145
136
|
|
137
|
+
context 'when the legacy comment format was used' do
|
138
|
+
it 'raises LegacyCommentSeparatorError' do
|
139
|
+
comment = '# :reek:DuplicateMethodCall:'
|
140
|
+
expect { build_code_comment(comment: comment) }.
|
141
|
+
to raise_error Reek::Errors::LegacyCommentSeparatorError
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
146
145
|
describe 'validating configuration keys' do
|
147
146
|
context 'when basic options are mispelled' do
|
148
147
|
it 'raises BadDetectorConfigurationKeyInCommentError' do
|
149
148
|
expect do
|
150
149
|
# exclude -> exlude and enabled -> nabled
|
151
150
|
comment = '# :reek:UncommunicativeMethodName { exlude: alfa, nabled: true }'
|
152
|
-
|
151
|
+
build_code_comment(comment: comment)
|
153
152
|
end.to raise_error(Reek::Errors::BadDetectorConfigurationKeyInCommentError)
|
154
153
|
end
|
155
154
|
end
|
@@ -158,7 +157,7 @@ RSpec.describe Reek::CodeComment::CodeCommentValidator do
|
|
158
157
|
it 'does not raise' do
|
159
158
|
expect do
|
160
159
|
comment = '# :reek:UncommunicativeMethodName { exclude: alfa, enabled: true }'
|
161
|
-
|
160
|
+
build_code_comment(comment: comment)
|
162
161
|
end.not_to raise_error
|
163
162
|
end
|
164
163
|
end
|
@@ -168,7 +167,7 @@ RSpec.describe Reek::CodeComment::CodeCommentValidator do
|
|
168
167
|
expect do
|
169
168
|
# max_copies -> mx_copies and min_clump_size -> mn_clump_size
|
170
169
|
comment = '# :reek:DataClump { mx_copies: 4, mn_clump_size: 3 }'
|
171
|
-
|
170
|
+
build_code_comment(comment: comment)
|
172
171
|
end.to raise_error(Reek::Errors::BadDetectorConfigurationKeyInCommentError)
|
173
172
|
end
|
174
173
|
end
|
@@ -177,7 +176,7 @@ RSpec.describe Reek::CodeComment::CodeCommentValidator do
|
|
177
176
|
it 'does not raise' do
|
178
177
|
expect do
|
179
178
|
comment = '# :reek:DataClump { max_copies: 4, min_clump_size: 3 }'
|
180
|
-
|
179
|
+
build_code_comment(comment: comment)
|
181
180
|
end.not_to raise_error
|
182
181
|
end
|
183
182
|
end
|