snoot 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +20 -0
  3. data/LICENSE +21 -0
  4. data/README.md +49 -0
  5. data/data/reek_docs/API.md +174 -0
  6. data/data/reek_docs/Attribute.md +39 -0
  7. data/data/reek_docs/Basic-Smell-Options.md +85 -0
  8. data/data/reek_docs/Boolean-Parameter.md +54 -0
  9. data/data/reek_docs/Class-Variable.md +40 -0
  10. data/data/reek_docs/Code-Smells.md +39 -0
  11. data/data/reek_docs/Command-Line-Options.md +119 -0
  12. data/data/reek_docs/Control-Couple.md +26 -0
  13. data/data/reek_docs/Control-Parameter.md +32 -0
  14. data/data/reek_docs/Data-Clump.md +46 -0
  15. data/data/reek_docs/Duplicate-Method-Call.md +264 -0
  16. data/data/reek_docs/Feature-Envy.md +93 -0
  17. data/data/reek_docs/How-To-Write-New-Detectors.md +144 -0
  18. data/data/reek_docs/How-reek-works-internally.md +114 -0
  19. data/data/reek_docs/Instance-Variable-Assumption.md +163 -0
  20. data/data/reek_docs/Irresponsible-Module.md +47 -0
  21. data/data/reek_docs/LICENSE +20 -0
  22. data/data/reek_docs/Large-Class.md +16 -0
  23. data/data/reek_docs/Long-Parameter-List.md +39 -0
  24. data/data/reek_docs/Long-Yield-List.md +37 -0
  25. data/data/reek_docs/Manual-Dispatch.md +30 -0
  26. data/data/reek_docs/Missing-Safe-Method.md +92 -0
  27. data/data/reek_docs/Module-Initialize.md +62 -0
  28. data/data/reek_docs/Nested-Iterators.md +59 -0
  29. data/data/reek_docs/Nil-Check.md +47 -0
  30. data/data/reek_docs/RSpec-matchers.md +129 -0
  31. data/data/reek_docs/Rake-Task.md +66 -0
  32. data/data/reek_docs/Reek-4-to-Reek-5-migration.md +188 -0
  33. data/data/reek_docs/Reek-Driven-Development.md +46 -0
  34. data/data/reek_docs/Repeated-Conditional.md +47 -0
  35. data/data/reek_docs/Simulated-Polymorphism.md +16 -0
  36. data/data/reek_docs/Smell-Suppression.md +96 -0
  37. data/data/reek_docs/Style-Guide.md +19 -0
  38. data/data/reek_docs/Subclassed-From-Core-Class.md +79 -0
  39. data/data/reek_docs/Too-Many-Constants.md +37 -0
  40. data/data/reek_docs/Too-Many-Instance-Variables.md +43 -0
  41. data/data/reek_docs/Too-Many-Methods.md +56 -0
  42. data/data/reek_docs/Too-Many-Statements.md +54 -0
  43. data/data/reek_docs/Uncommunicative-Method-Name.md +94 -0
  44. data/data/reek_docs/Uncommunicative-Module-Name.md +92 -0
  45. data/data/reek_docs/Uncommunicative-Name.md +18 -0
  46. data/data/reek_docs/Uncommunicative-Parameter-Name.md +90 -0
  47. data/data/reek_docs/Uncommunicative-Variable-Name.md +96 -0
  48. data/data/reek_docs/Unused-Parameters.md +28 -0
  49. data/data/reek_docs/Unused-Private-Method.md +101 -0
  50. data/data/reek_docs/Utility-Function.md +57 -0
  51. data/data/reek_docs/Versioning-Policy.md +7 -0
  52. data/data/reek_docs/YAML-Reports.md +93 -0
  53. data/exe/snoot +5 -0
  54. data/lib/snoot/analyse_run/decision.rb +62 -0
  55. data/lib/snoot/analyse_run/result.rb +12 -0
  56. data/lib/snoot/analyse_run.rb +70 -0
  57. data/lib/snoot/analyser_orchestration/default.rb +149 -0
  58. data/lib/snoot/analyser_orchestration/result_mapping.rb +52 -0
  59. data/lib/snoot/analyser_orchestration.rb +21 -0
  60. data/lib/snoot/analyser_result.rb +14 -0
  61. data/lib/snoot/cli/event.rb +13 -0
  62. data/lib/snoot/cli/pipeline.rb +14 -0
  63. data/lib/snoot/cli.rb +147 -0
  64. data/lib/snoot/findings.rb +23 -0
  65. data/lib/snoot/render_report.rb +82 -0
  66. data/lib/snoot/run.rb +35 -0
  67. data/lib/snoot/state_error.rb +9 -0
  68. data/lib/snoot/value_types.rb +20 -0
  69. data/lib/snoot/version.rb +5 -0
  70. data/lib/snoot.rb +21 -0
  71. data/snoot.allium +482 -0
  72. metadata +160 -0
