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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +11 -2
  3. data/.gitignore +1 -0
  4. data/.travis.yml +1 -0
  5. data/CHANGELOG.md +8 -0
  6. data/Gemfile +3 -0
  7. data/README.md +2 -0
  8. data/docs/Attribute.md +1 -1
  9. data/docs/Uncommunicative-Method-Name.md +32 -2
  10. data/docs/Uncommunicative-Module-Name.md +29 -2
  11. data/docs/Uncommunicative-Parameter-Name.md +30 -2
  12. data/features/configuration_files/accept_setting.feature +70 -0
  13. data/features/configuration_files/exclude_directives.feature +34 -0
  14. data/features/configuration_files/mix_accept_reject_setting.feature +81 -0
  15. data/features/configuration_files/reject_setting.feature +78 -0
  16. data/features/configuration_files/unused_private_method.feature +66 -0
  17. data/lib/reek/configuration/app_configuration.rb +0 -30
  18. data/lib/reek/context/method_context.rb +15 -1
  19. data/lib/reek/smells/uncommunicative_method_name.rb +8 -7
  20. data/lib/reek/smells/uncommunicative_module_name.rb +8 -8
  21. data/lib/reek/smells/uncommunicative_parameter_name.rb +9 -7
  22. data/lib/reek/smells/unused_private_method.rb +7 -2
  23. data/lib/reek/version.rb +1 -1
  24. data/reek.gemspec +3 -4
  25. data/spec/reek/configuration/app_configuration_spec.rb +6 -27
  26. data/spec/reek/smells/uncommunicative_method_name_spec.rb +72 -16
  27. data/spec/reek/smells/uncommunicative_module_name_spec.rb +60 -47
  28. data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +55 -9
  29. data/spec/reek/smells/unused_parameters_spec.rb +9 -0
  30. data/spec/reek/smells/unused_private_method_spec.rb +38 -0
  31. data/spec/reek/smells/utility_function_spec.rb +3 -5
  32. data/spec/spec_helper.rb +28 -1
  33. 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
- DEFAULT_ACCEPT_NAMES = [].freeze
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 => DEFAULT_ACCEPT_NAMES
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
- accept_names(context).any? { |accept_name| fully_qualified_name == accept_name } ||
70
- reject_patterns(context).none? { |pattern| module_name.match pattern }
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 accept_names(context)
78
- value(ACCEPT_KEY, context, DEFAULT_ACCEPT_NAMES)
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
- DEFAULT_ACCEPT_NAMES = [].freeze
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 => DEFAULT_ACCEPT_NAMES
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
- accept_names(context).any? { |accept_name| name == accept_name } ||
62
- reject_patterns(context).none? { |pattern| name.match pattern }
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 accept_names(context)
70
- value(ACCEPT_KEY, context, DEFAULT_ACCEPT_NAMES)
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
- ignore_methods = value(EXCLUDE_KEY, ctx, DEFAULT_EXCLUDE_SET)
83
- ignore_methods.any? { |ignore_method| method.name[ignore_method] }
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
@@ -7,6 +7,6 @@ module Reek
7
7
  # @public
8
8
  module Version
9
9
  # @public
10
- STRING = '4.0.0.pre1'.freeze
10
+ STRING = '4.0.0'.freeze
11
11
  end
12
12
  end
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 = <<-DESC
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.from_map directory_directives: directory_directives
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.from_map directory_directives: directory_directives,
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.from_map directory_directives: directory_directives,
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
- ['help', '+', '-', '/', '*'].each do |method_name|
11
- it "accepts the method name '#{method_name}'" do
12
- src = "def #{method_name}(arg) basics(17) end"
13
- expect(src).not_to reek_of(:UncommunicativeMethodName)
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
- ['x', 'x2', 'method2'].each do |method_name|
18
- context 'with a bad name' do
19
- let(:warning) do
20
- src = "def #{method_name}; end"
21
- ctx = Reek::Context::CodeContext.new(nil, Reek::Source::SourceCode.from(src).syntax_tree)
22
- detector.inspect(ctx).first
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
- it_should_behave_like 'common fields set correctly'
62
+ describe '`reject` patterns' do
63
+ let(:source) { 'def helper; end' }
26
64
 
27
- it 'reports the correct values' do
28
- expect(warning.parameters[:name]).to eq(method_name)
29
- expect(warning.lines).to eq([1])
30
- expect(warning.context).to eq(method_name)
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
- ['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
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
- it 'reports one-letter name' do
17
- expect("#{type} X; end").to reek_of(:UncommunicativeModuleName, name: 'X')
18
- end
16
+ it 'reports one-letter name' do
17
+ expect("#{type} X; end").to reek_of(:UncommunicativeModuleName, name: 'X')
18
+ end
19
19
 
20
- it 'reports name of the form "x2"' do
21
- expect("#{type} X2; end").to reek_of(:UncommunicativeModuleName, name: 'X2')
22
- end
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
- it 'reports long name ending in a number' do
25
- expect("#{type} Printer2; end").to reek_of(:UncommunicativeModuleName, name: 'Printer2')
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 'reports a bad scoped name' do
29
- src = "#{type} Foo::X; end"
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].smell_type).to eq(Reek::Smells::UncommunicativeModuleName.smell_type)
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
- context 'accept patterns' do
40
- let(:configuration) do
41
- default_directive_for_smell = {
42
- default_directive: {
43
- Reek::Smells::UncommunicativeModuleName => {
44
- 'accept' => ['Inline::C']
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
- it 'make smelly name pass' do
52
- src = 'module Inline::C; end'
52
+ describe '`accept` patterns' do
53
+ let(:source) { 'class Classy1; end' }
53
54
 
54
- expect(src).to_not reek_of(described_class, {}, configuration)
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
- it 'reports names with typos' do
58
- src = 'module Inline::K; end'
63
+ describe '`reject` patterns' do
64
+ let(:source) { 'class BaseHelper; end' }
59
65
 
60
- expect(src).to reek_of(described_class, {}, configuration)
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
- context 'looking at the YAML' do
65
- let(:warning) do
66
- src = 'module Printer2; end'
67
- ctx = Reek::Context::CodeContext.new(nil, Reek::Source::SourceCode.from(src).syntax_tree)
68
- detector.inspect(ctx).first
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
- it_should_behave_like 'common fields set correctly'
72
-
73
- it 'reports the correct values' do
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
- context 'looking at the smell result fields' do
77
- let(:warning) do
78
- src = 'def bad(good, bad2, good_again); basics(good, bad2, good_again); end'
79
- ctx = Reek::Context::MethodContext.new(nil, Reek::Source::SourceCode.from(src).syntax_tree)
80
- detector.inspect(ctx).first
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
- it_should_behave_like 'common fields set correctly'
86
+ it 'contains proper smell warnings' do
87
+ smells = detector.inspect(context)
88
+ warning = smells[0]
84
89
 
85
- it 'reports the correct values' do
86
- expect(warning.parameters[:name]).to eq('bad2')
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
- default_directive: {
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.from_map(default_directive)
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.from_map default_directive: config
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