reek 4.5.2 → 4.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/Gemfile +1 -1
  4. data/docs/How-To-Write-New-Detectors.md +6 -6
  5. data/docs/Irresponsible-Module.md +8 -1
  6. data/docs/RSpec-matchers.md +5 -10
  7. data/docs/templates/default/docstring/setup.rb +1 -8
  8. data/lib/reek/context_builder.rb +25 -1
  9. data/lib/reek/detector_repository.rb +64 -0
  10. data/lib/reek/examiner.rb +2 -2
  11. data/lib/reek/smell_configuration.rb +64 -0
  12. data/lib/reek/smell_detectors/attribute.rb +0 -2
  13. data/lib/reek/smell_detectors/base_detector.rb +5 -4
  14. data/lib/reek/smell_detectors/boolean_parameter.rb +0 -1
  15. data/lib/reek/smell_detectors/class_variable.rb +0 -1
  16. data/lib/reek/smell_detectors/control_parameter.rb +0 -1
  17. data/lib/reek/smell_detectors/data_clump.rb +0 -1
  18. data/lib/reek/smell_detectors/duplicate_method_call.rb +0 -1
  19. data/lib/reek/smell_detectors/feature_envy.rb +0 -1
  20. data/lib/reek/smell_detectors/instance_variable_assumption.rb +0 -1
  21. data/lib/reek/smell_detectors/irresponsible_module.rb +0 -1
  22. data/lib/reek/smell_detectors/long_parameter_list.rb +0 -2
  23. data/lib/reek/smell_detectors/long_yield_list.rb +0 -1
  24. data/lib/reek/smell_detectors/manual_dispatch.rb +0 -1
  25. data/lib/reek/smell_detectors/module_initialize.rb +0 -1
  26. data/lib/reek/smell_detectors/nested_iterators.rb +0 -1
  27. data/lib/reek/smell_detectors/nil_check.rb +0 -1
  28. data/lib/reek/smell_detectors/prima_donna_method.rb +0 -1
  29. data/lib/reek/smell_detectors/repeated_conditional.rb +0 -1
  30. data/lib/reek/smell_detectors/subclassed_from_core_class.rb +0 -1
  31. data/lib/reek/smell_detectors/too_many_constants.rb +0 -1
  32. data/lib/reek/smell_detectors/too_many_instance_variables.rb +0 -1
  33. data/lib/reek/smell_detectors/too_many_methods.rb +0 -1
  34. data/lib/reek/smell_detectors/too_many_statements.rb +0 -1
  35. data/lib/reek/smell_detectors/uncommunicative_method_name.rb +0 -1
  36. data/lib/reek/smell_detectors/uncommunicative_module_name.rb +0 -1
  37. data/lib/reek/smell_detectors/uncommunicative_parameter_name.rb +0 -1
  38. data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +0 -1
  39. data/lib/reek/smell_detectors/unused_parameters.rb +0 -1
  40. data/lib/reek/smell_detectors/unused_private_method.rb +0 -2
  41. data/lib/reek/smell_detectors/utility_function.rb +0 -1
  42. data/lib/reek/smell_warning.rb +85 -0
  43. data/lib/reek/spec.rb +4 -4
  44. data/lib/reek/spec/should_reek_of.rb +1 -1
  45. data/lib/reek/version.rb +1 -1
  46. data/spec/factories/factories.rb +2 -2
  47. data/spec/reek/{smell_detectors/detector_repository_spec.rb → detector_repository_spec.rb} +3 -3
  48. data/spec/reek/examiner_spec.rb +5 -5
  49. data/spec/reek/{smell_detectors/smell_configuration_spec.rb → smell_configuration_spec.rb} +3 -3
  50. data/spec/reek/smell_detectors/feature_envy_spec.rb +5 -0
  51. data/spec/reek/smell_detectors/module_initialize_spec.rb +11 -0
  52. data/spec/reek/{smell_detectors/smell_warning_spec.rb → smell_warning_spec.rb} +3 -3
  53. data/spec/reek/spec/should_reek_of_spec.rb +1 -1
  54. data/tasks/configuration.rake +2 -2
  55. metadata +8 -8
  56. data/lib/reek/smell_detectors/detector_repository.rb +0 -66
  57. data/lib/reek/smell_detectors/smell_configuration.rb +0 -66
  58. 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: 602cbf9c7a40f8103d514b8d4088192cbfd7fdff
