reek 4.5.2 → 4.5.3
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/CHANGELOG.md +4 -0
- data/Gemfile +1 -1
- data/docs/How-To-Write-New-Detectors.md +6 -6
- data/docs/Irresponsible-Module.md +8 -1
- data/docs/RSpec-matchers.md +5 -10
- data/docs/templates/default/docstring/setup.rb +1 -8
- data/lib/reek/context_builder.rb +25 -1
- data/lib/reek/detector_repository.rb +64 -0
- data/lib/reek/examiner.rb +2 -2
- data/lib/reek/smell_configuration.rb +64 -0
- data/lib/reek/smell_detectors/attribute.rb +0 -2
- data/lib/reek/smell_detectors/base_detector.rb +5 -4
- data/lib/reek/smell_detectors/boolean_parameter.rb +0 -1
- data/lib/reek/smell_detectors/class_variable.rb +0 -1
- data/lib/reek/smell_detectors/control_parameter.rb +0 -1
- data/lib/reek/smell_detectors/data_clump.rb +0 -1
- data/lib/reek/smell_detectors/duplicate_method_call.rb +0 -1
- data/lib/reek/smell_detectors/feature_envy.rb +0 -1
- data/lib/reek/smell_detectors/instance_variable_assumption.rb +0 -1
- data/lib/reek/smell_detectors/irresponsible_module.rb +0 -1
- data/lib/reek/smell_detectors/long_parameter_list.rb +0 -2
- data/lib/reek/smell_detectors/long_yield_list.rb +0 -1
- data/lib/reek/smell_detectors/manual_dispatch.rb +0 -1
- data/lib/reek/smell_detectors/module_initialize.rb +0 -1
- data/lib/reek/smell_detectors/nested_iterators.rb +0 -1
- data/lib/reek/smell_detectors/nil_check.rb +0 -1
- data/lib/reek/smell_detectors/prima_donna_method.rb +0 -1
- data/lib/reek/smell_detectors/repeated_conditional.rb +0 -1
- data/lib/reek/smell_detectors/subclassed_from_core_class.rb +0 -1
- data/lib/reek/smell_detectors/too_many_constants.rb +0 -1
- data/lib/reek/smell_detectors/too_many_instance_variables.rb +0 -1
- data/lib/reek/smell_detectors/too_many_methods.rb +0 -1
- data/lib/reek/smell_detectors/too_many_statements.rb +0 -1
- data/lib/reek/smell_detectors/uncommunicative_method_name.rb +0 -1
- data/lib/reek/smell_detectors/uncommunicative_module_name.rb +0 -1
- data/lib/reek/smell_detectors/uncommunicative_parameter_name.rb +0 -1
- data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +0 -1
- data/lib/reek/smell_detectors/unused_parameters.rb +0 -1
- data/lib/reek/smell_detectors/unused_private_method.rb +0 -2
- data/lib/reek/smell_detectors/utility_function.rb +0 -1
- data/lib/reek/smell_warning.rb +85 -0
- data/lib/reek/spec.rb +4 -4
- data/lib/reek/spec/should_reek_of.rb +1 -1
- data/lib/reek/version.rb +1 -1
- data/spec/factories/factories.rb +2 -2
- data/spec/reek/{smell_detectors/detector_repository_spec.rb → detector_repository_spec.rb} +3 -3
- data/spec/reek/examiner_spec.rb +5 -5
- data/spec/reek/{smell_detectors/smell_configuration_spec.rb → smell_configuration_spec.rb} +3 -3
- data/spec/reek/smell_detectors/feature_envy_spec.rb +5 -0
- data/spec/reek/smell_detectors/module_initialize_spec.rb +11 -0
- data/spec/reek/{smell_detectors/smell_warning_spec.rb → smell_warning_spec.rb} +3 -3
- data/spec/reek/spec/should_reek_of_spec.rb +1 -1
- data/tasks/configuration.rake +2 -2
- metadata +8 -8
- data/lib/reek/smell_detectors/detector_repository.rb +0 -66
- data/lib/reek/smell_detectors/smell_configuration.rb +0 -66
- data/lib/reek/smell_detectors/smell_warning.rb +0 -88
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 34053f46644b02e67bce28cf14ec83289581d477
|
4
|
+
data.tar.gz: 66637070f65e2c5a95ef84c3e981de7a0ffa5bf5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9125b0336673170f5466a661f9c1893fcefc16bc7ace54d75979e817854844e7ccdd6cac5a223ca0c1a200fbaeafd56da79f0435b3f9ea14c9d67e9bea0dc024
|
7
|
+
data.tar.gz: a8308b810ce8785c1fdd39abbcb19708a86c0aa276698f3d803550a4d1d332ec2612fcfa5c92dbdf29096c4fed4066b05506a78ce8ea6ff1a0a708e5e6fa42aa
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
@@ -9,19 +9,19 @@ it covers and the overall rationale behind it.
|
|
9
9
|
|
10
10
|
### Structure
|
11
11
|
|
12
|
-
All smell detectors reside in `lib/reek/
|
12
|
+
All smell detectors reside in `lib/reek/smell_detectors` and have the following base structure:
|
13
13
|
|
14
14
|
```Ruby
|
15
|
-
require_relative '
|
15
|
+
require_relative 'base_detector'
|
16
16
|
require_relative 'smell_warning'
|
17
17
|
|
18
18
|
module Reek
|
19
|
-
module
|
19
|
+
module SmellDetectors
|
20
20
|
#
|
21
21
|
# Here goes your introduction for this detector.
|
22
22
|
#
|
23
23
|
# See {file:docs/Your-Detector.md} for details.
|
24
|
-
class YourDetector <
|
24
|
+
class YourDetector < BaseDetector
|
25
25
|
def self.contexts
|
26
26
|
[:class] # In case you're operating on class contexts only - just an example.
|
27
27
|
end
|
@@ -37,7 +37,7 @@ module Reek
|
|
37
37
|
# This can just be a method but it can also be a more sophisticated set up.
|
38
38
|
# Check out other smell detectors to get a feeling for what to do here.
|
39
39
|
found_smells(ctx).map do |smell|
|
40
|
-
# "smell_warning" is defined in
|
40
|
+
# "smell_warning" is defined in BaseDetector and should be used by you
|
41
41
|
# to construct smell warnings
|
42
42
|
smell_warning(
|
43
43
|
context: ctx,
|
@@ -59,7 +59,7 @@ module Reek
|
|
59
59
|
end
|
60
60
|
```
|
61
61
|
|
62
|
-
For your detector to be properly loaded you need to require it in `lib/reek/
|
62
|
+
For your detector to be properly loaded you need to require it in `lib/reek/smell_detectors.rb` as well.
|
63
63
|
|
64
64
|
### defaults.reek
|
65
65
|
|
@@ -2,7 +2,14 @@
|
|
2
2
|
|
3
3
|
## Introduction
|
4
4
|
|
5
|
-
Classes and modules are the units of reuse and release. It is therefore
|
5
|
+
Classes and modules are the units of reuse and release. It is therefore
|
6
|
+
considered good practice to annotate every class and module with a brief
|
7
|
+
comment outlining its responsibilities.
|
8
|
+
|
9
|
+
For further guideline on how to write good documentation in Ruby, see these
|
10
|
+
links:
|
11
|
+
- [Rails API documentation guidelines](http://edgeguides.rubyonrails.org/api_documentation_guidelines.html)
|
12
|
+
- [Comments tell you why](https://blog.codinghorror.com/code-tells-you-how-comments-tell-you-why/)
|
6
13
|
|
7
14
|
## Example
|
8
15
|
|
data/docs/RSpec-matchers.md
CHANGED
@@ -74,15 +74,10 @@ See the "Quickstart" example from above.
|
|
74
74
|
Checks the target source code for instances of "smell type"
|
75
75
|
and returns true only if it can find one of them that matches.
|
76
76
|
|
77
|
-
|
78
|
-
"smell type" UtilityFunction, which is represented as a concrete class
|
79
|
-
in Reek but it could also be "Duplication" which is a "smell categgory".
|
77
|
+
You can pass the smell type you want to check for as String or as Symbol:
|
80
78
|
|
81
|
-
You could pass many different types of input here:
|
82
79
|
- `:UtilityFunction`
|
83
80
|
- `"UtilityFunction"`
|
84
|
-
- `Reek::Smells::UtilityFunction` (the right way if you really want to pass a
|
85
|
-
class)
|
86
81
|
|
87
82
|
It is recommended to pass this as a symbol like `:UtilityFunction`. However we
|
88
83
|
don't enforce this.
|
@@ -106,20 +101,20 @@ So in a nutshell `reek_of` takes the following two arguments:
|
|
106
101
|
|
107
102
|
```Ruby
|
108
103
|
reek_of(:FeatureEnvy)
|
109
|
-
reek_of(
|
104
|
+
reek_of(:UtilityFunction)
|
110
105
|
```
|
111
106
|
|
112
107
|
With smell_details:
|
113
108
|
|
114
109
|
```Ruby
|
115
|
-
reek_of(UncommunicativeParameterName, name: 'x2')
|
116
|
-
reek_of(DataClump, count: 3)
|
110
|
+
reek_of(:UncommunicativeParameterName, name: 'x2')
|
111
|
+
reek_of(:DataClump, count: 3)
|
117
112
|
```
|
118
113
|
|
119
114
|
**Examples from a real spec**
|
120
115
|
|
121
116
|
```Ruby
|
122
|
-
expect(src).to reek_of(
|
117
|
+
expect(src).to reek_of(:DuplicateMethodCall, name: '@other.thing')
|
123
118
|
```
|
124
119
|
|
125
120
|
### reek_only_of
|
@@ -10,14 +10,7 @@ end
|
|
10
10
|
|
11
11
|
def api_marker
|
12
12
|
return if object.type == :root
|
13
|
-
|
14
|
-
when 'public'
|
15
|
-
# erb(:public_api_marker)
|
16
|
-
when 'private'
|
17
|
-
# Let section 'private' handle this.
|
18
|
-
else
|
19
|
-
erb(:private)
|
20
|
-
end
|
13
|
+
erb(:private) unless ['public', 'private'].include? api_text
|
21
14
|
end
|
22
15
|
|
23
16
|
private
|
data/lib/reek/context_builder.rb
CHANGED
@@ -19,7 +19,7 @@ module Reek
|
|
19
19
|
# counting. Ideally `ContextBuilder` would only build up the context tree and leave the
|
20
20
|
# statement and reference counting to the contexts.
|
21
21
|
#
|
22
|
-
# :reek:TooManyMethods: { max_methods:
|
22
|
+
# :reek:TooManyMethods: { max_methods: 31 }
|
23
23
|
# :reek:UnusedPrivateMethod: { exclude: [ !ruby/regexp /process_/ ] }
|
24
24
|
class ContextBuilder
|
25
25
|
attr_reader :context_tree
|
@@ -228,6 +228,30 @@ module Reek
|
|
228
228
|
current_context.record_use_of_self
|
229
229
|
end
|
230
230
|
|
231
|
+
# Handles `super` nodes a.k.a. calls to `super` with arguments
|
232
|
+
#
|
233
|
+
# An input example that would trigger this method would be:
|
234
|
+
#
|
235
|
+
# def call_me; super(); end
|
236
|
+
#
|
237
|
+
# or
|
238
|
+
#
|
239
|
+
# def call_me; super(bar); end
|
240
|
+
#
|
241
|
+
# but not
|
242
|
+
#
|
243
|
+
# def call_me; super; end
|
244
|
+
#
|
245
|
+
# and not
|
246
|
+
#
|
247
|
+
# def call_me; super do end; end
|
248
|
+
#
|
249
|
+
# We record one reference to `self`.
|
250
|
+
#
|
251
|
+
def process_super(_)
|
252
|
+
current_context.record_use_of_self
|
253
|
+
end
|
254
|
+
|
231
255
|
# Handles `block` nodes.
|
232
256
|
#
|
233
257
|
# An input example that would trigger this method would be:
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'smell_detectors'
|
3
|
+
require_relative 'smell_detectors/base_detector'
|
4
|
+
require_relative 'configuration/app_configuration'
|
5
|
+
|
6
|
+
module Reek
|
7
|
+
#
|
8
|
+
# Contains all the existing smell detectors and exposes operations on them.
|
9
|
+
#
|
10
|
+
class DetectorRepository
|
11
|
+
# @return [Array<Reek::SmellDetectors::BaseDetector>] All known SmellDetectors
|
12
|
+
# e.g. [Reek::SmellDetectors::BooleanParameter, Reek::SmellDetectors::ClassVariable].
|
13
|
+
def self.smell_types
|
14
|
+
Reek::SmellDetectors::BaseDetector.descendants.sort_by(&:name)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param filter_by_smells [Array<String>]
|
18
|
+
# List of smell types to filter by, e.g. "DuplicateMethodCall".
|
19
|
+
# More precisely it should be whatever is returned by `BaseDetector`.smell_type.
|
20
|
+
# This means that you can write the "DuplicateMethodCall" from above also like this:
|
21
|
+
# Reek::SmellDetectors::DuplicateMethodCall.smell_type
|
22
|
+
# if you want to make sure you do not fat-finger strings.
|
23
|
+
#
|
24
|
+
# @return [Array<Reek::SmellDetectors::BaseDetector>] All SmellDetectors that we want to filter for
|
25
|
+
# e.g. [Reek::SmellDetectors::Attribute].
|
26
|
+
def self.eligible_smell_types(filter_by_smells = [])
|
27
|
+
return smell_types if filter_by_smells.empty?
|
28
|
+
smell_types.select do |klass|
|
29
|
+
filter_by_smells.include? klass.smell_type
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(smell_types: self.class.smell_types,
|
34
|
+
configuration: {})
|
35
|
+
@configuration = configuration
|
36
|
+
@smell_types = smell_types
|
37
|
+
@detectors = smell_types.map { |klass| klass.new configuration_for(klass) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def examine(context)
|
41
|
+
smell_detectors_for(context.type).flat_map do |detector|
|
42
|
+
detector.run_for(context)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
attr_reader :configuration, :smell_types, :detectors
|
49
|
+
|
50
|
+
def configuration_for(klass)
|
51
|
+
configuration.fetch klass, {}
|
52
|
+
end
|
53
|
+
|
54
|
+
def smell_detectors_for(type)
|
55
|
+
enabled_detectors.select do |detector|
|
56
|
+
detector.contexts.include? type
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def enabled_detectors
|
61
|
+
detectors.select { |detector| detector.config.enabled? }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/reek/examiner.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
require_relative 'context_builder'
|
3
3
|
require_relative 'errors/bad_detector_in_comment_error'
|
4
4
|
require_relative 'errors/garbage_detector_configuration_in_comment_error'
|
5
|
-
require_relative '
|
5
|
+
require_relative 'detector_repository'
|
6
6
|
require_relative 'source/source_code'
|
7
7
|
|
8
8
|
module Reek
|
@@ -57,7 +57,7 @@ module Reek
|
|
57
57
|
def initialize(source,
|
58
58
|
filter_by_smells: [],
|
59
59
|
configuration: Configuration::AppConfiguration.default,
|
60
|
-
detector_repository_class:
|
60
|
+
detector_repository_class: DetectorRepository)
|
61
61
|
@source = Source::SourceCode.from(source)
|
62
62
|
@smell_types = detector_repository_class.eligible_smell_types(filter_by_smells)
|
63
63
|
@detector_repository = detector_repository_class.new(smell_types: @smell_types,
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Reek
|
3
|
+
#
|
4
|
+
# Represents a single set of configuration options for a smell detector
|
5
|
+
#
|
6
|
+
class SmellConfiguration
|
7
|
+
# The name of the config field that specifies whether a smell is
|
8
|
+
# enabled. Set to +true+ or +false+.
|
9
|
+
ENABLED_KEY = 'enabled'.freeze
|
10
|
+
|
11
|
+
# The name of the config field that sets scope-specific overrides
|
12
|
+
# for other values in the current smell detector's configuration.
|
13
|
+
OVERRIDES_KEY = 'overrides'.freeze
|
14
|
+
|
15
|
+
def initialize(hash)
|
16
|
+
@options = hash
|
17
|
+
end
|
18
|
+
|
19
|
+
def merge(new_options)
|
20
|
+
options.merge!(new_options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def enabled?
|
24
|
+
options[ENABLED_KEY]
|
25
|
+
end
|
26
|
+
|
27
|
+
def overrides_for(context)
|
28
|
+
Overrides.new(options.fetch(OVERRIDES_KEY, {})).for_context(context)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Retrieves the value, if any, for the given +key+ in the given +context+.
|
32
|
+
#
|
33
|
+
# Raises an error if neither the context nor this config have a value for
|
34
|
+
# the key.
|
35
|
+
#
|
36
|
+
def value(key, context)
|
37
|
+
overrides_for(context).each { |conf| return conf[key] if conf.key?(key) }
|
38
|
+
options.fetch(key)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
attr_reader :options
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# A set of context-specific overrides for smell detectors.
|
48
|
+
#
|
49
|
+
class Overrides
|
50
|
+
def initialize(hash)
|
51
|
+
@hash = hash
|
52
|
+
end
|
53
|
+
|
54
|
+
# Find any overrides that match the supplied context
|
55
|
+
def for_context(context)
|
56
|
+
contexts = hash.keys.select { |ckey| context.matches?([ckey]) }
|
57
|
+
contexts.map { |exc| hash[exc] }
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
attr_reader :hash
|
63
|
+
end
|
64
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'set'
|
3
|
-
require_relative '
|
3
|
+
require_relative '../smell_warning'
|
4
|
+
require_relative '../smell_configuration'
|
4
5
|
|
5
6
|
module Reek
|
6
7
|
module SmellDetectors
|
@@ -106,9 +107,9 @@ module Reek
|
|
106
107
|
# Returns all descendants of BaseDetector
|
107
108
|
#
|
108
109
|
# @return [Array<Constant>], e.g.:
|
109
|
-
# [Reek::
|
110
|
-
# Reek::
|
111
|
-
# Reek::
|
110
|
+
# [Reek::SmellDetectors::Attribute,
|
111
|
+
# Reek::SmellDetectors::BooleanParameter,
|
112
|
+
# Reek::SmellDetectors::ClassVariable,
|
112
113
|
# ...]
|
113
114
|
#
|
114
115
|
def descendants
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module Reek
|
5
|
+
#
|
6
|
+
# Reports a warning that a smell has been found.
|
7
|
+
#
|
8
|
+
# @public
|
9
|
+
#
|
10
|
+
# :reek:TooManyInstanceVariables: { max_instance_variables: 6 }
|
11
|
+
class SmellWarning
|
12
|
+
include Comparable
|
13
|
+
extend Forwardable
|
14
|
+
|
15
|
+
# @public
|
16
|
+
attr_reader :context, :lines, :message, :parameters, :smell_detector, :source
|
17
|
+
def_delegators :smell_detector, :smell_type
|
18
|
+
|
19
|
+
# @note When using Reek's public API, you should not create SmellWarning
|
20
|
+
# objects yourself. This is why the initializer is not part of the
|
21
|
+
# public API.
|
22
|
+
#
|
23
|
+
# :reek:LongParameterList: { max_params: 6 }
|
24
|
+
def initialize(smell_detector, context: '', lines:, message:,
|
25
|
+
source:, parameters: {})
|
26
|
+
@smell_detector = smell_detector
|
27
|
+
@source = source
|
28
|
+
@context = context.to_s
|
29
|
+
@lines = lines
|
30
|
+
@message = message
|
31
|
+
@parameters = parameters
|
32
|
+
|
33
|
+
freeze
|
34
|
+
end
|
35
|
+
|
36
|
+
# @public
|
37
|
+
def hash
|
38
|
+
identifying_values.hash
|
39
|
+
end
|
40
|
+
|
41
|
+
# @public
|
42
|
+
def <=>(other)
|
43
|
+
identifying_values <=> other.identifying_values
|
44
|
+
end
|
45
|
+
|
46
|
+
# @public
|
47
|
+
def eql?(other)
|
48
|
+
(self <=> other).zero?
|
49
|
+
end
|
50
|
+
|
51
|
+
# @public
|
52
|
+
def to_hash
|
53
|
+
stringified_params = Hash[parameters.map { |key, val| [key.to_s, val] }]
|
54
|
+
base_hash.merge(stringified_params)
|
55
|
+
end
|
56
|
+
|
57
|
+
alias yaml_hash to_hash
|
58
|
+
|
59
|
+
def base_message
|
60
|
+
"#{smell_type}: #{context} #{message}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def smell_class
|
64
|
+
smell_detector.class
|
65
|
+
end
|
66
|
+
|
67
|
+
protected
|
68
|
+
|
69
|
+
def identifying_values
|
70
|
+
[smell_type, context, message, lines]
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def base_hash
|
76
|
+
{
|
77
|
+
'context' => context,
|
78
|
+
'lines' => lines,
|
79
|
+
'message' => message,
|
80
|
+
'smell_type' => smell_type,
|
81
|
+
'source' => source
|
82
|
+
}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/reek/spec.rb
CHANGED
@@ -48,10 +48,10 @@ module Reek
|
|
48
48
|
# Checks the target source code for instances of "smell type"
|
49
49
|
# and returns true only if it can find one of them that matches.
|
50
50
|
#
|
51
|
-
# You
|
51
|
+
# You can pass the smell type you want to check for as String or as Symbol:
|
52
|
+
#
|
52
53
|
# - :UtilityFunction
|
53
54
|
# - "UtilityFunction"
|
54
|
-
# - Reek::Smells::UtilityFunction
|
55
55
|
#
|
56
56
|
# It is recommended to pass this as a symbol like :UtilityFunction. However we don't
|
57
57
|
# enforce this.
|
@@ -70,7 +70,7 @@ module Reek
|
|
70
70
|
# @example Without smell_details
|
71
71
|
#
|
72
72
|
# reek_of(:FeatureEnvy)
|
73
|
-
# reek_of(
|
73
|
+
# reek_of(:UtilityFunction)
|
74
74
|
#
|
75
75
|
# @example With smell_details
|
76
76
|
#
|
@@ -79,7 +79,7 @@ module Reek
|
|
79
79
|
#
|
80
80
|
# @example From a real spec
|
81
81
|
#
|
82
|
-
# expect(src).to reek_of(
|
82
|
+
# expect(src).to reek_of(:DuplicateMethodCall, name: '@other.thing')
|
83
83
|
#
|
84
84
|
# @public
|
85
85
|
#
|
@@ -25,7 +25,7 @@ module Reek
|
|
25
25
|
configuration = Configuration::AppConfiguration.default)
|
26
26
|
@smell_type = normalize smell_type_or_class
|
27
27
|
@smell_details = smell_details
|
28
|
-
configuration.load_values(smell_type => {
|
28
|
+
configuration.load_values(smell_type => { SmellConfiguration::ENABLED_KEY => true })
|
29
29
|
@configuration = configuration
|
30
30
|
end
|
31
31
|
|
data/lib/reek/version.rb
CHANGED
data/spec/factories/factories.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require_relative '../../lib/reek/smell_detectors'
|
2
2
|
require_relative '../../lib/reek/smell_detectors/base_detector'
|
3
|
-
require_relative '../../lib/reek/
|
3
|
+
require_relative '../../lib/reek/smell_warning'
|
4
4
|
require_relative '../../lib/reek/cli/options'
|
5
5
|
|
6
6
|
FactoryGirl.define do
|
@@ -34,7 +34,7 @@ FactoryGirl.define do
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
factory :smell_warning, class: Reek::
|
37
|
+
factory :smell_warning, class: Reek::SmellWarning do
|
38
38
|
skip_create
|
39
39
|
smell_detector
|
40
40
|
context 'self'
|
@@ -1,8 +1,8 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative '../spec_helper'
|
2
2
|
require_lib 'reek/smell_detectors/base_detector'
|
3
|
-
require_lib 'reek/
|
3
|
+
require_lib 'reek/detector_repository'
|
4
4
|
|
5
|
-
RSpec.describe Reek::
|
5
|
+
RSpec.describe Reek::DetectorRepository do
|
6
6
|
describe '.smell_types' do
|
7
7
|
let(:smell_types) { described_class.smell_types }
|
8
8
|
|
data/spec/reek/examiner_spec.rb
CHANGED
@@ -87,7 +87,7 @@ RSpec.describe Reek::Examiner do
|
|
87
87
|
examiner = described_class.new code, filter_by_smells: ['DuplicateMethodCall']
|
88
88
|
|
89
89
|
smell = examiner.smells.first
|
90
|
-
expect(smell).to be_a(Reek::
|
90
|
+
expect(smell).to be_a(Reek::SmellWarning)
|
91
91
|
expect(smell.message).to eq("calls 'bar.call_me()' 2 times")
|
92
92
|
end
|
93
93
|
|
@@ -111,13 +111,13 @@ RSpec.describe Reek::Examiner do
|
|
111
111
|
let(:source) { 'class C; def does_crash_reek; end; end' }
|
112
112
|
|
113
113
|
let(:examiner) do
|
114
|
-
detector_repository = instance_double 'Reek::
|
114
|
+
detector_repository = instance_double 'Reek::DetectorRepository'
|
115
115
|
allow(detector_repository).to receive(:examine) do
|
116
116
|
raise ArgumentError, 'Looks like bad source'
|
117
117
|
end
|
118
|
-
class_double('Reek::
|
119
|
-
allow(Reek::
|
120
|
-
allow(Reek::
|
118
|
+
class_double('Reek::DetectorRepository').as_stubbed_const
|
119
|
+
allow(Reek::DetectorRepository).to receive(:eligible_smell_types)
|
120
|
+
allow(Reek::DetectorRepository).to receive(:new).and_return detector_repository
|
121
121
|
|
122
122
|
described_class.new source
|
123
123
|
end
|
@@ -1,7 +1,7 @@
|
|
1
|
-
require_relative '
|
2
|
-
require_lib 'reek/
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
require_lib 'reek/smell_configuration'
|
3
3
|
|
4
|
-
RSpec.describe Reek::
|
4
|
+
RSpec.describe Reek::SmellConfiguration do
|
5
5
|
context 'when overriding default configs' do
|
6
6
|
let(:base_config) do
|
7
7
|
{
|
@@ -97,6 +97,11 @@ RSpec.describe Reek::SmellDetectors::FeatureEnvy do
|
|
97
97
|
expect(src).not_to reek_of(:FeatureEnvy)
|
98
98
|
end
|
99
99
|
|
100
|
+
it 'does not report parameter method called with super ' do
|
101
|
+
src = 'def alfa(bravo) super(bravo.to_s); end'
|
102
|
+
expect(src).not_to reek_of(:FeatureEnvy)
|
103
|
+
end
|
104
|
+
|
100
105
|
it 'ignores multiple ivars' do
|
101
106
|
src = <<-EOS
|
102
107
|
def func
|
@@ -49,4 +49,15 @@ RSpec.describe Reek::SmellDetectors::ModuleInitialize do
|
|
49
49
|
|
50
50
|
expect(src).not_to reek_of(:ModuleInitialize)
|
51
51
|
end
|
52
|
+
|
53
|
+
it 'can be disabled via comment' do
|
54
|
+
src = <<-EOS
|
55
|
+
# :reek:ModuleInitialize
|
56
|
+
module Alfa
|
57
|
+
def initialize; end
|
58
|
+
end
|
59
|
+
EOS
|
60
|
+
|
61
|
+
expect(src).not_to reek_of(:ModuleInitialize)
|
62
|
+
end
|
52
63
|
end
|
@@ -1,7 +1,7 @@
|
|
1
|
-
require_relative '
|
2
|
-
require_lib 'reek/
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
require_lib 'reek/smell_warning'
|
3
3
|
|
4
|
-
RSpec.describe Reek::
|
4
|
+
RSpec.describe Reek::SmellWarning do
|
5
5
|
let(:duplication_detector) { build(:smell_detector, smell_type: 'DuplicateMethodCall') }
|
6
6
|
let(:feature_envy_detector) { build(:smell_detector, smell_type: 'FeatureEnvy') }
|
7
7
|
let(:utility_function_detector) { build(:smell_detector, smell_type: 'UtilityFunction') }
|
@@ -133,7 +133,7 @@ RSpec.describe Reek::Spec::ShouldReekOf do
|
|
133
133
|
context 'for a smell that is disabled by default' do
|
134
134
|
before do
|
135
135
|
default_config = Reek::SmellDetectors::UnusedPrivateMethod.default_config
|
136
|
-
expect(default_config[Reek::
|
136
|
+
expect(default_config[Reek::SmellConfiguration::ENABLED_KEY]).to be_falsy
|
137
137
|
end
|
138
138
|
|
139
139
|
it 'enables the smell detector to match automatically' do
|
data/tasks/configuration.rake
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
require_relative '../lib/reek/
|
1
|
+
require_relative '../lib/reek/detector_repository'
|
2
2
|
require 'yaml'
|
3
3
|
|
4
4
|
namespace :configuration do
|
5
5
|
desc 'Updates the default configuration file when smell defaults change'
|
6
6
|
task :update_default_configuration do
|
7
7
|
DEFAULT_SMELL_CONFIGURATION = 'defaults.reek'.freeze
|
8
|
-
content = Reek::
|
8
|
+
content = Reek::DetectorRepository.smell_types.each_with_object({}) do |klass, hash|
|
9
9
|
hash[klass.smell_type] = klass.default_config
|
10
10
|
end
|
11
11
|
File.open(DEFAULT_SMELL_CONFIGURATION, 'w') { |file| YAML.dump(content, file) }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reek
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.5.
|
4
|
+
version: 4.5.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Rutherford
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2016-
|
14
|
+
date: 2016-12-05 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: codeclimate-engine-rb
|
@@ -222,6 +222,7 @@ files:
|
|
222
222
|
- lib/reek/context/statement_counter.rb
|
223
223
|
- lib/reek/context/visibility_tracker.rb
|
224
224
|
- lib/reek/context_builder.rb
|
225
|
+
- lib/reek/detector_repository.rb
|
225
226
|
- lib/reek/errors/bad_detector_configuration_key_in_comment_error.rb
|
226
227
|
- lib/reek/errors/bad_detector_in_comment_error.rb
|
227
228
|
- lib/reek/errors/garbage_detector_configuration_in_comment_error.rb
|
@@ -247,6 +248,7 @@ files:
|
|
247
248
|
- lib/reek/report/text_report.rb
|
248
249
|
- lib/reek/report/xml_report.rb
|
249
250
|
- lib/reek/report/yaml_report.rb
|
251
|
+
- lib/reek/smell_configuration.rb
|
250
252
|
- lib/reek/smell_detectors.rb
|
251
253
|
- lib/reek/smell_detectors/attribute.rb
|
252
254
|
- lib/reek/smell_detectors/base_detector.rb
|
@@ -254,7 +256,6 @@ files:
|
|
254
256
|
- lib/reek/smell_detectors/class_variable.rb
|
255
257
|
- lib/reek/smell_detectors/control_parameter.rb
|
256
258
|
- lib/reek/smell_detectors/data_clump.rb
|
257
|
-
- lib/reek/smell_detectors/detector_repository.rb
|
258
259
|
- lib/reek/smell_detectors/duplicate_method_call.rb
|
259
260
|
- lib/reek/smell_detectors/feature_envy.rb
|
260
261
|
- lib/reek/smell_detectors/instance_variable_assumption.rb
|
@@ -267,8 +268,6 @@ files:
|
|
267
268
|
- lib/reek/smell_detectors/nil_check.rb
|
268
269
|
- lib/reek/smell_detectors/prima_donna_method.rb
|
269
270
|
- lib/reek/smell_detectors/repeated_conditional.rb
|
270
|
-
- lib/reek/smell_detectors/smell_configuration.rb
|
271
|
-
- lib/reek/smell_detectors/smell_warning.rb
|
272
271
|
- lib/reek/smell_detectors/subclassed_from_core_class.rb
|
273
272
|
- lib/reek/smell_detectors/too_many_constants.rb
|
274
273
|
- lib/reek/smell_detectors/too_many_instance_variables.rb
|
@@ -281,6 +280,7 @@ files:
|
|
281
280
|
- lib/reek/smell_detectors/unused_parameters.rb
|
282
281
|
- lib/reek/smell_detectors/unused_private_method.rb
|
283
282
|
- lib/reek/smell_detectors/utility_function.rb
|
283
|
+
- lib/reek/smell_warning.rb
|
284
284
|
- lib/reek/source/source_code.rb
|
285
285
|
- lib/reek/source/source_locator.rb
|
286
286
|
- lib/reek/spec.rb
|
@@ -348,6 +348,7 @@ files:
|
|
348
348
|
- spec/reek/context/root_context_spec.rb
|
349
349
|
- spec/reek/context/statement_counter_spec.rb
|
350
350
|
- spec/reek/context_builder_spec.rb
|
351
|
+
- spec/reek/detector_repository_spec.rb
|
351
352
|
- spec/reek/examiner_spec.rb
|
352
353
|
- spec/reek/rake/task_spec.rb
|
353
354
|
- spec/reek/report/code_climate/code_climate_configuration_spec.rb
|
@@ -362,13 +363,13 @@ files:
|
|
362
363
|
- spec/reek/report/xml_report_spec.rb
|
363
364
|
- spec/reek/report/yaml_report_spec.rb
|
364
365
|
- spec/reek/report_spec.rb
|
366
|
+
- spec/reek/smell_configuration_spec.rb
|
365
367
|
- spec/reek/smell_detectors/attribute_spec.rb
|
366
368
|
- spec/reek/smell_detectors/base_detector_spec.rb
|
367
369
|
- spec/reek/smell_detectors/boolean_parameter_spec.rb
|
368
370
|
- spec/reek/smell_detectors/class_variable_spec.rb
|
369
371
|
- spec/reek/smell_detectors/control_parameter_spec.rb
|
370
372
|
- spec/reek/smell_detectors/data_clump_spec.rb
|
371
|
-
- spec/reek/smell_detectors/detector_repository_spec.rb
|
372
373
|
- spec/reek/smell_detectors/duplicate_method_call_spec.rb
|
373
374
|
- spec/reek/smell_detectors/feature_envy_spec.rb
|
374
375
|
- spec/reek/smell_detectors/instance_variable_assumption_spec.rb
|
@@ -381,8 +382,6 @@ files:
|
|
381
382
|
- spec/reek/smell_detectors/nil_check_spec.rb
|
382
383
|
- spec/reek/smell_detectors/prima_donna_method_spec.rb
|
383
384
|
- spec/reek/smell_detectors/repeated_conditional_spec.rb
|
384
|
-
- spec/reek/smell_detectors/smell_configuration_spec.rb
|
385
|
-
- spec/reek/smell_detectors/smell_warning_spec.rb
|
386
385
|
- spec/reek/smell_detectors/subclassed_from_core_class_spec.rb
|
387
386
|
- spec/reek/smell_detectors/too_many_constants_spec.rb
|
388
387
|
- spec/reek/smell_detectors/too_many_instance_variables_spec.rb
|
@@ -395,6 +394,7 @@ files:
|
|
395
394
|
- spec/reek/smell_detectors/unused_parameters_spec.rb
|
396
395
|
- spec/reek/smell_detectors/unused_private_method_spec.rb
|
397
396
|
- spec/reek/smell_detectors/utility_function_spec.rb
|
397
|
+
- spec/reek/smell_warning_spec.rb
|
398
398
|
- spec/reek/source/source_code_spec.rb
|
399
399
|
- spec/reek/source/source_locator_spec.rb
|
400
400
|
- spec/reek/spec/should_reek_of_spec.rb
|
@@ -1,66 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require_relative '../smell_detectors'
|
3
|
-
require_relative 'base_detector'
|
4
|
-
require_relative '../configuration/app_configuration'
|
5
|
-
|
6
|
-
module Reek
|
7
|
-
module SmellDetectors
|
8
|
-
#
|
9
|
-
# Contains all the existing smell detectors and exposes operations on them.
|
10
|
-
#
|
11
|
-
class DetectorRepository
|
12
|
-
# @return [Array<Reek::SmellDetectors::BaseDetector>] All known SmellDetectors
|
13
|
-
# e.g. [Reek::Smells::BooleanParameter, Reek::Smells::ClassVariable].
|
14
|
-
def self.smell_types
|
15
|
-
Reek::SmellDetectors::BaseDetector.descendants.sort_by(&:name)
|
16
|
-
end
|
17
|
-
|
18
|
-
# @param filter_by_smells [Array<String>]
|
19
|
-
# List of smell types to filter by, e.g. "DuplicateMethodCall".
|
20
|
-
# More precisely it should be whatever is returned by `BaseDetector`.smell_type.
|
21
|
-
# This means that you can write the "DuplicateMethodCall" from above also like this:
|
22
|
-
# Reek::Smells::DuplicateMethodCall.smell_type
|
23
|
-
# if you want to make sure you do not fat-finger strings.
|
24
|
-
#
|
25
|
-
# @return [Array<Reek::SmellDetectors::BaseDetector>] All SmellDetectors that we want to filter for
|
26
|
-
# e.g. [Reek::Smells::Attribute].
|
27
|
-
def self.eligible_smell_types(filter_by_smells = [])
|
28
|
-
return smell_types if filter_by_smells.empty?
|
29
|
-
smell_types.select do |klass|
|
30
|
-
filter_by_smells.include? klass.smell_type
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def initialize(smell_types: self.class.smell_types,
|
35
|
-
configuration: {})
|
36
|
-
@configuration = configuration
|
37
|
-
@smell_types = smell_types
|
38
|
-
@detectors = smell_types.map { |klass| klass.new configuration_for(klass) }
|
39
|
-
end
|
40
|
-
|
41
|
-
def examine(context)
|
42
|
-
smell_detectors_for(context.type).flat_map do |detector|
|
43
|
-
detector.run_for(context)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
attr_reader :configuration, :smell_types, :detectors
|
50
|
-
|
51
|
-
def configuration_for(klass)
|
52
|
-
configuration.fetch klass, {}
|
53
|
-
end
|
54
|
-
|
55
|
-
def smell_detectors_for(type)
|
56
|
-
enabled_detectors.select do |detector|
|
57
|
-
detector.contexts.include? type
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def enabled_detectors
|
62
|
-
detectors.select { |detector| detector.config.enabled? }
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
@@ -1,66 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module Reek
|
3
|
-
module SmellDetectors
|
4
|
-
#
|
5
|
-
# Represents a single set of configuration options for a smell detector
|
6
|
-
#
|
7
|
-
class SmellConfiguration
|
8
|
-
# The name of the config field that specifies whether a smell is
|
9
|
-
# enabled. Set to +true+ or +false+.
|
10
|
-
ENABLED_KEY = 'enabled'.freeze
|
11
|
-
|
12
|
-
# The name of the config field that sets scope-specific overrides
|
13
|
-
# for other values in the current smell detector's configuration.
|
14
|
-
OVERRIDES_KEY = 'overrides'.freeze
|
15
|
-
|
16
|
-
def initialize(hash)
|
17
|
-
@options = hash
|
18
|
-
end
|
19
|
-
|
20
|
-
def merge(new_options)
|
21
|
-
options.merge!(new_options)
|
22
|
-
end
|
23
|
-
|
24
|
-
def enabled?
|
25
|
-
options[ENABLED_KEY]
|
26
|
-
end
|
27
|
-
|
28
|
-
def overrides_for(context)
|
29
|
-
Overrides.new(options.fetch(OVERRIDES_KEY, {})).for_context(context)
|
30
|
-
end
|
31
|
-
|
32
|
-
# Retrieves the value, if any, for the given +key+ in the given +context+.
|
33
|
-
#
|
34
|
-
# Raises an error if neither the context nor this config have a value for
|
35
|
-
# the key.
|
36
|
-
#
|
37
|
-
def value(key, context)
|
38
|
-
overrides_for(context).each { |conf| return conf[key] if conf.key?(key) }
|
39
|
-
options.fetch(key)
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
attr_reader :options
|
45
|
-
end
|
46
|
-
|
47
|
-
#
|
48
|
-
# A set of context-specific overrides for smell detectors.
|
49
|
-
#
|
50
|
-
class Overrides
|
51
|
-
def initialize(hash)
|
52
|
-
@hash = hash
|
53
|
-
end
|
54
|
-
|
55
|
-
# Find any overrides that match the supplied context
|
56
|
-
def for_context(context)
|
57
|
-
contexts = hash.keys.select { |ckey| context.matches?([ckey]) }
|
58
|
-
contexts.map { |exc| hash[exc] }
|
59
|
-
end
|
60
|
-
|
61
|
-
private
|
62
|
-
|
63
|
-
attr_reader :hash
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
@@ -1,88 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'forwardable'
|
3
|
-
|
4
|
-
module Reek
|
5
|
-
# @public
|
6
|
-
module SmellDetectors
|
7
|
-
#
|
8
|
-
# Reports a warning that a smell has been found.
|
9
|
-
#
|
10
|
-
# @public
|
11
|
-
#
|
12
|
-
# :reek:TooManyInstanceVariables: { max_instance_variables: 6 }
|
13
|
-
class SmellWarning
|
14
|
-
include Comparable
|
15
|
-
extend Forwardable
|
16
|
-
|
17
|
-
# @public
|
18
|
-
attr_reader :context, :lines, :message, :parameters, :smell_detector, :source
|
19
|
-
def_delegators :smell_detector, :smell_type
|
20
|
-
|
21
|
-
# @note When using Reek's public API, you should not create SmellWarning
|
22
|
-
# objects yourself. This is why the initializer is not part of the
|
23
|
-
# public API.
|
24
|
-
#
|
25
|
-
# :reek:LongParameterList: { max_params: 6 }
|
26
|
-
def initialize(smell_detector, context: '', lines:, message:,
|
27
|
-
source:, parameters: {})
|
28
|
-
@smell_detector = smell_detector
|
29
|
-
@source = source
|
30
|
-
@context = context.to_s
|
31
|
-
@lines = lines
|
32
|
-
@message = message
|
33
|
-
@parameters = parameters
|
34
|
-
|
35
|
-
freeze
|
36
|
-
end
|
37
|
-
|
38
|
-
# @public
|
39
|
-
def hash
|
40
|
-
identifying_values.hash
|
41
|
-
end
|
42
|
-
|
43
|
-
# @public
|
44
|
-
def <=>(other)
|
45
|
-
identifying_values <=> other.identifying_values
|
46
|
-
end
|
47
|
-
|
48
|
-
# @public
|
49
|
-
def eql?(other)
|
50
|
-
(self <=> other).zero?
|
51
|
-
end
|
52
|
-
|
53
|
-
# @public
|
54
|
-
def to_hash
|
55
|
-
stringified_params = Hash[parameters.map { |key, val| [key.to_s, val] }]
|
56
|
-
base_hash.merge(stringified_params)
|
57
|
-
end
|
58
|
-
|
59
|
-
alias yaml_hash to_hash
|
60
|
-
|
61
|
-
def base_message
|
62
|
-
"#{smell_type}: #{context} #{message}"
|
63
|
-
end
|
64
|
-
|
65
|
-
def smell_class
|
66
|
-
smell_detector.class
|
67
|
-
end
|
68
|
-
|
69
|
-
protected
|
70
|
-
|
71
|
-
def identifying_values
|
72
|
-
[smell_type, context, message, lines]
|
73
|
-
end
|
74
|
-
|
75
|
-
private
|
76
|
-
|
77
|
-
def base_hash
|
78
|
-
{
|
79
|
-
'context' => context,
|
80
|
-
'lines' => lines,
|
81
|
-
'message' => message,
|
82
|
-
'smell_type' => smell_type,
|
83
|
-
'source' => source
|
84
|
-
}
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|