reek 4.5.2 → 4.5.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|