reek 4.0.0.pre1 → 4.0.0

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.
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