reek 4.4.0 → 4.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/CONTRIBUTING.md +41 -4
  4. data/README.md +15 -3
  5. data/defaults.reek +1 -1
  6. data/docs/Basic-Smell-Options.md +2 -2
  7. data/docs/Code-Smells.md +4 -0
  8. data/docs/How-To-Write-New-Detectors.md +116 -0
  9. data/docs/How-reek-works-internally.md +3 -4
  10. data/docs/Instance-Variable-Assumption.md +134 -0
  11. data/docs/Simulated-Polymorphism.md +1 -1
  12. data/docs/Smell-Suppression.md +6 -6
  13. data/docs/{style-guide.md → Style-Guide.md} +0 -0
  14. data/docs/Unused-Private-Method.md +1 -1
  15. data/docs/YAML-Reports.md +0 -18
  16. data/features/configuration_files/directory_specific_directives.feature +4 -4
  17. data/features/configuration_files/unused_private_method.feature +2 -2
  18. data/features/samples.feature +122 -117
  19. data/features/smells/subclassed_from_core_class.feature +1 -1
  20. data/lib/reek/code_comment.rb +13 -4
  21. data/lib/reek/context/code_context.rb +1 -0
  22. data/lib/reek/examiner.rb +24 -27
  23. data/lib/reek/smells/class_variable.rb +1 -1
  24. data/lib/reek/smells/control_parameter.rb +1 -1
  25. data/lib/reek/smells/data_clump.rb +1 -1
  26. data/lib/reek/smells/duplicate_method_call.rb +1 -1
  27. data/lib/reek/smells/feature_envy.rb +1 -1
  28. data/lib/reek/smells/instance_variable_assumption.rb +1 -1
  29. data/lib/reek/smells/prima_donna_method.rb +1 -1
  30. data/lib/reek/smells/repeated_conditional.rb +1 -1
  31. data/lib/reek/smells/smell_detector.rb +5 -14
  32. data/lib/reek/smells/smell_repository.rb +1 -5
  33. data/lib/reek/smells/smell_warning.rb +6 -8
  34. data/lib/reek/smells/subclassed_from_core_class.rb +1 -1
  35. data/lib/reek/smells/uncommunicative_variable_name.rb +22 -12
  36. data/lib/reek/smells/unused_private_method.rb +1 -1
  37. data/lib/reek/spec.rb +2 -2
  38. data/lib/reek/spec/should_reek_of.rb +12 -8
  39. data/lib/reek/version.rb +1 -1
  40. data/spec/reek/code_comment_spec.rb +13 -5
  41. data/spec/reek/examiner_spec.rb +2 -2
  42. data/spec/reek/smells/attribute_spec.rb +91 -78
  43. data/spec/reek/smells/boolean_parameter_spec.rb +72 -64
  44. data/spec/reek/smells/class_variable_spec.rb +81 -68
  45. data/spec/reek/smells/control_parameter_spec.rb +101 -141
  46. data/spec/reek/smells/data_clump_spec.rb +94 -149
  47. data/spec/reek/smells/duplicate_method_call_spec.rb +98 -85
  48. data/spec/reek/smells/feature_envy_spec.rb +164 -183
  49. data/spec/reek/smells/instance_variable_assumption_spec.rb +51 -147
  50. data/spec/reek/smells/irresponsible_module_spec.rb +153 -170
  51. data/spec/reek/smells/long_parameter_list_spec.rb +44 -88
  52. data/spec/reek/smells/long_yield_list_spec.rb +41 -41
  53. data/spec/reek/smells/manual_dispatch_spec.rb +36 -18
  54. data/spec/reek/smells/module_initialize_spec.rb +31 -33
  55. data/spec/reek/smells/nested_iterators_spec.rb +189 -183
  56. data/spec/reek/smells/nil_check_spec.rb +48 -37
  57. data/spec/reek/smells/prima_donna_method_spec.rb +41 -26
  58. data/spec/reek/smells/repeated_conditional_spec.rb +75 -87
  59. data/spec/reek/smells/smell_warning_spec.rb +7 -0
  60. data/spec/reek/smells/subclassed_from_core_class_spec.rb +37 -112
  61. data/spec/reek/smells/too_many_constants_spec.rb +109 -199
  62. data/spec/reek/smells/too_many_instance_variables_spec.rb +105 -128
  63. data/spec/reek/smells/too_many_methods_spec.rb +38 -62
  64. data/spec/reek/smells/too_many_statements_spec.rb +69 -45
  65. data/spec/reek/smells/uncommunicative_method_name_spec.rb +16 -29
  66. data/spec/reek/smells/uncommunicative_module_name_spec.rb +24 -37
  67. data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +55 -60
  68. data/spec/reek/smells/uncommunicative_variable_name_spec.rb +108 -95
  69. data/spec/reek/smells/unused_parameters_spec.rb +73 -49
  70. data/spec/reek/smells/unused_private_method_spec.rb +97 -50
  71. data/spec/reek/smells/utility_function_spec.rb +130 -188
  72. data/spec/reek/spec/should_reek_of_spec.rb +2 -2
  73. metadata +6 -7
  74. data/lib/reek/cli/warning_collector.rb +0 -27
  75. data/spec/reek/cli/warning_collector_spec.rb +0 -25
  76. data/spec/reek/smells/smell_detector_shared.rb +0 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9e0d256e7194e6839716a3817b6fe951985f4fc8
