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.
All smell detectors reside in lib/reek/smell_detectors
and have the following base structure:
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.
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.
- 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
- Depending on what your detector does it might make sense to add it to other doc pages as well e.g. Simulated Polymorphism
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
:
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.
We make use of 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 file. For help with the schema syntax you can refer to the dry-schema documentation. You can also take a look at the existing schema rules for they all look fairly similar.
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.