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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +20 -0
- data/LICENSE +21 -0
- data/README.md +49 -0
- data/data/reek_docs/API.md +174 -0
- data/data/reek_docs/Attribute.md +39 -0
- data/data/reek_docs/Basic-Smell-Options.md +85 -0
- data/data/reek_docs/Boolean-Parameter.md +54 -0
- data/data/reek_docs/Class-Variable.md +40 -0
- data/data/reek_docs/Code-Smells.md +39 -0
- data/data/reek_docs/Command-Line-Options.md +119 -0
- data/data/reek_docs/Control-Couple.md +26 -0
- data/data/reek_docs/Control-Parameter.md +32 -0
- data/data/reek_docs/Data-Clump.md +46 -0
- data/data/reek_docs/Duplicate-Method-Call.md +264 -0
- data/data/reek_docs/Feature-Envy.md +93 -0
- data/data/reek_docs/How-To-Write-New-Detectors.md +144 -0
- data/data/reek_docs/How-reek-works-internally.md +114 -0
- data/data/reek_docs/Instance-Variable-Assumption.md +163 -0
- data/data/reek_docs/Irresponsible-Module.md +47 -0
- data/data/reek_docs/LICENSE +20 -0
- data/data/reek_docs/Large-Class.md +16 -0
- data/data/reek_docs/Long-Parameter-List.md +39 -0
- data/data/reek_docs/Long-Yield-List.md +37 -0
- data/data/reek_docs/Manual-Dispatch.md +30 -0
- data/data/reek_docs/Missing-Safe-Method.md +92 -0
- data/data/reek_docs/Module-Initialize.md +62 -0
- data/data/reek_docs/Nested-Iterators.md +59 -0
- data/data/reek_docs/Nil-Check.md +47 -0
- data/data/reek_docs/RSpec-matchers.md +129 -0
- data/data/reek_docs/Rake-Task.md +66 -0
- data/data/reek_docs/Reek-4-to-Reek-5-migration.md +188 -0
- data/data/reek_docs/Reek-Driven-Development.md +46 -0
- data/data/reek_docs/Repeated-Conditional.md +47 -0
- data/data/reek_docs/Simulated-Polymorphism.md +16 -0
- data/data/reek_docs/Smell-Suppression.md +96 -0
- data/data/reek_docs/Style-Guide.md +19 -0
- data/data/reek_docs/Subclassed-From-Core-Class.md +79 -0
- data/data/reek_docs/Too-Many-Constants.md +37 -0
- data/data/reek_docs/Too-Many-Instance-Variables.md +43 -0
- data/data/reek_docs/Too-Many-Methods.md +56 -0
- data/data/reek_docs/Too-Many-Statements.md +54 -0
- data/data/reek_docs/Uncommunicative-Method-Name.md +94 -0
- data/data/reek_docs/Uncommunicative-Module-Name.md +92 -0
- data/data/reek_docs/Uncommunicative-Name.md +18 -0
- data/data/reek_docs/Uncommunicative-Parameter-Name.md +90 -0
- data/data/reek_docs/Uncommunicative-Variable-Name.md +96 -0
- data/data/reek_docs/Unused-Parameters.md +28 -0
- data/data/reek_docs/Unused-Private-Method.md +101 -0
- data/data/reek_docs/Utility-Function.md +57 -0
- data/data/reek_docs/Versioning-Policy.md +7 -0
- data/data/reek_docs/YAML-Reports.md +93 -0
- data/exe/snoot +5 -0
- data/lib/snoot/analyse_run/decision.rb +62 -0
- data/lib/snoot/analyse_run/result.rb +12 -0
- data/lib/snoot/analyse_run.rb +70 -0
- data/lib/snoot/analyser_orchestration/default.rb +149 -0
- data/lib/snoot/analyser_orchestration/result_mapping.rb +52 -0
- data/lib/snoot/analyser_orchestration.rb +21 -0
- data/lib/snoot/analyser_result.rb +14 -0
- data/lib/snoot/cli/event.rb +13 -0
- data/lib/snoot/cli/pipeline.rb +14 -0
- data/lib/snoot/cli.rb +147 -0
- data/lib/snoot/findings.rb +23 -0
- data/lib/snoot/render_report.rb +82 -0
- data/lib/snoot/run.rb +35 -0
- data/lib/snoot/state_error.rb +9 -0
- data/lib/snoot/value_types.rb +20 -0
- data/lib/snoot/version.rb +5 -0
- data/lib/snoot.rb +21 -0
- data/snoot.allium +482 -0
- metadata +160 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 2a358948bce28c9ff2446d748bbf7a387946f93acf7f332ec25eecc63a0f4700
|
|
4
|
+
data.tar.gz: 0f11f20fe357b26d27fb29459188c958d5804ae64e86ffee6b112a584b28fd87
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 66266a46163b0b322ee44e2ad384fa1b3b44bd5e26c42bb3770cf0d2657dac72ef0da51deb1282d1367ffacfbd50bb1afa279ac2bbdbcab43a4b617d51918530
|
|
7
|
+
data.tar.gz: 734c2edb2cbe119d6588e751e1ac1c2a9de4190b4ab2929412dbf584f6abeb2fa0c3f9b01ce632bdff71bb0a1094c29d9fac15948ff7845f959b0cc889dfb678
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-05-14
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Initial release.
|
|
15
|
+
- `snoot` CLI that orchestrates reek, flog, and flay over a given path
|
|
16
|
+
set (or the current directory when no paths are given) and emits a
|
|
17
|
+
single agent-targeted finding to stdout.
|
|
18
|
+
|
|
19
|
+
[Unreleased]: https://github.com/bkudria/snoot/compare/v0.1.0...HEAD
|
|
20
|
+
[0.1.0]: https://github.com/bkudria/snoot/releases/tag/v0.1.0
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Benjamin Kudria
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# snoot
|
|
2
|
+
|
|
3
|
+
A Ruby gem that orchestrates `reek`, `flog`, and `flay` over a configured path set and emits a single agent-targeted report describing one finding.
|
|
4
|
+
|
|
5
|
+
The report is centred on an LLM coding agent as the reader: each run produces one of two report shapes -- a doc + instances pair for smell findings, or a header + finding context + doc trio for complexity and duplication findings -- or acknowledges that nothing was worth reporting, or signals analyser failure.
|
|
6
|
+
|
|
7
|
+
## Status
|
|
8
|
+
|
|
9
|
+
Pre-1.0. Three analysers (Reek / Flog / Flay) are used, and `snoot <paths>` drives the full pipeline end-to-end: it reports a single finding to stdout, an acknowledgement when nothing is worth reporting, or a failure line on stderr. With no positional arguments, `snoot` scans the current directory. The exit code is `1` when a finding is rendered, `0` when there is nothing to report, and `2` when analysis fails.
|
|
10
|
+
|
|
11
|
+
## Specification
|
|
12
|
+
|
|
13
|
+
The behaviour is specified in [`snoot.allium`](snoot.allium) (the Allium
|
|
14
|
+
behavioural contract); [`GOALS.md`](GOALS.md) records the design rationale.
|
|
15
|
+
The spec, the implementation in `lib/`, and the tests in `spec/` are peer
|
|
16
|
+
artifacts and are kept in sync — see [`CLAUDE.md`](CLAUDE.md).
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
Requires Ruby 4.0 or later.
|
|
21
|
+
|
|
22
|
+
gem install snoot
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
After install, the `snoot` executable is available on PATH:
|
|
27
|
+
|
|
28
|
+
snoot --version
|
|
29
|
+
snoot --help
|
|
30
|
+
snoot lib/foo.rb # analyse a specific path
|
|
31
|
+
snoot # scan the current directory
|
|
32
|
+
|
|
33
|
+
`snoot` emits a single agent-targeted finding to stdout and exits with:
|
|
34
|
+
|
|
35
|
+
- `0` — nothing worth reporting,
|
|
36
|
+
- `1` — a finding was rendered,
|
|
37
|
+
- `2` — analysis failed (failure line on stderr).
|
|
38
|
+
|
|
39
|
+
## Privacy
|
|
40
|
+
|
|
41
|
+
`snoot` runs entirely locally. It analyses source on disk with the bundled
|
|
42
|
+
`reek`, `flog`, and `flay` libraries and writes its single finding to stdout —
|
|
43
|
+
no source code, findings, or telemetry are sent over the network.
|
|
44
|
+
|
|
45
|
+
## License
|
|
46
|
+
|
|
47
|
+
snoot is MIT-licensed; see [`LICENSE`](LICENSE).
|
|
48
|
+
|
|
49
|
+
The reek smell-documentation files vendored under [`data/reek_docs/`](data/reek_docs/) are reproduced from [troessner/reek](https://github.com/troessner/reek) (Copyright © 2008, 2009 Kevin Rutherford) under the MIT license; see [`data/reek_docs/LICENSE`](data/reek_docs/LICENSE). Re-syncing those files with `rake docs:sync` also refreshes the bundled license notice.
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Using Reek inside your Ruby application
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
Either standalone via
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
gem install reek
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
or by adding
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
gem 'reek'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
to your Gemfile.
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
Code says more than a thousand words:
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
require 'reek'
|
|
25
|
+
|
|
26
|
+
source = <<-RUBY
|
|
27
|
+
class Dirty
|
|
28
|
+
def m(a,b,c)
|
|
29
|
+
puts a,b
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
RUBY
|
|
33
|
+
|
|
34
|
+
reporter = Reek::Report::TextReport.new
|
|
35
|
+
examiner = Reek::Examiner.new source
|
|
36
|
+
reporter.add_examiner examiner
|
|
37
|
+
reporter.show
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This would output the following on STDOUT:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
string -- 5 warnings:
|
|
44
|
+
Dirty has no descriptive comment (IrresponsibleModule)
|
|
45
|
+
Dirty#m has the name 'm' (UncommunicativeMethodName)
|
|
46
|
+
Dirty#m has the parameter name 'a' (UncommunicativeParameterName)
|
|
47
|
+
Dirty#m has the parameter name 'b' (UncommunicativeParameterName)
|
|
48
|
+
Dirty#m has unused parameter 'c' (UnusedParameters)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Note that `Reek::Examiner.new` can take `source` as `String`, `Pathname`, `File` or `IO`.
|
|
52
|
+
|
|
53
|
+
## API stability
|
|
54
|
+
|
|
55
|
+
Everything that is mentioned in this document can be considered stable in the
|
|
56
|
+
sense that it will only change across major versions.
|
|
57
|
+
|
|
58
|
+
There is one thing in this API documentation you can't and shouldn't rely on:
|
|
59
|
+
The `SmellWarning` messages itself.
|
|
60
|
+
|
|
61
|
+
Something like this
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
Dirty#m has the parameter name 'a' (UncommunicativeParameterName)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
might change even across minor versions.
|
|
68
|
+
|
|
69
|
+
You should not need to be specific about those messages anyways.
|
|
70
|
+
In case you'd like to be specific about `SmellWarnings` please have a look at
|
|
71
|
+
[accessing the smell warnings directly](#accessing-the-smell-warnings-directly).
|
|
72
|
+
|
|
73
|
+
Additionally you can use one of our structured [outputs formats](#choosing-your-output-format)
|
|
74
|
+
like JSON or YAML if you need a more fine-grained access to our
|
|
75
|
+
`SmellWarnings`.
|
|
76
|
+
|
|
77
|
+
## Choosing your output format
|
|
78
|
+
|
|
79
|
+
Besides normal text output, Reek can generate output in YAML,
|
|
80
|
+
JSON, HTML and XML by using the following Report types:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
TextReport
|
|
84
|
+
YAMLReport
|
|
85
|
+
JSONReport
|
|
86
|
+
HTMLReport
|
|
87
|
+
XMLReport
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Configuration
|
|
91
|
+
|
|
92
|
+
Given you have the following configuration file called `.reek.yml` in your root directory:
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
---
|
|
96
|
+
IrresponsibleModule:
|
|
97
|
+
enabled: false
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Reek will load this file automatically by default. If you want to load the
|
|
101
|
+
configuration explicitly, you can use one of the methods below.
|
|
102
|
+
|
|
103
|
+
You can now use either
|
|
104
|
+
|
|
105
|
+
```ruby
|
|
106
|
+
Reek::Configuration::AppConfiguration.from_path Pathname.new('config.reek')
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
but you can also pass a hash with the contents of the `.reek.yml` YAML file
|
|
110
|
+
to `Reek::Configuration::AppConfiguration.from_hash`.
|
|
111
|
+
|
|
112
|
+
Given the example above you would load that as follows:
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
require 'reek'
|
|
116
|
+
|
|
117
|
+
config_hash = { 'IrresponsibleModule' => { 'enabled' => false } }
|
|
118
|
+
configuration = Reek::Configuration::AppConfiguration.from_hash config_hash
|
|
119
|
+
|
|
120
|
+
source = <<-RUBY
|
|
121
|
+
class Dirty
|
|
122
|
+
def call_me(a,b)
|
|
123
|
+
puts a,b
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
RUBY
|
|
127
|
+
|
|
128
|
+
reporter = Reek::Report::TextReport.new
|
|
129
|
+
examiner = Reek::Examiner.new(source, configuration: configuration); nil
|
|
130
|
+
reporter.add_examiner examiner; nil
|
|
131
|
+
reporter.show
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
This would now only report `UncommunicativeParameterName` but not
|
|
135
|
+
`IrresponsibleModule` for the `Dirty` class:
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
string -- 2 warnings:
|
|
139
|
+
Dirty#call_me has the parameter name 'a' (UncommunicativeParameterName)
|
|
140
|
+
Dirty#call_me has the parameter name 'b' (UncommunicativeParameterName)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Of course, directory specific configuration and excluded paths are supported as
|
|
144
|
+
well:
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
config_hash = {
|
|
148
|
+
'IrresponsibleModule' => { 'enabled' => false }
|
|
149
|
+
'spec/samples/three_clean_files/' =>
|
|
150
|
+
{ 'UtilityFunction' => { "enabled" => false } }
|
|
151
|
+
'exclude_paths' =>
|
|
152
|
+
[ 'spec/samples/two_smelly_files' ]
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Accessing the smell warnings directly
|
|
157
|
+
|
|
158
|
+
You can also access the smells detected by an examiner directly:
|
|
159
|
+
|
|
160
|
+
```ruby
|
|
161
|
+
require 'reek'
|
|
162
|
+
|
|
163
|
+
source = <<-END
|
|
164
|
+
class C
|
|
165
|
+
end
|
|
166
|
+
END
|
|
167
|
+
|
|
168
|
+
examiner = Reek::Examiner.new source
|
|
169
|
+
examiner.smells.each do |smell|
|
|
170
|
+
puts smell.message
|
|
171
|
+
end
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
`Examiner#smells` returns a list of `SmellWarning` objects.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Attribute
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
A class that publishes a setter for an instance variable invites
|
|
6
|
+
client classes to become too intimate with its inner workings, and in
|
|
7
|
+
particular with its representation of state.
|
|
8
|
+
|
|
9
|
+
The same holds to a lesser extent for getters, but Reek doesn't flag those.
|
|
10
|
+
|
|
11
|
+
## Example
|
|
12
|
+
|
|
13
|
+
Given:
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
class Klass
|
|
17
|
+
attr_accessor :dummy
|
|
18
|
+
end
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Reek would emit the following warning:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
reek test.rb
|
|
25
|
+
|
|
26
|
+
test.rb -- 1 warning:
|
|
27
|
+
[2]:Attribute: Klass#dummy is a writable attribute
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Support in Reek
|
|
31
|
+
|
|
32
|
+
This detector raises a warning for every public `attr_writer`,
|
|
33
|
+
`attr_accessor`, and `attr` with the writable flag set to `true`.
|
|
34
|
+
|
|
35
|
+
Reek does not raise warnings for read-only attributes.
|
|
36
|
+
|
|
37
|
+
## Configuration
|
|
38
|
+
|
|
39
|
+
_Attribute_ supports only the [Basic Smell Options](Basic-Smell-Options.md).
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Basic Smell Options
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
Every smell detector in Reek offers at least the following configuration options:
|
|
6
|
+
|
|
7
|
+
| Option | Value | Effect |
|
|
8
|
+
| ---------------|-------------|---------|
|
|
9
|
+
| `enabled` | Boolean | Determines whether the smell detector is active. Defaults to `true` |
|
|
10
|
+
| `exclude` | an array of strings that will be converted into regular expressions | Ignores any context whose full description matches any element of this array. |
|
|
11
|
+
|
|
12
|
+
The file `docs/defaults.reek.yml` (shipped with the Reek gem) lists any default
|
|
13
|
+
exclusions for each smell.
|
|
14
|
+
|
|
15
|
+
## Examples
|
|
16
|
+
|
|
17
|
+
**An easy one:**
|
|
18
|
+
|
|
19
|
+
To stop Reek reporting smells in any method called `write` you might create a configuration file containing this:
|
|
20
|
+
|
|
21
|
+
```yaml
|
|
22
|
+
DuplicateMethodCall:
|
|
23
|
+
exclude:
|
|
24
|
+
- write
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Internally Reek will convert this to the Regexp /write/.
|
|
28
|
+
|
|
29
|
+
**A more sophisticated one:**
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
FeatureEnvy:
|
|
33
|
+
exclude:
|
|
34
|
+
- "MyModel#do_things"
|
|
35
|
+
- "MyHelper"
|
|
36
|
+
- "ApplicationController#respond"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
This would not report FeatureEnvy for the instance method `MyModel#do_things`, the whole module `MyHelper` and the `respond` instance method of `ApplicationController`
|
|
40
|
+
|
|
41
|
+
## Advanced configuration
|
|
42
|
+
|
|
43
|
+
Sometimes just strings are not enough for configuration. E.g. consider this code sample:
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
class Klass
|
|
47
|
+
def foo1; end
|
|
48
|
+
def foo1bar; end
|
|
49
|
+
end
|
|
50
|
+
```
|
|
51
|
+
Both "Klass#foo1" and "Klass#foo1bar" will smell of UncommunicativeMethodName. Now let's assume
|
|
52
|
+
you are ok with "Klass#foo1" but not "Klass#foo1bar".
|
|
53
|
+
Just having this configuration
|
|
54
|
+
|
|
55
|
+
```yaml
|
|
56
|
+
UncommunicativeMethodName:
|
|
57
|
+
exclude:
|
|
58
|
+
- "Klass#foo1"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
wouldn't work because now "Klass#foo1bar" wouldn't smell as well.
|
|
62
|
+
|
|
63
|
+
For this reason Reek has a special syntax that allows you to use regexes by using a forward slash at the beginning and the end of the string.
|
|
64
|
+
Everything within the forward slashes will be loaded as a regex.
|
|
65
|
+
|
|
66
|
+
A possible configuration that hat excludes "Klass#foo1" from this scan but not "Klass#foo1bar" could look like this:
|
|
67
|
+
|
|
68
|
+
```yaml
|
|
69
|
+
UncommunicativeMethodName:
|
|
70
|
+
exclude:
|
|
71
|
+
- "/Klass#foo1$/"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Reek 4
|
|
75
|
+
|
|
76
|
+
In Reek 4 you could also pass regexes to `exclude`, meaning this was perfectly valid as well:
|
|
77
|
+
|
|
78
|
+
```yaml
|
|
79
|
+
DuplicateMethodCall:
|
|
80
|
+
exclude:
|
|
81
|
+
- !ruby/regexp /write/
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Support for this has been scrapped with Reek 5 to make the Reek configuration more yaml standard compliant.
|
|
85
|
+
You can still pass in regexes, you just have to wrap them into a string. Please see "Advanced configuration" above.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Boolean Parameter
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
_Boolean Parameter_ is a case of [Control Couple](Control-Couple.md), where a
|
|
6
|
+
method parameter is defaulted to true or false. A _Boolean Parameter_
|
|
7
|
+
effectively permits a method's caller to decide which execution path to take.
|
|
8
|
+
This is a case of bad cohesion. You're creating a dependency between methods
|
|
9
|
+
that is not really necessary, thus increasing coupling.
|
|
10
|
+
|
|
11
|
+
## Example
|
|
12
|
+
|
|
13
|
+
Given
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
class Dummy
|
|
17
|
+
def hit_the_switch(switch = true)
|
|
18
|
+
if switch
|
|
19
|
+
puts 'Hitting the switch'
|
|
20
|
+
# do other things...
|
|
21
|
+
else
|
|
22
|
+
puts 'Not hitting the switch'
|
|
23
|
+
# do other things...
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Reek would emit the following warning:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
test.rb -- 3 warnings:
|
|
33
|
+
[1]:Dummy#hit_the_switch has boolean parameter 'switch' (BooleanParameter)
|
|
34
|
+
[2]:Dummy#hit_the_switch is controlled by argument switch (ControlParameter)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Note that both smells are reported, _Boolean Parameter_ and _Control Parameter_.
|
|
38
|
+
|
|
39
|
+
## Getting rid of the smell
|
|
40
|
+
|
|
41
|
+
This is highly dependent on your exact architecture, but looking at the example above what you could do is:
|
|
42
|
+
|
|
43
|
+
* Move everything in the `if` branch into a separate method
|
|
44
|
+
* Move everything in the `else` branch into a separate method
|
|
45
|
+
* Get rid of the `hit_the_switch` method altogether
|
|
46
|
+
* Make the decision what method to call in the initial caller of `hit_the_switch`
|
|
47
|
+
|
|
48
|
+
## Current support in Reek
|
|
49
|
+
|
|
50
|
+
Reek can only detect a _Boolean Parameter_ when it has a default initializer like in the example above.
|
|
51
|
+
|
|
52
|
+
## Configuration
|
|
53
|
+
|
|
54
|
+
_Boolean Parameter_ supports the [Basic Smell Options](Basic-Smell-Options.md).
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Class Variable
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
Class variables form part of the global runtime state, and as such make it easy for one part of the system to accidentally or inadvertently depend on another part of the system. So the system becomes more prone to problems where changing something over here breaks something over there. In particular, class variables can make it hard to set up tests (because the context of the test includes all global state).
|
|
6
|
+
|
|
7
|
+
For a detailed explanation, check out [this article](https://web.archive.org/web/20160714084532/http://4thmouse.com:80/index.php/2011/03/20/why-class-variables-in-ruby-are-a-bad-idea). and [Stackoverflow](https://stackoverflow.com/a/10594849/7798638)
|
|
8
|
+
|
|
9
|
+
## Example
|
|
10
|
+
|
|
11
|
+
Given
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
class Dummy
|
|
15
|
+
@@class_variable = :whatever
|
|
16
|
+
end
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Reek would emit the following warning:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
reek test.rb
|
|
23
|
+
|
|
24
|
+
test.rb -- 1 warning:
|
|
25
|
+
[2]:Dummy declares the class variable @@class_variable (ClassVariable)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Getting rid of the smell
|
|
29
|
+
|
|
30
|
+
You can use class-instance variable to mitigate the problem (as also suggested in the linked article above):
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
class Dummy
|
|
34
|
+
@class_variable = :whatever
|
|
35
|
+
end
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Configuration
|
|
39
|
+
|
|
40
|
+
_Class Variable_ supports the [Basic Smell Options](Basic-Smell-Options.md).
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Code Smells
|
|
2
|
+
|
|
3
|
+
Smells are indicators of where your code might be hard to read, maintain or evolve, rather than things that are specifically _wrong_. Naturally this means that Reek is looking towards your code's future (and that can make its reports seem somewhat subjective, of course).
|
|
4
|
+
|
|
5
|
+
Reek currently includes checks for the following smells:
|
|
6
|
+
|
|
7
|
+
* [Attribute](Attribute.md)
|
|
8
|
+
* [Class Variable](Class-Variable.md)
|
|
9
|
+
* [Control Couple](Control-Couple.md), including
|
|
10
|
+
* [Boolean Parameter](Boolean-Parameter.md)
|
|
11
|
+
* [Control Parameter](Control-Parameter.md)
|
|
12
|
+
* [Data Clump](Data-Clump.md)
|
|
13
|
+
* [Duplicate Method Call](Duplicate-Method-Call.md)
|
|
14
|
+
* [Instance Variable Assumption](Instance-Variable-Assumption.md)
|
|
15
|
+
* [Irresponsible Module](Irresponsible-Module.md)
|
|
16
|
+
* [Large Class](Large-Class.md), including
|
|
17
|
+
* [Too Many Constants](Too-Many-Constants.md)
|
|
18
|
+
* [Too Many Instance Variables](Too-Many-Instance-Variables.md)
|
|
19
|
+
* [Too Many Methods](Too-Many-Methods.md)
|
|
20
|
+
* [Long Parameter List](Long-Parameter-List.md), and its special case [Long Yield List](Long-Yield-List.md)
|
|
21
|
+
* Low Cohesion, including
|
|
22
|
+
* [Feature Envy](Feature-Envy.md)
|
|
23
|
+
* [Utility Function](Utility-Function.md)
|
|
24
|
+
* [Module Initialize](Module-Initialize.md)
|
|
25
|
+
* [Nested Iterators](Nested-Iterators.md)
|
|
26
|
+
* [Missing Safe Method](Missing-Safe-Method.md), formerly known as Prima Donna Method
|
|
27
|
+
* [Simulated Polymorphism](Simulated-Polymorphism.md), including
|
|
28
|
+
* [Manual Dispatch](Manual-Dispatch.md)
|
|
29
|
+
* [Nil Check](Nil-Check.md)
|
|
30
|
+
* [Repeated Conditional](Repeated-Conditional.md)
|
|
31
|
+
* [Subclassed From Core Class](Subclassed-From-Core-Class.md)
|
|
32
|
+
* [Too Many Statements](Too-Many-Statements.md)
|
|
33
|
+
* [Uncommunicative Name](Uncommunicative-Name.md), including
|
|
34
|
+
* [Uncommunicative Method Name](Uncommunicative-Method-Name.md)
|
|
35
|
+
* [Uncommunicative Module Name](Uncommunicative-Module-Name.md)
|
|
36
|
+
* [Uncommunicative Parameter Name](Uncommunicative-Parameter-Name.md)
|
|
37
|
+
* [Uncommunicative Variable Name](Uncommunicative-Variable-Name.md)
|
|
38
|
+
* [Unused Parameters](Unused-Parameters.md)
|
|
39
|
+
* [Unused Private Method](Unused-Private-Method.md)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Command Line Options
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
Reek follows standard Unix convention for passing arguments.
|
|
6
|
+
|
|
7
|
+
Check out
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
reek -h
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
for details.
|
|
14
|
+
|
|
15
|
+
## Telling Reek to use a specific configuration file
|
|
16
|
+
|
|
17
|
+
In case your configuration file is not in the standard location (that would be your project directory or
|
|
18
|
+
whatever directory you're running Reek from) you can specify a configuration file with the `-c` option
|
|
19
|
+
like this:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
reek -c /somewhere/on/your/filesystem/reek_config.yml lib/
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Telling Reek Which Code to Check
|
|
26
|
+
|
|
27
|
+
Probably the most standard use case would be to check all Ruby files in the lib directory:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
reek lib/*.rb
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
In general, if any command-line argument is a directory, Reek searches that directory and all sub-directories for Ruby source files. Thus
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
reek lib
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
would be equivalent to
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
reek lib/**/*.rb
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Occasionally you may want to quickly check a code snippet without going to the trouble of creating a file to hold it. You can pass the snippet directly to Reek's standard input:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
echo "def x() true end" | reek
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
To just check all Ruby files in the current directory, you can simply run it
|
|
52
|
+
with no parameters:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
reek
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Telling Reek Which Smells to Detect
|
|
59
|
+
|
|
60
|
+
You can tell Reek to only check particular smells by using the `--smell`
|
|
61
|
+
option and passing in the smell name.
|
|
62
|
+
|
|
63
|
+
For example, to only check for [Utility Function](Utility-Function.md), you
|
|
64
|
+
would use:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
reek --smell UtilityFunction
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
You can select several smells by repeating the `--smell` option like so:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
reek --smell UtilityFunction --smell UncommunicativeMethodName
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Output options
|
|
77
|
+
|
|
78
|
+
### Output smell's line number
|
|
79
|
+
|
|
80
|
+
By passing in a "-n" flag to the _reek_ command, the output will suppress the line numbers:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
$ reek -n mess.rb
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
mess.rb -- 2 warnings:
|
|
88
|
+
x doesn't depend on instance state (UtilityFunction)
|
|
89
|
+
x has the name 'x' (UncommunicativeMethodName)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Otherwise line numbers will be shown as default at the beginning of each warning in square brackets:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
$ reek mess.rb
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
mess.rb -- 2 warnings:
|
|
100
|
+
[2]:x doesn't depend on instance state (UtilityFunction)
|
|
101
|
+
[2]:x has the name 'x' (UncommunicativeMethodName)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Enable the verbose mode
|
|
105
|
+
|
|
106
|
+
_reek_ has a verbose mode which you might find helpful as a beginner. "verbose" just means that behind each warning a helpful link will be displayed which leads directly to the corresponding _reek_ documentation page.
|
|
107
|
+
This mode can be enabled via the "-U" or "--documentation" flag.
|
|
108
|
+
|
|
109
|
+
So for instance, if your test file would smell of _ClassVariable_, this is what the _reek_ output would look like:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
reek -U test.rb
|
|
113
|
+
```
|
|
114
|
+
```
|
|
115
|
+
test.rb -- 1 warning:
|
|
116
|
+
[2]:Dummy declares the class variable @@class_variable (ClassVariable) [https://github.com/troessner/reek/blob/master/docs/Class-Variable.md]
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Note the link at the end.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Control Couple
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
Control coupling occurs when a method or block checks the value of a parameter
|
|
6
|
+
in order to decide which execution path to take. The offending parameter is
|
|
7
|
+
often called a _Control Couple_.
|
|
8
|
+
|
|
9
|
+
Control Coupling is a kind of duplication, because the calling method already knows which path should be taken.
|
|
10
|
+
|
|
11
|
+
Control Coupling reduces the code's flexibility by creating a dependency
|
|
12
|
+
between the caller and callee: any change to the possible values of the
|
|
13
|
+
controlling parameter must be reflected on both sides of the call. A _Control
|
|
14
|
+
Couple_ also reveals a loss of simplicity: the called method probably has more
|
|
15
|
+
than one responsibility, because it includes at least two different code paths.
|
|
16
|
+
|
|
17
|
+
You can find a good write-up regarding this problem [here](https://solnic.codes/2012/04/11/get-rid-of-that-code-smell-control-couple/).
|
|
18
|
+
|
|
19
|
+
## Current Support in Reek
|
|
20
|
+
|
|
21
|
+
Reek performs the following checks that fall in this category:
|
|
22
|
+
|
|
23
|
+
* [Control-Parameter](Control-Parameter.md) - a method parameter or block
|
|
24
|
+
parameter is the tested value in a conditional statement
|
|
25
|
+
* [Boolean-Parameter](Boolean-Parameter.md) - a method parameter is defaulted
|
|
26
|
+
to `true` or `false`.
|