4
- data.tar.gz: ec2e207fe63cd0c8cd5b48071e660bd83ccb1cc6
3
+ metadata.gz: 6f4239287ce5d453034915801d47bd07738e0a09
4
+ data.tar.gz: 717f8ac8b8a9e0093a4aaa2ee9c5f739fd27be57
5
5
  SHA512:
6
- metadata.gz: 6a2f747bdbded9c2610d934c23b07a72b001a93a9a9ed99d20b9e2bdea7c6ed5e7e667c8e05713b1e77e148294fb8e4cd34d606e197829744491db395a0c964b
7
- data.tar.gz: 16d71aee67557e3f4d78cba1d45c8452032ccf76148c57b0c50650d0bff4f8fbcb4ecc9020df33a88746a80cce65235231e5b9a4f610e187a47a43baba27b1bf
6
+ metadata.gz: ccbb4be4cf7bb2d47ce72f5fd7d709ca3b3b4e49b95253aa549a14d62a7468a176c9b10cb1d21da9667963791c3f8eca66137e0f2dd03bd5507e65c94deb6953
7
+ data.tar.gz: 90eb9358f3f66271fa0a568e4582f9d473911723730a30e1b9de7771e5f79381832be59c3d511ddae81c614d772d67a6e49a07ddece6de756f482663976ee5ea
@@ -1,5 +1,11 @@
1
1
  # Change log
2
2
 
3
+ ## 4.4.1 (2016-09-13)
4
+
5
+ * (troessner) Quote names in smell detector messages
6
+ * (troessner) Make our CodeComment regex more lenient
7
+ * (troessner) Fix UncommunicativeVariableName does not take regex configuration into account
8
+
3
9
  ## 4.4.0 (2016-08-24)
4
10
 
5
11
  * (waldyr) Add ignored nodes parameter to local_nodes
@@ -27,7 +27,9 @@ version, Ruby platform (MRI, JRuby, etc.), operating system.
27
27
  Try to provide a minimal example that reproduces the issue.
28
28
  Extra kudos if you can write it as a failing test. :)
29
29
 
30
- ## Setup and Pull Request Basics
30
+ ## Contribute features, bugfixes, documentation
31
+
32
+ ### Getting started
31
33
 
32
34
  Fork Reek, then clone it, make sure you have
