reek 4.0.0.pre1 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +11 -2
- data/.gitignore +1 -0
- data/.travis.yml +1 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile +3 -0
- data/README.md +2 -0
- data/docs/Attribute.md +1 -1
- data/docs/Uncommunicative-Method-Name.md +32 -2
- data/docs/Uncommunicative-Module-Name.md +29 -2
- data/docs/Uncommunicative-Parameter-Name.md +30 -2
- data/features/configuration_files/accept_setting.feature +70 -0
- data/features/configuration_files/exclude_directives.feature +34 -0
- data/features/configuration_files/mix_accept_reject_setting.feature +81 -0
- data/features/configuration_files/reject_setting.feature +78 -0
- data/features/configuration_files/unused_private_method.feature +66 -0
- data/lib/reek/configuration/app_configuration.rb +0 -30
- data/lib/reek/context/method_context.rb +15 -1
- data/lib/reek/smells/uncommunicative_method_name.rb +8 -7
- data/lib/reek/smells/uncommunicative_module_name.rb +8 -8
- data/lib/reek/smells/uncommunicative_parameter_name.rb +9 -7
- data/lib/reek/smells/unused_private_method.rb +7 -2
- data/lib/reek/version.rb +1 -1
- data/reek.gemspec +3 -4
- data/spec/reek/configuration/app_configuration_spec.rb +6 -27
- data/spec/reek/smells/uncommunicative_method_name_spec.rb +72 -16
- data/spec/reek/smells/uncommunicative_module_name_spec.rb +60 -47
- data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +55 -9
- data/spec/reek/smells/unused_parameters_spec.rb +9 -0
- data/spec/reek/smells/unused_private_method_spec.rb +38 -0
- data/spec/reek/smells/utility_function_spec.rb +3 -5
- data/spec/spec_helper.rb +28 -1
- metadata +11 -7
@@ -28,12 +28,12 @@ module Reek
|
|
28
28
|
# to be treated as exceptions; these names will not be reported as
|
29
29
|
# uncommunicative.
|
30
30
|
ACCEPT_KEY = 'accept'.freeze
|
31
|
-
|
31
|
+
DEFAULT_ACCEPT_PATTERNS = [].freeze
|
32
32
|
|
33
33
|
def self.default_config
|
34
|
-
super.merge(
|
34
|
+
super().merge(
|
35
35
|
REJECT_KEY => DEFAULT_REJECT_PATTERNS,
|
36
|
-
ACCEPT_KEY =>
|
36
|
+
ACCEPT_KEY => DEFAULT_ACCEPT_PATTERNS
|
37
37
|
)
|
38
38
|
end
|
39
39
|
|
@@ -66,16 +66,16 @@ module Reek
|
|
66
66
|
|
67
67
|
# :reek:ControlParameter
|
68
68
|
def acceptable_name?(context:, module_name:, fully_qualified_name:)
|
69
|
-
|
70
|
-
reject_patterns(context).none? { |
|
69
|
+
accept_patterns(context).any? { |accept_pattern| fully_qualified_name.match accept_pattern } ||
|
70
|
+
reject_patterns(context).none? { |reject_pattern| module_name.match reject_pattern }
|
71
71
|
end
|
72
72
|
|
73
73
|
def reject_patterns(context)
|
74
|
-
value(REJECT_KEY, context, DEFAULT_REJECT_PATTERNS)
|
74
|
+
Array value(REJECT_KEY, context, DEFAULT_REJECT_PATTERNS)
|
75
75
|
end
|
76
76
|
|
77
|
-
def
|
78
|
-
value(ACCEPT_KEY, context,
|
77
|
+
def accept_patterns(context)
|
78
|
+
Array value(ACCEPT_KEY, context, DEFAULT_ACCEPT_PATTERNS)
|
79
79
|
end
|
80
80
|
end
|
81
81
|
end
|
@@ -16,6 +16,8 @@ module Reek
|
|
16
16
|
# Currently +UncommunicativeParameterName+ checks for
|
17
17
|
# * 1-character names
|
18
18
|
# * names ending with a number
|
19
|
+
# * names beginning with an underscore
|
20
|
+
# * names containing a capital letter (assuming camelCase)
|
19
21
|
#
|
20
22
|
# See {file:docs/Uncommunicative-Parameter-Name.md} for details.
|
21
23
|
class UncommunicativeParameterName < SmellDetector
|
@@ -23,12 +25,12 @@ module Reek
|
|
23
25
|
DEFAULT_REJECT_PATTERNS = [/^.$/, /[0-9]$/, /[A-Z]/, /^_/].freeze
|
24
26
|
|
25
27
|
ACCEPT_KEY = 'accept'.freeze
|
26
|
-
|
28
|
+
DEFAULT_ACCEPT_PATTERNS = [].freeze
|
27
29
|
|
28
30
|
def self.default_config
|
29
31
|
super.merge(
|
30
32
|
REJECT_KEY => DEFAULT_REJECT_PATTERNS,
|
31
|
-
ACCEPT_KEY =>
|
33
|
+
ACCEPT_KEY => DEFAULT_ACCEPT_PATTERNS
|
32
34
|
)
|
33
35
|
end
|
34
36
|
|
@@ -58,16 +60,16 @@ module Reek
|
|
58
60
|
end
|
59
61
|
|
60
62
|
def acceptable_name?(name:, context:)
|
61
|
-
|
62
|
-
reject_patterns(context).none? { |
|
63
|
+
accept_patterns(context).any? { |accept_pattern| name.match accept_pattern } ||
|
64
|
+
reject_patterns(context).none? { |reject_pattern| name.match reject_pattern }
|
63
65
|
end
|
64
66
|
|
65
67
|
def reject_patterns(context)
|
66
|
-
value(REJECT_KEY, context, DEFAULT_REJECT_PATTERNS)
|
68
|
+
Array value(REJECT_KEY, context, DEFAULT_REJECT_PATTERNS)
|
67
69
|
end
|
68
70
|
|
69
|
-
def
|
70
|
-
value(ACCEPT_KEY, context,
|
71
|
+
def accept_patterns(context)
|
72
|
+
Array value(ACCEPT_KEY, context, DEFAULT_ACCEPT_PATTERNS)
|
71
73
|
end
|
72
74
|
|
73
75
|
# :reek:UtilityFunction
|
@@ -78,9 +78,14 @@ module Reek
|
|
78
78
|
# @param ctx [Context::ClassContext]
|
79
79
|
# @return [Boolean]
|
80
80
|
#
|
81
|
+
# :reek:FeatureEnvy
|
81
82
|
def ignore_method?(ctx, method)
|
82
|
-
|
83
|
-
|
83
|
+
# ignore_contexts will be e.g. ["Foo::Smelly#my_method", "..."]
|
84
|
+
ignore_contexts = value(EXCLUDE_KEY, ctx, DEFAULT_EXCLUDE_SET)
|
85
|
+
ignore_contexts.any? do |ignore_context|
|
86
|
+
full_name = "#{method.parent.full_name}##{method.name}"
|
87
|
+
full_name[ignore_context]
|
88
|
+
end
|
84
89
|
end
|
85
90
|
end
|
86
91
|
end
|
data/lib/reek/version.rb
CHANGED
data/reek.gemspec
CHANGED
@@ -7,10 +7,9 @@ Gem::Specification.new do |s|
|
|
7
7
|
|
8
8
|
s.authors = ['Kevin Rutherford', 'Timo Roessner', 'Matijs van Zuijlen', 'Piotr Szotkowski']
|
9
9
|
s.default_executable = 'reek'
|
10
|
-
s.description =
|
11
|
-
Reek is a tool that examines Ruby classes, modules and methods and reports
|
12
|
-
any code smells it finds.
|
13
|
-
DESC
|
10
|
+
s.description =
|
11
|
+
'Reek is a tool that examines Ruby classes, modules and methods and reports ' \
|
12
|
+
'any code smells it finds.'
|
14
13
|
|
15
14
|
s.license = 'MIT'
|
16
15
|
s.email = ['timo.roessner@googlemail.com']
|
@@ -57,28 +57,6 @@ RSpec.describe Reek::Configuration::AppConfiguration do
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
-
describe '#from_map' do
|
61
|
-
it 'properly sets the configuration from simple data structures' do
|
62
|
-
config = described_class.from_map(directory_directives: directory_directives_value,
|
63
|
-
default_directive: default_directive_value,
|
64
|
-
excluded_paths: exclude_paths_value)
|
65
|
-
|
66
|
-
expect(config.send(:excluded_paths)).to eq(expected_excluded_paths)
|
67
|
-
expect(config.send(:default_directive)).to eq(expected_default_directive)
|
68
|
-
expect(config.send(:directory_directives)).to eq(expected_directory_directives)
|
69
|
-
end
|
70
|
-
|
71
|
-
it 'properly sets the configuration from native structures' do
|
72
|
-
config = described_class.from_map(directory_directives: expected_directory_directives,
|
73
|
-
default_directive: expected_default_directive,
|
74
|
-
excluded_paths: expected_excluded_paths)
|
75
|
-
|
76
|
-
expect(config.send(:excluded_paths)).to eq(expected_excluded_paths)
|
77
|
-
expect(config.send(:default_directive)).to eq(expected_default_directive)
|
78
|
-
expect(config.send(:directory_directives)).to eq(expected_directory_directives)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
60
|
describe '#from_hash' do
|
83
61
|
it 'sets the configuration a unified simple data structure' do
|
84
62
|
config = described_class.from_hash(combined_value)
|
@@ -104,7 +82,7 @@ RSpec.describe Reek::Configuration::AppConfiguration do
|
|
104
82
|
end
|
105
83
|
|
106
84
|
it 'returns the corresponding directive' do
|
107
|
-
configuration = described_class.
|
85
|
+
configuration = described_class.from_hash directory_directives
|
108
86
|
expect(configuration.directive_for(source_via)).to eq(bang_config)
|
109
87
|
end
|
110
88
|
end
|
@@ -120,11 +98,12 @@ RSpec.describe Reek::Configuration::AppConfiguration do
|
|
120
98
|
}
|
121
99
|
end
|
122
100
|
let(:source_via) { "#{directory}/dummy.rb" }
|
101
|
+
let(:configuration_as_hash) { directory_directives.merge(default_directive) }
|
123
102
|
|
124
103
|
it 'returns the directory directive with the default directive reverse-merged' do
|
125
|
-
configuration = described_class.
|
126
|
-
default_directive: default_directive
|
104
|
+
configuration = described_class.from_hash configuration_as_hash
|
127
105
|
actual = configuration.directive_for(source_via)
|
106
|
+
|
128
107
|
expect(actual[Reek::Smells::IrresponsibleModule]).to be_truthy
|
129
108
|
expect(actual[Reek::Smells::TooManyStatements]).to be_truthy
|
130
109
|
expect(actual[Reek::Smells::TooManyStatements][:max_statements]).to eq(8)
|
@@ -138,10 +117,10 @@ RSpec.describe Reek::Configuration::AppConfiguration do
|
|
138
117
|
let(:directory_directives) do
|
139
118
|
{ 'spec/samples/two_smelly_files' => attribute_config }
|
140
119
|
end
|
120
|
+
let(:configuration_as_hash) { directory_directives.merge(default_directive) }
|
141
121
|
|
142
122
|
it 'returns the default directive' do
|
143
|
-
configuration = described_class.
|
144
|
-
default_directive: default_directive
|
123
|
+
configuration = described_class.from_hash configuration_as_hash
|
145
124
|
expect(configuration.directive_for(source_via)).to eq(default_directive)
|
146
125
|
end
|
147
126
|
end
|
@@ -4,31 +4,87 @@ require_relative 'smell_detector_shared'
|
|
4
4
|
|
5
5
|
RSpec.describe Reek::Smells::UncommunicativeMethodName do
|
6
6
|
let(:detector) { build(:smell_detector, smell_type: :UncommunicativeMethodName) }
|
7
|
-
|
8
7
|
it_should_behave_like 'SmellDetector'
|
9
8
|
|
10
|
-
|
11
|
-
it
|
12
|
-
|
13
|
-
|
9
|
+
describe 'default configuration' do
|
10
|
+
it 'reports one-word names' do
|
11
|
+
expect('def a; end').to reek_of(:UncommunicativeMethodName)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'reports names ending with a digit' do
|
15
|
+
expect('def xyz1; end').to reek_of(:UncommunicativeMethodName)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'reports camelcased names' do
|
19
|
+
expect('def aBBa; end').to reek_of(:UncommunicativeMethodName)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'does not report one-letter special characters' do
|
23
|
+
['+', '-', '/', '*'].each do |symbol|
|
24
|
+
expect("def #{symbol}; end").not_to reek_of(:UncommunicativeMethodName)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'inspect' do
|
30
|
+
let(:source) { 'def x; end' }
|
31
|
+
let(:context) { code_context(source) }
|
32
|
+
let(:detector) { build(:smell_detector, smell_type: :UncommunicativeMethodName) }
|
33
|
+
|
34
|
+
it 'returns an array of smell warnings' do
|
35
|
+
smells = detector.inspect(context)
|
36
|
+
expect(smells.length).to eq(1)
|
37
|
+
expect(smells[0]).to be_a_kind_of(Reek::Smells::SmellWarning)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'contains proper smell warnings' do
|
41
|
+
smells = detector.inspect(context)
|
42
|
+
warning = smells[0]
|
43
|
+
|
44
|
+
expect(warning.smell_type).to eq(Reek::Smells::UncommunicativeMethodName.smell_type)
|
45
|
+
expect(warning.parameters[:name]).to eq('x')
|
46
|
+
expect(warning.context).to match(/#{warning.parameters[:name]}/)
|
47
|
+
expect(warning.lines).to eq([1])
|
14
48
|
end
|
15
49
|
end
|
16
50
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
51
|
+
describe '`accept` patterns' do
|
52
|
+
let(:source) { 'def x; end' }
|
53
|
+
|
54
|
+
it 'make smelly names pass via regex / strings given by list / literal' do
|
55
|
+
[[/x/], /x/, ['x'], 'x'].each do |pattern|
|
56
|
+
configuration = accept_configuration_for(described_class, pattern: pattern)
|
57
|
+
expect(source).to_not reek_of(described_class, {}, configuration)
|
23
58
|
end
|
59
|
+
end
|
60
|
+
end
|
24
61
|
|
25
|
-
|
62
|
+
describe '`reject` patterns' do
|
63
|
+
let(:source) { 'def helper; end' }
|
26
64
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
expect(
|
65
|
+
it 'reject smelly names via regex / strings given by list / literal' do
|
66
|
+
[[/helper/], /helper/, ['helper'], 'helper'].each do |pattern|
|
67
|
+
configuration = reject_configuration_for(described_class, pattern: pattern)
|
68
|
+
expect(source).to reek_of(described_class, {}, configuration)
|
31
69
|
end
|
32
70
|
end
|
33
71
|
end
|
72
|
+
|
73
|
+
describe '.default_config' do
|
74
|
+
it 'should merge in the default accept and reject patterns' do
|
75
|
+
expected = {
|
76
|
+
'enabled' => true,
|
77
|
+
'exclude' => [],
|
78
|
+
'reject' => [/^[a-z]$/, /[0-9]$/, /[A-Z]/],
|
79
|
+
'accept' => []
|
80
|
+
}
|
81
|
+
expect(described_class.default_config).to eq(expected)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '.contexts' do
|
86
|
+
it 'should be scoped to classes and modules' do
|
87
|
+
expect(described_class.contexts).to eq([:def, :defs])
|
88
|
+
end
|
89
|
+
end
|
34
90
|
end
|
@@ -5,74 +5,87 @@ require_lib 'reek/context/code_context'
|
|
5
5
|
|
6
6
|
RSpec.describe Reek::Smells::UncommunicativeModuleName do
|
7
7
|
let(:detector) { build(:smell_detector, smell_type: :UncommunicativeModuleName) }
|
8
|
-
|
9
8
|
it_should_behave_like 'SmellDetector'
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
describe 'default configuration' do
|
11
|
+
['class', 'module'].each do |type|
|
12
|
+
it 'does not report one-word name' do
|
13
|
+
expect("#{type} Helper; end").not_to reek_of(:UncommunicativeModuleName)
|
14
|
+
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
it 'reports one-letter name' do
|
17
|
+
expect("#{type} X; end").to reek_of(:UncommunicativeModuleName, name: 'X')
|
18
|
+
end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
it 'reports name of the form "x2"' do
|
21
|
+
expect("#{type} X2; end").to reek_of(:UncommunicativeModuleName, name: 'X2')
|
22
|
+
end
|
23
23
|
|
24
|
-
|
25
|
-
|
24
|
+
it 'reports long name ending in a number' do
|
25
|
+
expect("#{type} Printer2; end").to reek_of(:UncommunicativeModuleName, name: 'Printer2')
|
26
|
+
end
|
26
27
|
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'inspect' do
|
31
|
+
let(:source) { 'class Foo::X; end' }
|
32
|
+
let(:context) { code_context(source) }
|
33
|
+
let(:detector) { build(:smell_detector, smell_type: :UncommunicativeModuleName) }
|
27
34
|
|
28
|
-
it '
|
29
|
-
|
30
|
-
ctx = Reek::Context::CodeContext.new(nil, Reek::Source::SourceCode.from(src).syntax_tree)
|
31
|
-
smells = detector.inspect(ctx)
|
35
|
+
it 'returns an array of smell warnings' do
|
36
|
+
smells = detector.inspect(context)
|
32
37
|
expect(smells.length).to eq(1)
|
33
|
-
expect(smells[0]
|
34
|
-
expect(smells[0].parameters[:name]).to eq('X')
|
35
|
-
expect(smells[0].context).to match(/#{smells[0].parameters[:name]}/)
|
38
|
+
expect(smells[0]).to be_a_kind_of(Reek::Smells::SmellWarning)
|
36
39
|
end
|
37
|
-
end
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
}
|
48
|
-
Reek::Configuration::AppConfiguration.from_map(default_directive_for_smell)
|
41
|
+
it 'contains proper smell warnings' do
|
42
|
+
smells = detector.inspect(context)
|
43
|
+
warning = smells[0]
|
44
|
+
|
45
|
+
expect(warning.smell_type).to eq(Reek::Smells::UncommunicativeModuleName.smell_type)
|
46
|
+
expect(warning.parameters[:name]).to eq('X')
|
47
|
+
expect(warning.context).to match(/#{warning.parameters[:name]}/)
|
48
|
+
expect(warning.lines).to eq([1])
|
49
49
|
end
|
50
|
+
end
|
50
51
|
|
51
|
-
|
52
|
-
|
52
|
+
describe '`accept` patterns' do
|
53
|
+
let(:source) { 'class Classy1; end' }
|
53
54
|
|
54
|
-
|
55
|
+
it 'make smelly names pass via regex / strings given by list / literal' do
|
56
|
+
[[/lassy/], /lassy/, ['lassy'], 'lassy'].each do |pattern|
|
57
|
+
configuration = accept_configuration_for(described_class, pattern: pattern)
|
58
|
+
expect(source).to_not reek_of(described_class, {}, configuration)
|
59
|
+
end
|
55
60
|
end
|
61
|
+
end
|
56
62
|
|
57
|
-
|
58
|
-
|
63
|
+
describe '`reject` patterns' do
|
64
|
+
let(:source) { 'class BaseHelper; end' }
|
59
65
|
|
60
|
-
|
66
|
+
it 'reject smelly names via regex / strings given by list / literal' do
|
67
|
+
[[/Helper/], /Helper/, ['Helper'], 'Helper'].each do |pattern|
|
68
|
+
configuration = reject_configuration_for(described_class, pattern: pattern)
|
69
|
+
expect(source).to reek_of(described_class, {}, configuration)
|
70
|
+
end
|
61
71
|
end
|
62
72
|
end
|
63
73
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
74
|
+
describe '.default_config' do
|
75
|
+
it 'should merge in the default accept and reject patterns' do
|
76
|
+
expected = {
|
77
|
+
'enabled' => true,
|
78
|
+
'exclude' => [],
|
79
|
+
'reject' => [/^.$/, /[0-9]$/],
|
80
|
+
'accept' => []
|
81
|
+
}
|
82
|
+
expect(described_class.default_config).to eq(expected)
|
69
83
|
end
|
84
|
+
end
|
70
85
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
expect(warning.parameters[:name]).to eq('Printer2')
|
75
|
-
expect(warning.lines).to eq([1])
|
86
|
+
describe '.contexts' do
|
87
|
+
it 'should be scoped to classes and modules' do
|
88
|
+
expect(described_class.contexts).to eq([:module, :class])
|
76
89
|
end
|
77
90
|
end
|
78
91
|
end
|
@@ -5,7 +5,6 @@ require_lib 'reek/context/method_context'
|
|
5
5
|
|
6
6
|
RSpec.describe Reek::Smells::UncommunicativeParameterName do
|
7
7
|
let(:detector) { build(:smell_detector, smell_type: :UncommunicativeParameterName) }
|
8
|
-
|
9
8
|
it_should_behave_like 'SmellDetector'
|
10
9
|
|
11
10
|
{ 'obj.' => 'with a receiver',
|
@@ -73,18 +72,65 @@ RSpec.describe Reek::Smells::UncommunicativeParameterName do
|
|
73
72
|
end
|
74
73
|
end
|
75
74
|
|
76
|
-
|
77
|
-
let(:
|
78
|
-
|
79
|
-
|
80
|
-
|
75
|
+
describe 'inspect' do
|
76
|
+
let(:source) { 'def foo(bar2); baz(bar2); end' }
|
77
|
+
let(:context) { method_context(source) }
|
78
|
+
let(:detector) { build(:smell_detector, smell_type: :UncommunicativeParameterName) }
|
79
|
+
|
80
|
+
it 'returns an array of smell warnings' do
|
81
|
+
smells = detector.inspect(context)
|
82
|
+
expect(smells.length).to eq(1)
|
83
|
+
expect(smells[0]).to be_a_kind_of(Reek::Smells::SmellWarning)
|
81
84
|
end
|
82
85
|
|
83
|
-
|
86
|
+
it 'contains proper smell warnings' do
|
87
|
+
smells = detector.inspect(context)
|
88
|
+
warning = smells[0]
|
84
89
|
|
85
|
-
|
86
|
-
expect(warning.parameters[:name]).to eq('
|
90
|
+
expect(warning.smell_type).to eq(Reek::Smells::UncommunicativeParameterName.smell_type)
|
91
|
+
expect(warning.parameters[:name]).to eq('bar2')
|
92
|
+
expect(warning.context).to match('foo')
|
87
93
|
expect(warning.lines).to eq([1])
|
88
94
|
end
|
89
95
|
end
|
96
|
+
|
97
|
+
describe '`accept` patterns' do
|
98
|
+
let(:source) { 'def foo(bar2); baz(bar2); end' }
|
99
|
+
|
100
|
+
it 'make smelly names pass via regex / strings given by list / literal' do
|
101
|
+
[[/bar2/], /bar2/, ['bar2'], 'bar2'].each do |pattern|
|
102
|
+
configuration = accept_configuration_for(described_class, pattern: pattern)
|
103
|
+
expect(source).to_not reek_of(described_class, {}, configuration)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe '`reject` patterns' do
|
109
|
+
let(:source) { 'def foo(bar); baz(bar); end' }
|
110
|
+
|
111
|
+
it 'reject smelly names via regex / strings given by list / literal' do
|
112
|
+
[[/bar/], /bar/, ['bar'], 'bar'].each do |pattern|
|
113
|
+
configuration = reject_configuration_for(described_class, pattern: pattern)
|
114
|
+
expect(source).to reek_of(described_class, {}, configuration)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe '.default_config' do
|
120
|
+
it 'should merge in the default accept and reject patterns' do
|
121
|
+
expected = {
|
122
|
+
'enabled' => true,
|
123
|
+
'exclude' => [],
|
124
|
+
'reject' => [/^.$/, /[0-9]$/, /[A-Z]/, /^_/],
|
125
|
+
'accept' => []
|
126
|
+
}
|
127
|
+
expect(described_class.default_config).to eq(expected)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe '.contexts' do
|
132
|
+
it 'should be scoped to classes and modules' do
|
133
|
+
expect(described_class.contexts).to eq([:def, :defs])
|
134
|
+
end
|
135
|
+
end
|
90
136
|
end
|
@@ -66,5 +66,14 @@ RSpec.describe Reek::Smells::UnusedParameters do
|
|
66
66
|
expect('def simple(var, kw: :val, **args); @var, @kw, @args = var, kw, args; end').
|
67
67
|
not_to reek_of(:UnusedParameters)
|
68
68
|
end
|
69
|
+
|
70
|
+
it 'reports nothing when using a parameter via self assignment' do
|
71
|
+
expect('def simple(counter); counter += 1; end').not_to reek_of(:UnusedParameters)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'reports nothing when using a parameter on a rescue' do
|
75
|
+
expect('def simple(tries = 3); puts "nothing"; rescue; retry if tries -= 1 > 0; raise; end').
|
76
|
+
not_to reek_of(:UnusedParameters)
|
77
|
+
end
|
69
78
|
end
|
70
79
|
end
|
@@ -115,4 +115,42 @@ RSpec.describe Reek::Smells::UnusedPrivateMethod do
|
|
115
115
|
expect(source).not_to reek_of(:UnusedPrivateMethod)
|
116
116
|
end
|
117
117
|
end
|
118
|
+
|
119
|
+
describe 'prevent methods from being reported' do
|
120
|
+
let(:source) do
|
121
|
+
<<-EOF
|
122
|
+
class Car
|
123
|
+
private
|
124
|
+
def start; end
|
125
|
+
def drive; end
|
126
|
+
end
|
127
|
+
EOF
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'excludes them via direct match in the app configuration' do
|
131
|
+
configuration = test_configuration_for(
|
132
|
+
described_class =>
|
133
|
+
{
|
134
|
+
Reek::Smells::SmellConfiguration::ENABLED_KEY => true,
|
135
|
+
Reek::Smells::SmellDetector::EXCLUDE_KEY => ['Car#drive']
|
136
|
+
}
|
137
|
+
)
|
138
|
+
|
139
|
+
expect(source).to reek_of(:UnusedPrivateMethod, { name: :start }, configuration)
|
140
|
+
expect(source).not_to reek_of(:UnusedPrivateMethod, { name: :drive }, configuration)
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'excludes them via regex in the app configuration' do
|
144
|
+
configuration = test_configuration_for(
|
145
|
+
described_class =>
|
146
|
+
{
|
147
|
+
Reek::Smells::SmellConfiguration::ENABLED_KEY => true,
|
148
|
+
Reek::Smells::SmellDetector::EXCLUDE_KEY => [/drive/]
|
149
|
+
}
|
150
|
+
)
|
151
|
+
|
152
|
+
expect(source).to reek_of(:UnusedPrivateMethod, { name: :start }, configuration)
|
153
|
+
expect(source).not_to reek_of(:UnusedPrivateMethod, { name: :drive }, configuration)
|
154
|
+
end
|
155
|
+
end
|
118
156
|
end
|
@@ -220,13 +220,11 @@ RSpec.describe Reek::Smells::UtilityFunction do
|
|
220
220
|
describe 'disabling UtilityFunction via configuration for non-public methods' do
|
221
221
|
let(:configuration) do
|
222
222
|
default_directive = {
|
223
|
-
|
224
|
-
Reek::Smells::UtilityFunction =>
|
225
|
-
Reek::Smells::UtilityFunction::PUBLIC_METHODS_ONLY_KEY => true
|
226
|
-
}
|
223
|
+
Reek::Smells::UtilityFunction => {
|
224
|
+
Reek::Smells::UtilityFunction::PUBLIC_METHODS_ONLY_KEY => true
|
227
225
|
}
|
228
226
|
}
|
229
|
-
Reek::Configuration::AppConfiguration.
|
227
|
+
Reek::Configuration::AppConfiguration.from_hash(default_directive)
|
230
228
|
end
|
231
229
|
|
232
230
|
context 'public methods' do
|
data/spec/spec_helper.rb
CHANGED
@@ -27,7 +27,7 @@ module Helpers
|
|
27
27
|
when Pathname
|
28
28
|
configuration = Reek::Configuration::AppConfiguration.from_path(config)
|
29
29
|
when Hash
|
30
|
-
configuration = Reek::Configuration::AppConfiguration.
|
30
|
+
configuration = Reek::Configuration::AppConfiguration.from_hash config
|
31
31
|
else
|
32
32
|
raise "Unknown config given in `test_configuration_for`: #{config.inspect}"
|
33
33
|
end
|
@@ -41,6 +41,33 @@ module Helpers
|
|
41
41
|
Reek::Source::SourceCode.from(code).syntax_tree
|
42
42
|
end
|
43
43
|
|
44
|
+
# @param code [String] The given code.
|
45
|
+
#
|
46
|
+
# @return syntax_tree [Reek::Context::CodeContext]
|
47
|
+
def code_context(code)
|
48
|
+
Reek::Context::CodeContext.new(nil, syntax_tree(code))
|
49
|
+
end
|
50
|
+
|
51
|
+
# @param code [String] The given code.
|
52
|
+
#
|
53
|
+
# @return syntax_tree [Reek::Context::MethodContext]
|
54
|
+
def method_context(code)
|
55
|
+
Reek::Context::MethodContext.new(nil, syntax_tree(code))
|
56
|
+
end
|
57
|
+
|
58
|
+
# Helper methods to generate a configuration for smell types that support
|
59
|
+
# `accept` and `reject` settings.
|
60
|
+
%w(accept reject).each do |switch|
|
61
|
+
define_method("#{switch}_configuration_for") do |smell_type, pattern:|
|
62
|
+
hash = {
|
63
|
+
smell_type => {
|
64
|
+
switch => pattern
|
65
|
+
}
|
66
|
+
}
|
67
|
+
Reek::Configuration::AppConfiguration.from_hash(hash)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
44
71
|
# :reek:UncommunicativeMethodName
|
45
72
|
def sexp(type, *children)
|
46
73
|
@klass_map ||= Reek::AST::ASTNodeClassMap.new
|