4
- data.tar.gz: 39c4099443c81b23662e3c640374a0af287001ee
3
+ metadata.gz: 34053f46644b02e67bce28cf14ec83289581d477
4
+ data.tar.gz: 66637070f65e2c5a95ef84c3e981de7a0ffa5bf5
5
5
  SHA512:
6
- metadata.gz: bfd0a2c1dfa98ca1e4edbf1967d973f17bf10ad9e15c9b205c68a222bb23dd46084488c942f0570ca58e1baa86258e9f8fb6f953ac3f7ad2077f8441fd56b9b2
7
- data.tar.gz: d46599e1cbb959026843db0d87c3cd8749baa517ff9db172c4ab37e73fcb3c3b827ae8ef425ead5553dea452b91bddeefa898eb80e8e091152855a9346b83116
6
+ metadata.gz: 9125b0336673170f5466a661f9c1893fcefc16bc7ace54d75979e817854844e7ccdd6cac5a223ca0c1a200fbaeafd56da79f0435b3f9ea14c9d67e9bea0dc024
7
+ data.tar.gz: a8308b810ce8785c1fdd39abbcb19708a86c0aa276698f3d803550a4d1d332ec2612fcfa5c92dbdf29096c4fed4066b05506a78ce8ea6ff1a0a708e5e6fa42aa
@@ -1,5 +1,9 @@
1
1
  # Change log
2
2
 
3
+ ## 4.5.3 (2016-12-05)
4
+
5
+ * (jhubert) Stop reporting FeatureEnvy with super and arguments.
6
+
3
7
  ## 4.5.2 (2016-11-06)
4
8
 
5
9
  * (troessner) Warn about multiple configuration files.
data/Gemfile CHANGED
@@ -15,7 +15,7 @@ group :development do
15
15
  gem 'activesupport', '~> 4.2'
16
16
 
17
17
  if RUBY_VERSION >= '2.3'
18
- gem 'rubocop', '~> 0.44.1'
18
+ gem 'rubocop', '~> 0.45.0'
19
19
  gem 'rubocop-rspec', '~> 1.7'
20
20
  end
21
21
 
@@ -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/smells` and have the following base structure:
12
+ All smell detectors reside in `lib/reek/smell_detectors` and have the following base structure:
13
13
 
14
14
  ```Ruby
15
- require_relative 'smell_detector'
15
+ require_relative 'base_detector'
16
16
  require_relative 'smell_warning'
17
17
 
18
18
  module Reek