33
35
  [Bundler](http://bundler.io) installed, install dependencies
@@ -47,17 +49,52 @@ Once you’re sure your copy of Reek works create your own feature branch from o
47
49
  git checkout -b your_feature_or_fix_name
48
50
  ```
49
51
 
50
- Make sure you have read our [style guide](docs/style-guide.md) before you
52
+ Make sure you have read our [style guide](docs/Style-Guide.md) before you
51
53
  start contributing.
52
54
 
53
55
  Then start hacking and add new tests which make sure that your new feature works or
54
56
  demonstrate that your fix was needed.
57
+
58
+ ### Tests
59
+
55
60
  Reek is using [Rspec](http://rspec.info/) for unit and functional testing and [cucumber]() for integration tests.
56
61
 
57
62
  When it comes to Rspec we're trying to follow [betterspecs](http://betterspecs.org/).
58
- Additonally you can find an excellent cheat sheet on how to write idiomatic Rspec [here](http://www.rubypigeon.com/posts/rspec-core-cheat-sheet).
63
+ We're not using Rspec's [shared examples](https://www.relishapp.com/rspec/rspec-core/docs/example-groups/shared-examples) because we find
64
+ them rather harming than helpful.
65
+ You can find an excellent cheat sheet on how to write idiomatic Rspec [here](http://www.rubypigeon.com/posts/rspec-core-cheat-sheet).
66
+
67
+ **Spec naming conventions**
68
+
69
+ We do not use the popular "foo" / "bar" naming when it comes to the question "how to come up with good example names?".
70
+ Instead, we use the [military alphabet](https://en.wikipedia.org/wiki/NATO_phonetic_alphabet) in ascending order
71
+ which means that we would write this
72
+
73
+ ```Ruby
74
+ class Foo
75
+ def bar(baz)
76
+ baz.quux
77
+ end
78
+ end
79
+ ```
80
+
81
+ rather like this:
82
+
83
+ ```Ruby
84
+ class Alfa
85
+ def bravo(charlie)
86
+ charlie.delta
87
+ end
88
+ end
89
+ ```
90
+
91
+ ### How to write new smell detectors
92
+
93
+ Please see [our separate guide](docs/How-To-Write-New-Detectors.md) for this.
94
+
95
+ ### Finishing up
59
96
 
60
- We also care a lot about [good commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
97
+ We care a lot about [good commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
61
98
 
62
99
  Once you’re happy with your feature / fix – or want to
63
100
  share it as a work-in-progress and request comments – once
data/README.md CHANGED
@@ -210,15 +210,27 @@ Reek currently includes checks for some aspects of
210
210
  and more. See the [Code Smells](docs/Code-Smells.md)
211
211
  for up to date details of exactly what Reek will check in your code.
212
212
 
213
- Note that [Unused Private Method](docs/Unused-Private-Method.md) is disabled by default
213
+ **Special configuration for controversial detectors:**
214
+
215
+ [Unused Private Method](docs/Unused-Private-Method.md) is disabled by default
214
216
  because it is [kind of controversial](https://github.com/troessner/reek/issues/844) which means
215
- you have to explicitly activate in your configuration via
217
+ you have to explicitly activate it in your configuration via
216
218
 
217
219
  ```Yaml
218
220
  UnusedPrivateMethod:
219
221
  enabled: true
220
222
  ```
221
223
 
224
+ [Utility Function](docs/Utility-Function.md) is a [controversial detector](https://github.com/troessner/reek/issues/681)
225
+ as well that can turn out to be really unforgiving.
226
+ As a consequence, we made it possible to disable it for non-public methods like this:
227
+
228
+ ```Yaml
229
+ ---
230
+ UtilityFunction:
231
+ public_methods_only: true
232
+ ```
233
+
222
234
  ## Configuration
223
235
 
224
236
  ### Command-line interface
@@ -356,7 +368,7 @@ end
356
368
  You can even pass in smell specific configuration settings:
357
369
 
358
370
  ```Ruby
359
- # :reek:NestedIterators: { max_allowed_nesting: 2 }
371
+ # :reek:NestedIterators { max_allowed_nesting: 2 }
360
372
  def smelly_method foo
361
373
  foo.each {|bar| bar.each {|baz| baz.qux}}
362
374
  end
@@ -115,7 +115,7 @@ UncommunicativeVariableName:
115
115
  - !ruby/regexp /[0-9]$/
116
116
  - !ruby/regexp /[A-Z]/
117
117
  accept:
118
- - _
118
+ - !ruby/regexp /^_$/
119
119
  UnusedParameters:
120
120
  enabled: true
121
121
  exclude: []
@@ -19,7 +19,7 @@ exclusions for each smell.
19
19
  To stop Reek reporting smells in any method called `write` you might create a configuration file containing this:
20
20
 
21
21
  ```yaml
22
- ControlCouple:
22
+ DuplicateMethodCall:
23
23
  exclude:
24
24
  - write
25
25
  ```
@@ -27,7 +27,7 @@ ControlCouple:
27
27
  Or a little more sophisticated using a Ruby regex like this:
28
28
 
29
29
  ```yaml
30
- ControlCouple:
30
+ DuplicateMethodCall:
31
31
  exclude:
32
32
  - !ruby/regexp /write/
33
33
  ```
@@ -11,8 +11,10 @@ Reek currently includes checks for the following smells:
11
11
  * [Control Parameter](Control-Parameter.md)
12
12
  * [Data Clump](Data-Clump.md)
13
13
  * [Duplicate Method Call](Duplicate-Method-Call.md)
14
+ * [Instance Variable Assumption](Instance-Variable-Assumption.md)
14
15
  * [Irresponsible Module](Irresponsible-Module.md)
15
16
  * [Large Class](Large-Class.md), including
17
+ * [Too Many Constants](Too-Many-Constants.md)
16
18
  * [Too Many Instance Variables](Too-Many-Instance-Variables.md)
17
19
  * [Too Many Methods](Too-Many-Methods.md)
18
20
  * [Long Parameter List](Long-Parameter-List.md), and its special case [Long Yield List](Long-Yield-List.md)
@@ -23,8 +25,10 @@ Reek currently includes checks for the following smells:
23
25
  * [Nested Iterators](Nested-Iterators.md)
24
26
  * [Prima-Donna-Method](Prima-Donna-Method.md)
25
27
  * [Simulated Polymorphism](Simulated-Polymorphism.md), including
28
+ * [Manual Dispatch](Manual-Dispatch.md)
26
29
  * [Nil Check](Nil-Check.md)
27
30
  * [Repeated Conditional](Repeated-Conditional.md)
31
+ * [Subclassed From Core Class](Subclassed-From-Core-Class.md)
28
32
  * [Too Many Statements](Too-Many-Statements.md)
29
33
  * [Uncommunicative Name](Uncommunicative-Name.md), including
30
34
  * [Uncommunicative Method Name](Uncommunicative-Method-Name.md)
@@ -0,0 +1,116 @@
1
+ ## How to write new detectors
2
+
3
+ ### Outline what you have in mind
4
+
5
+ Before starting to code you should discuss the overall idea for your new smell detector with
6
+ us in a corresponding github issue.
7
+ We all should have a solid understanding of what this detector actually reports, the edgecases
8
+ it covers and the overall rationale behind it.
9
+
10
+ ### Structure
11
+
12
+ All smell detectors reside in `lib/reek/smells` and have the following base structure:
13
+
14
+ ```Ruby
15
+ require_relative 'smell_detector'
16
+ require_relative 'smell_warning'
17
+
18
+ module Reek
19
+ module Smells
20
+ #
21
+ # Here goes your introduction for this detector.
22
+ #
23
+ # See {file:docs/Your-Detector.md} for details.
24
+ class YourDetector < SmellDetector
25
+ def self.contexts
26
+ [:class] # In case you're operating on class contexts only - just an example.
27
+ end
28
+
29
+ #
30
+ # Here you should document what you expect "ctx" to look like.
31
+ #
32
+ # @return [Array<SmellWarning>]
33
+ #
34
+ def sniff(ctx)
35
+ # "found_smells" below is just an abstraction for
36
+ # "find the smells in question" and iteratore over them.
37
+ # This can just be a method but it can also be a more sophisticated set up.
38
+ # Check out other smell detectors to get a feeling for what to do here.
39
+ found_smells(ctx).map do |smell|
40
+ # "smell_warning" is defined in SmellDetector and should be used by you
41
+ # to construct smell warnings
42
+ smell_warning(
43
+ context: ctx,
44
+ lines: [], # lines on which the smell was detected
45
+ message: "...", # the message that is printed on STDOUT
46
+ # whatever you interpolate into the "message" should go into
47
+ # parameters below - if you do not interpolate anything you
48
+ # can omit this
49
+ parameters: { })
50
+ end
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ # Here goes everything you need for finding smells.
57
+ end
58
+ end
59
+ end
60
+ ```
61
+
62
+ ### Documentation
63
+
64
+ * Above every `SmellDetector::sniff` method it should be documented what the expected AST is
65
+ * Every detector should have a separate documentation page in /docs. You can
66
+ take any arbitrary existing smell detector documentation page as template (since
67
+ they all have the same structure already)
68
+ * The detector should be listed under [Code Smells](docs/Code-Smells.md)
69
+
70
+ ### Rspec examples
71
+
72
+ All smell detector specs start out with 2 generic examples like below - the second one
73
+ only if it makes sense.
74
+ Here's what it looks like for `UncommunicativeVariableName`:
75
+
76
+ ```Ruby
77
+ it 'reports the right values' do
78
+ src = <<-EOS
79
+ def alfa
80
+ bravo = 5
81
+ end
82
+ EOS
83
+
84
+ expect(src).to reek_of(:UncommunicativeVariableName,
85
+ lines: [2],
86
+ context: 'alfa',
87
+ message: "has the variable name 'bravo'",
88
+ source: 'string',
89
+ name: 'bravo')
90
+ end
91
+
92
+ it 'does count all occurences' do
93
+ src = <<-EOS
94
+ def alfa
95
+ bravo = 3
96
+ charlie = 7
97
+ end
98
+ EOS
99
+
100
+ expect(src).to reek_of(:UncommunicativeVariableName,
101
+ lines: [2],
102
+ name: 'bravo')
103
+ expect(src).to reek_of(:UncommunicativeVariableName,
104
+ lines: [3],
105
+ name: 'charlie')
106
+ end
107
+ ```
108
+
109
+ The following examples should then cover the detector specific features.
110
+
111
+ ### Cucumber features
112
+
113
+ We are trying to write as few Cucumber features as possible.
114
+ Normally, there should be no need to write a new feature for a new smell detector.
115
+ If you feel like this is necessary in this case, please discuss this with us via
116
+ github issue or in your work-in-progress pull request before doing anything.
@@ -25,7 +25,7 @@
25
25
  | |
26
26
  | |
27
27
  ReekCommand (cli/reek_command) |
28
- * uses a reporter (report/report) |
28
+ * uses a reporter (report/report) |
29
29
  * uses a SourceLocator (source/source_locator) |
30
30
  / | \ |
31
31
  / | \ |
@@ -41,9 +41,8 @@
41
41
  * generates the AST out of the given source
42
42
  * adorns the generated AST via a TreeDresser (core/tree_dresser)
43
43
  * initializes a SmellRepository with all relevant smells (smells/smell_repository)
44
- * initializes a WarningCollector (cli/warning_collector)
45
44
  * builds a tree of Contexts using ContextBuilder
46
- * runs the smell detectors from the SmellRepository above on each of the contexts
45
+ * tells the SmellRepository above to run each of its smell detectors above on each of the contexts
47
46
  / | \
48
47
  / | \
49
48
  / | \
@@ -51,7 +50,7 @@
51
50
  \ | /
52
51
  \ | /
53
52
  \ | /
54
- WarningCollector
53
+ SmellRepository
55
54
  |
56
55
  |
57
56
  |
@@ -0,0 +1,134 @@
1
+ # Instance Variable Assumption
2
+
3
+ ## Introduction
4
+
5
+ Classes should not assume that instance variables are set or present outside of the current class definition.
6
+
7
+ Good:
8
+
9
+ ```Ruby
10
+ class Foo
11
+ def initialize
12
+ @bar = :foo
13
+ end
14
+
15
+ def foo?
16
+ @bar == :foo
17
+ end
18
+ end
19
+ ```
20
+
21
+ Good as well:
22
+
23
+ ```Ruby
24
+ class Foo
25
+ def foo?
26
+ bar == :foo
27
+ end
28
+
29
+ def bar
30
+ @bar ||= :foo
31
+ end
32
+ end
33
+ ```
34
+
35
+ Bad:
36
+
37
+ ```Ruby
38
+ class Foo
39
+ def go_foo!
40
+ @bar = :foo
41
+ end
42
+
43
+ def foo?
44
+ @bar == :foo
45
+ end
46
+ end
47
+ ```
48
+
49
+ ## Example
50
+
51
+ Running Reek on:
52
+
53
+ ```Ruby
54
+ class Dummy
55
+ def test
56
+ @ivar
57
+ end
58
+ end
59
+ ```
60
+
61
+ would report:
62
+
63
+ ```Bash
64
+ [1]:InstanceVariableAssumption: Dummy assumes too much for instance variable @ivar [https://github.com/troessner/reek/blob/master/docs/Instance-Variable-Assumption.md]
65
+ ```
66
+
67
+ Note that this example would trigger this smell warning as well:
68
+
69
+ ```Ruby
70
+ class Parent
71
+ def initialize(omg)
72
+ @omg = omg
73
+ end
74
+ end
75
+
76
+ class Child < Parent
77
+ def foo
78
+ @omg
79
+ end
80
+ end
81
+ ```
82
+
83
+ The way to address the smell warning is that you should create an `attr_reader` to use `@omg` in the subclass and not access `@omg` directly like this:
84
+
85
+ ```Ruby
86
+ class Parent
87
+ attr_reader :omg
88
+
89
+ def initialize(omg)
90
+ @omg = omg
91
+ end
92
+ end
93
+
94
+ class Child < Parent
95
+ def foo
96
+ omg
97
+ end
98
+ end
99
+ ```
100
+
101
+ Directly accessing instance variables is considered a smell because it [breaks encapsulation](http://designisrefactoring.com/2015/03/29/organizing-data-self-encapsulation/) and makes it harder to reason about code.
102
+
103
+ If you don't want to expose those methods as public API just make them private like this:
104
+
105
+ ```Ruby
106
+ class Parent
107
+ def initialize(omg)
108
+ @omg = omg
109
+ end
110
+
111
+ private
112
+ attr_reader :omg
113
+ end
114
+
115
+ class Child < Parent
116
+ def foo
117
+ omg
118
+ end
119
+ end
120
+ ```
121
+
122
+
123
+ ## Current Support in Reek
124
+
125
+ An instance variable must:
126
+
127
+ * be set in the constructor
128
+ * or be accessed through a method with lazy initialization / memoization.
129
+
130
+ If not, _Instance Variable Assumption_ will be reported.
131
+
132
+ ## Configuration
133
+
134
+ _Instance Variable Assumption_ supports the [Basic Smell Options](Basic-Smell-Options.md).