docs/How-To-Write-New-Detectors.md
## How to write new detectors
### Outline what you have in mind
Before starting to code you should discuss the overall idea for your new smell detector with
us in a corresponding github issue.
We all should have a solid understanding of what this detector actually reports, the edge cases
it covers and the overall rationale behind it.
### Structure
All smell detectors reside in `lib/reek/smell_detectors` and have the following base structure:
```ruby
require_relative 'base_detector'
require_relative 'smell_warning'
module Reek
module SmellDetectors
#
# Here goes your introduction for this detector.
#
# See {file:docs/Your-Detector.md} for details.
class YourDetector < BaseDetector
def self.contexts
[:class] # In case you're operating on class contexts only - just an example.
end
#
# Here you should document what you expect the detector's context to look
# like.
#
# @return [Array<SmellWarning>]
#
def sniff
# "found_smells" below is just an abstraction for
# "find the smells in question" and iterate over them.
# This can just be a method but it can also be a more sophisticated set up.
# Check out other smell detectors to get a feeling for what to do here.
found_smells.map do |smell|
# "smell_warning" is defined in BaseDetector and should be used by you
# to construct smell warnings
smell_warning(
lines: [], # lines on which the smell was detected
message: "...", # the message that is printed on STDOUT
# whatever you interpolate into the "message" should go into
# parameters below - if you do not interpolate anything you
# can omit this
parameters: { })
end
end
end
private
# Here goes everything you need for finding smells.
end
end
end
```
For your detector to be properly loaded you need to require it in `lib/reek/smell_detectors.rb` as well.
### defaults.reek.yml
After you ran
```
bundle exec rake
```
for the first time with your shiny new detector in place the `docs/defaults.reek.yml`
file should have been updated automatically. Make sure you don't forget to check
in those changes as well.
### Documentation
* Above every `SmellDetector::sniff` method it should be documented what the expected AST is
* Every detector should have a separate documentation page in /docs. You can
take any arbitrary existing smell detector documentation page as template (since
they all have the same structure already)
* The detector should be listed under [Code Smells](docs/Code-Smells.md)
* Depending on what your detector does it might make sense to add it to other doc pages as
well e.g. [Simulated Polymorphism](docs/Simulated-Polymorphism.md)
### Rspec examples
All smell detector specs start out with 2 generic examples like below - the second one
only if it makes sense.
Here's what it looks like for `UncommunicativeVariableName`:
```ruby
it 'reports the right values' do
src = <<-RUBY
def alfa
bravo = 5
end
RUBY
expect(src).to reek_of(:UncommunicativeVariableName,
lines: [2],
context: 'alfa',
message: "has the variable name 'bravo'",
source: 'string',
name: 'bravo')
end
it 'does count all occurrences' do
src = <<-RUBY
def alfa
bravo = 3
charlie = 7
end
RUBY
expect(src).to reek_of(:UncommunicativeVariableName,
lines: [2],
name: 'bravo')
expect(src).to reek_of(:UncommunicativeVariableName,
lines: [3],
name: 'charlie')
end
```
The following examples should then cover the detector specific features.
### Schema validation
We make use of [dry-schema](https://github.com/dry-rb/dry-schema) to validate the
the reek configuration file (usually named `.reek.yml` in the root of a project).
The validation will warn reek users of missing or incorrectly defined configuration.
If you add or modify a detector you will need to make sure that you update the
[schema](lib/reek/configuration/schema.rb) file. For help with the schema syntax
you can refer to the [dry-schema documentation](https://dry-rb.org/gems/dry-schema).
You can also take a look at the existing schema rules for they all look fairly
similar.
### Cucumber features
We are trying to write as few Cucumber features as possible.
Normally, there should be no need to write a new feature for a new smell detector.
If you feel like this is necessary in this case, please discuss this with us via
github issue or in your work-in-progress pull request before doing anything.