@@ -0,0 +1,163 @@
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
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](https://www.designisrefactoring.com/2015/03/30/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
+ ## Using Instance Variable Assumption in a Rails context
133
+
134
+ In ActiveRecord it seems common to use callbacks like `after_initialize` to initialize instance variables as
135
+ outlined [here](https://stackoverflow.com/questions/41165520/overriding-applicationrecord-initialize-bad-idea)
136
+ or [here](http://blog.dalethatcher.com/2008/03/rails-dont-override-initialize-on.html)
137
+ instead of overriding the `initialize` method.
138
+ If an instance variable is initialized in such a callback Reek will report it correspondingly.
139
+
140
+ This would smell for instance:
141
+
142
+ ```ruby
143
+ class Sample < ApplicationRecord
144
+ after_initialize do
145
+ @my_var = false
146
+ end
147
+ end
148
+ ```
149
+
150
+ Since Reek cannot reliably detect that is used in a Rails context we recommend to disable this detector
151
+ for "app/models" like this:
152
+
153
+ ```yaml
154
+ directories:
155
+ # Your other configuration....
156
+ "app/models":
157
+ InstanceVariableAssumption:
158
+ enabled: false
159
+ ```
160
+
161
+ ## Configuration
162
+
163
+ _Instance Variable Assumption_ supports the [Basic Smell Options](Basic-Smell-Options.md).
@@ -0,0 +1,47 @@
1
+ # Irresponsible Module
2
+
3
+ ## Introduction
4
+
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/)
13
+
14
+ ## Example
15
+
16
+ Given
17
+
18
+ ```ruby
19
+ class Dummy
20
+ # Do things...
21
+ end
22
+ ```
23
+
24
+ Reek would emit the following warning:
25
+
26
+ ```
27
+ test.rb -- 1 warning:
28
+ [1]:IrresponsibleModule: Dummy has no descriptive comment
29
+ ```
30
+
31
+ Fixing this is simple - just an explaining comment:
32
+
33
+ ```ruby
34
+ # The Dummy class is responsible for ...
35
+ class Dummy
36
+ # Do things...
37
+ end
38
+ ```
39
+
40
+ ## Current Support in Reek
41
+
42
+ _Irresponsible Module_ checks classes and modules, including those
43
+ created through `Struct.new` and `Class.new` and directly assigned to a constant.
44
+
45
+ ## Configuration
46
+
47
+ _Irresponsible Module_ supports only the [Basic Smell Options](Basic-Smell-Options.md).
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008,2009 Kevin Rutherford
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,16 @@
1
+ # Large Class
2
+
3
+ ## Introduction
4
+
5
+ A _Large Class_ is a class or module that has a large number of instance
6
+ variables, methods or lines of code in any one piece of its specification.
7
+ (That is, this smell relates to pieces of the class's specification, not to the
8
+ size of the corresponding instance of `Class`.)
9
+
10
+ ## Current Support in Reek
11
+
12
+ Reek offers three checks in this category.
13
+
14
+ * [Too Many Constants](Too-Many-Constants.md)
15
+ * [Too Many Instance Variables](Too-Many-Instance-Variables.md)
16
+ * [Too Many Methods](Too-Many-Methods.md)
@@ -0,0 +1,39 @@
1
+ # Long Parameter List
2
+
3
+ ## Introduction
4
+
5
+ A _Long Parameter List_ occurs when a method has a lot of parameters.
6
+
7
+ ## Example
8
+
9
+ Given
10
+
11
+ ```ruby
12
+ class Dummy
13
+ def long_list(foo,bar,baz,fling,flung)
14
+ puts foo,bar,baz,fling,flung
15
+ end
16
+ end
17
+ ```
18
+
19
+ Reek would report the following warning:
20
+
21
+ ```
22
+ test.rb -- 1 warning:
23
+ [2]:Dummy#long_list has 5 parameters (LongParameterList)
24
+ ```
25
+
26
+ A common solution to this problem would be the introduction of parameter objects.
27
+
28
+ ## Current Support in Reek
29
+
30
+ _Long Parameter List_ reports any method or block with more than 3 parameters.
31
+
32
+ ## Configuration
33
+
34
+ Reek's _Long Parameter List_ detector supports the
35
+ [Basic Smell Options](Basic-Smell-Options.md), plus:
36
+
37
+ | Option | Value | Effect |
38
+ | -------------|---------|---------|
39
+ | `max_params` | integer | The maximum number of parameters allowed in a method or block before a warning is issued. Defaults to 3. |
@@ -0,0 +1,37 @@
1
+ # Long Yield List
2
+
3
+ ## Introduction
4
+
5
+ A _Long Yield List_ occurs when a method yields a lot of arguments to the block
6
+ it gets passed. It is a special case of [Long Parameter List](Long-Parameter-List.md).
7
+
8
+ ## Example
9
+
10
+ ```ruby
11
+ class Dummy
12
+ def yields_a_lot(foo,bar,baz,fling,flung)
13
+ yield foo,bar,baz,fling,flung
14
+ end
15
+ end
16
+ ```
17
+
18
+ Reek would report the following warning:
19
+
20
+ ```
21
+ test.rb -- 1 warning:
22
+ [4]:Dummy#yields_a_lot yields 5 parameters (LongYieldList)
23
+ ```
24
+
25
+ A common solution to this problem would be the introduction of parameter objects.
26
+
27
+ ## Current Support in Reek
28
+
29
+ Currently _Long Yield List_ reports any method or block with more than 3 parameters.
30
+
31
+ ## Configuration
32
+
33
+ Reek's _Long Yield List_ detector supports the [Basic Smell Options](Basic-Smell-Options.md), plus:
34
+
35
+ | Option | Value | Effect |
36
+ | -------------|---------|---------|
37
+ | `max_params` | integer | The maximum number of parameters allowed in a method or block before a warning is issued. Defaults to 3. |
@@ -0,0 +1,30 @@
1
+ ## Introduction
2
+
3
+ Reek reports a _Manual Dispatch_ smell if it finds source code that manually checks whether an object responds to a method before that method is called. Manual dispatch is a type of [Simulated Polymorphism](Simulated-Polymorphism.md) which leads to code that is harder to reason about, debug, and refactor.
4
+
5
+ ## Example
6
+
7
+ ```ruby
8
+ class MyManualDispatcher
9
+ attr_reader :foo
10
+
11
+ def initialize(foo)
12
+ @foo = foo
13
+ end
14
+
15
+ def call
16
+ foo.bar if foo.respond_to?(:bar)
17
+ end
18
+ end
19
+ ```
20
+
21
+ Reek would emit the following warning:
22
+
23
+ ```
24
+ test.rb -- 1 warning:
25
+ [9]: MyManualDispatcher manually dispatches method call (ManualDispatch)
26
+ ```
27
+
28
+ ## Configuration
29
+
30
+ _Manual Dispatch_ offers the [Basic Smell Options](Basic-Smell-Options.md).
@@ -0,0 +1,92 @@
1
+ # Missing Safe Method
2
+
3
+ ## Introduction
4
+
5
+ Candidate methods for the _Missing Safe Method_ smell are methods whose names
6
+ end with an exclamation mark.
7
+
8
+ An exclamation mark in method names means (the explanation below is taken from
9
+ [here](http://dablog.rubypal.com/2007/8/15/bang-methods-or-danger-will-rubyist)):
10
+
11
+ > The ! in method names that end with ! means, “This method is dangerous”—or,
12
+ > more precisely, this method is the “dangerous” version of an otherwise
13
+ > equivalent method, with the same name minus the !. “Danger” is relative; the
14
+ > ! doesn’t mean anything at all unless the method name it’s in corresponds to
15
+ > a similar but bang-less method name.
16
+ >
17
+ > So, for example, gsub! is the dangerous version of gsub. exit! is the
18
+ > dangerous version of exit. flatten! is the dangerous version of flatten. And
19
+ > so forth.
20
+
21
+ Such a method is called _Missing Safe Method_ if and only if the non-bang
22
+ version does not exist and this method is reported as a smell.
23
+
24
+ Missing Safe Method was formerly known as Prima Donna Method.
25
+
26
+ ## Example
27
+
28
+ Given
29
+
30
+ ```ruby
31
+ class C
32
+ def foo; end
33
+ def foo!; end
34
+ def bar!; end
35
+ end
36
+ ```
37
+
38
+ Reek would report the _Missing Safe Method_ smell for `bar!`, but not for `foo!`.
39
+
40
+ Reek reports this smell only in a class context, not in a module context in order to allow perfectly legit code like this:
41
+
42
+
43
+ ```ruby
44
+ class Parent
45
+ def foo; end
46
+ end
47
+
48
+ module Dangerous
49
+ def foo!; end
50
+ end
51
+
52
+ class Son < Parent
53
+ include Dangerous
54
+ end
55
+
56
+ class Daughter < Parent
57
+ end
58
+ ```
59
+
60
+ In this example, Reek would not report the _Missing Safe Method_ smell for the
61
+ method `foo` of the `Dangerous` module.
62
+
63
+ ## Configuration
64
+
65
+ _Missing Safe Method_ offers the [Basic Smell Options](Basic-Smell-Options.md).
66
+
67
+ ## Example configuration via source comment
68
+
69
+ Imagine code like this:
70
+
71
+ ```ruby
72
+ class Alfa
73
+ def bravo!
74
+ end
75
+ end
76
+ ```
77
+
78
+ This would report:
79
+
80
+ >>
81
+ ruby.rb -- 1 warning:
82
+ [1]:MissingSafeMethod: Alfa has missing safe method 'bravo!'
83
+
84
+ If you want to suppress this warning you can do this via source comment like this:
85
+
86
+ ```ruby
87
+ # :reek:MissingSafeMethod { exclude: [ bravo! ] }
88
+ class Alfa
89
+ def bravo!
90
+ end
91
+ end
92
+ ```
@@ -0,0 +1,62 @@
1
+ # Module Initialize
2
+
3
+ ## Introduction
4
+
5
+ A module is usually a mixin, so when an `#initialize` method is present it is
6
+ hard to tell initialization order and parameters so having `#initialize`
7
+ in a module is usually a bad idea.
8
+
9
+ ## Example
10
+
11
+ The `Foo` module below contains a method `initialize`. Although class `B` inherits from `A`, the inclusion of `Foo` stops `A#initialize` from being called.
12
+
13
+ ```ruby
14
+ class A
15
+ def initialize(a)
16
+ @a = a
17
+ end
18
+ end
19
+
20
+ module Foo
21
+ def initialize(foo)
22
+ @foo = foo
23
+ end
24
+ end
25
+
26
+ class B < A
27
+ include Foo
28
+
29
+ def initialize(b)
30
+ super('bar')
31
+ @b = b
32
+ end
33
+ end
34
+ ```
35
+
36
+ A simple solution is to rename `Foo#initialize` and call that method by name:
37
+
38
+ ```ruby
39
+ module Foo
40
+ def setup_foo_module(foo)
41
+ @foo = foo
42
+ end
43
+ end
44
+
45
+ class B < A
46
+ include Foo
47
+
48
+ def initialize(b)
49
+ super 'bar'
50
+ setup_foo_module('foo')
51
+ @b = b
52
+ end
53
+ end
54
+ ```
55
+
56
+ ## Current Support in Reek
57
+
58
+ Reek warns about module initialize when an instance method named `initialize` is present in a module.
59
+
60
+ ## Configuration
61
+
62
+ Module Initialize supports the [Basic Smell Options](Basic-Smell-Options.md).
@@ -0,0 +1,59 @@
1
+ # Nested Iterators
2
+
3
+ ## Introduction
4
+
5
+ A _Nested Iterator_ occurs when a block contains another block.
6
+
7
+ ## Example
8
+
9
+ Given
10
+
11
+ ```ruby
12
+ class Duck
13
+ class << self
14
+ def duck_names
15
+ %i!tick trick track!.each do |surname|
16
+ %i!duck!.each do |last_name|
17
+ puts "full name is #{surname} #{last_name}"
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ ```
24
+
25
+ Reek would report the following warning:
26
+
27
+ ```
28
+ test.rb -- 1 warning:
29
+ [5]:Duck#duck_names contains iterators nested 2 deep (NestedIterators)
30
+ ```
31
+
32
+ ## Current Support in Reek
33
+
34
+ _Nested Iterators_ reports failing methods only once.
35
+ `Object#tap` is ignored by default and thus does not count as iterator.
36
+ Furthermore iterators without block arguments are not counted, e.g.:
37
+
38
+
39
+ ```ruby
40
+ def foo
41
+ before do
42
+ item.each do |part|
43
+ puts part
44
+ end
45
+ end
46
+ end
47
+ ```
48
+
49
+ would not smell of NestedIterators (given a maximum allowed nesting of 1) since the
50
+ `before` would not be counted (because it doesn't pass any arguments to the block).
51
+
52
+ ## Configuration
53
+
54
+ _Nested Iterators_ offers the [Basic Smell Options](Basic-Smell-Options.md), plus:
55
+
56
+ | Option | Value | Effect |
57
+ | ----------------------|---------|---------|
58
+ | `max_allowed_nesting` | integer | The maximum depth of nested iterators. Defaults to 1 |
59
+ | `ignore_iterators` | Array | List of iterators to be excluded from the smell check. Includes only `tap` at the moment|
@@ -0,0 +1,47 @@
1
+ # Nil Check
2
+
3
+ ## Introduction
4
+
5
+ A _Nil Check_ is a type check. Failures of _Nil Check_ violate the
6
+ ["tell, don't ask"](http://robots.thoughtbot.com/tell-dont-ask) principle.
7
+ Additionally to that, type checks often mask bigger problems in your source
8
+ code like not using OOP and / or polymorphism when you should.
9
+
10
+ The _Nil Check_ code smell is a case of [Simulated Polymorphism](Simulated-Polymorphism.md).
11
+
12
+ ## Example
13
+
14
+ Given
15
+
16
+ ```ruby
17
+ class Klass
18
+ def nil_checker(argument)
19
+ if argument.nil?
20
+ puts "argument is nil!"
21
+ end
22
+ end
23
+ end
24
+ ```
25
+
26
+ Reek would emit the following warning:
27
+
28
+ ```
29
+ test.rb -- 1 warning:
30
+ [3]:Klass#nil_checker performs a nil-check. (NilCheck)
31
+ ```
32
+
33
+ ## Current Support in Reek
34
+
35
+ _Nil Check_ reports use of
36
+
37
+ * <code>.nil?</code> method
38
+ * <code>==</code> and <code>===</code> operators when checking vs. <code>nil</code>
39
+ * case statements that use syntax like <code>when nil</code>
40
+
41
+ _Nil Check_ allows use of
42
+
43
+ * the safe navigation operator like `foo&.bar`
44
+
45
+ ## Configuration
46
+
47
+ _Nil Check_ offers the [Basic Smell Options](Basic-Smell-Options.md).