19
- module Smells
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 < SmellDetector
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 SmellDetector and should be used by you
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/smells.rb` as well.
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 considered good practice to annotate every class and module with a brief comment outlining its responsibilities.
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
 
@@ -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
- Remember that this includes our "smell types" as well. So it could be the
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(Reek::Smells::UtilityFunction)
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(Reek::Smells::DuplicateMethodCall, name: '@other.thing')
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
- case api_text
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
@@ -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: 30 }
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
@@ -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 'smell_detectors/detector_repository'
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: SmellDetectors::DetectorRepository)
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,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require_relative 'smell_configuration'
3
2
  require_relative 'base_detector'
4
- require_relative 'smell_warning'
5
3
 
6
4
  module Reek
7
5
  module SmellDetectors
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require 'set'
3
- require_relative 'smell_configuration'
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::Smells::Attribute,
110
- # Reek::Smells::BooleanParameter,
111
- # Reek::Smells::ClassVariable,
110
+ # [Reek::SmellDetectors::Attribute,
111
+ # Reek::SmellDetectors::BooleanParameter,
112
+ # Reek::SmellDetectors::ClassVariable,
112
113
  # ...]
113
114
  #
114
115
  def descendants
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require 'set'
3
3
  require_relative 'base_detector'
4
- require_relative 'smell_warning'
5
4
 
6
5
  module Reek
7
6
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require_relative 'smell_configuration'
3
2
  require_relative 'base_detector'
4
- require_relative 'smell_warning'
5
3
 
6
4
  module Reek
7
5
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative '../ast/node'
3
3
  require_relative 'base_detector'
4
- require_relative 'smell_warning'
5
4
 
6
5
  module Reek
7
6
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
3
 
5
4
  module Reek
6
5
  module SmellDetectors
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'base_detector'
3
- require_relative 'smell_warning'
4
- require_relative '../context/method_context'
5
3
 
6
4
  module Reek
7
5
  module SmellDetectors
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative '../ast/reference_collector'
3
3
  require_relative 'base_detector'
4
- require_relative 'smell_warning'
5
4
 
6
5
  module Reek
7
6
  module SmellDetectors
@@ -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
@@ -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 could pass many different types of input here:
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(Reek::Smells::UtilityFunction)
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(Reek::Smells::DuplicateMethodCall, name: '@other.thing')
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 => { SmellDetectors::SmellConfiguration::ENABLED_KEY => true })
28
+ configuration.load_values(smell_type => { SmellConfiguration::ENABLED_KEY => true })
29
29
  @configuration = configuration
30
30
  end
31
31
 
@@ -7,6 +7,6 @@ module Reek
7
7
  # @public
8
8
  module Version
9
9
  # @public
10
- STRING = '4.5.2'.freeze
10
+ STRING = '4.5.3'.freeze
11
11
  end
12
12
  end
@@ -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/smell_detectors/smell_warning'
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::SmellDetectors::SmellWarning do
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 '../../spec_helper'
1
+ require_relative '../spec_helper'
2
2
  require_lib 'reek/smell_detectors/base_detector'
3
- require_lib 'reek/smell_detectors/detector_repository'
3
+ require_lib 'reek/detector_repository'
4
4
 
5
- RSpec.describe Reek::SmellDetectors::DetectorRepository do
5
+ RSpec.describe Reek::DetectorRepository do
6
6
  describe '.smell_types' do
7
7
  let(:smell_types) { described_class.smell_types }
8
8
 
@@ -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::SmellDetectors::SmellWarning)
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::SmellDetectors::DetectorRepository'
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::SmellDetectors::DetectorRepository').as_stubbed_const
119
- allow(Reek::SmellDetectors::DetectorRepository).to receive(:eligible_smell_types)
120
- allow(Reek::SmellDetectors::DetectorRepository).to receive(:new).and_return detector_repository
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 '../../spec_helper'
2
- require_lib 'reek/smell_detectors/smell_configuration'
1
+ require_relative '../spec_helper'
2
+ require_lib 'reek/smell_configuration'
3
3
 
4
- RSpec.describe Reek::SmellDetectors::SmellConfiguration do
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 '../../spec_helper'
2
- require_lib 'reek/smell_detectors/smell_warning'
1
+ require_relative '../spec_helper'
2
+ require_lib 'reek/smell_warning'
3
3
 
4
- RSpec.describe Reek::SmellDetectors::SmellWarning do
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::SmellDetectors::SmellConfiguration::ENABLED_KEY]).to be_falsy
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
@@ -1,11 +1,11 @@
1
- require_relative '../lib/reek/smell_detectors/detector_repository'
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::SmellDetectors::DetectorRepository.smell_types.each_with_object({}) do |klass, hash|
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.2
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-11-06 00:00:00.000000000 Z
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