reek
is a tool that examines Ruby classes, modules and methods and reports any Code Smells it finds. Install it like this:
gem install reek
and run it like this:
reek [options] [dir_or_source_file]*
Imagine a source file demo.rb containing:
class Dirty
# This method smells of :reek:NestedIterators but ignores them
def awful(x, y, offset = 0, log = false)
puts @screen.title
@screen = widgets.map {|w| w.each {|key| key += 3 * x}}
puts @screen.contents
end
end
Reek will report the following code smells in this file:
$ reek demo.rb
demo.rb -- 8 warnings:
[1]:Dirty has no descriptive comment (IrresponsibleModule)
[3]:Dirty#awful has 4 parameters (LongParameterList)
[3]:Dirty#awful has boolean parameter 'log' (BooleanParameter)
[3]:Dirty#awful has the parameter name 'x' (UncommunicativeParameterName)
[5]:Dirty#awful has the variable name 'w' (UncommunicativeVariableName)
[3]:Dirty#awful has unused parameter 'log' (UnusedParameters)
[3]:Dirty#awful has unused parameter 'offset' (UnusedParameters)
[3]:Dirty#awful has unused parameter 'y' (UnusedParameters)
There are multiple ways you can have reek
work on sources, the most common one just being
reek lib/
If you don't pass any source arguments to reek
it just takes the current working directory as source.
So
reek
is the exact same thing like being explicit:
reek .
Additionally can you pipe code to reek like this:
echo "class C; def m; end; end" | reek
This would print out:
$stdin -- 3 warnings:
[1]:C has no descriptive comment (IrresponsibleModule)
[1]:C has the name 'C' (UncommunicativeModuleName)
[1]:C#m has the name 'm' (UncommunicativeMethodName)
reek
currently includes checks for some aspects of Control Couple, Data Clump, Feature Envy, Large Class, Long Parameter List, Simulated Polymorphism, Too Many Statements, Uncommunicative Name, Unused Parameters and more. See the Code Smells for up to date details of exactly what reek
will check in your code.
For a basic overview, run
reek --help
For a summary of those CLI options see Command-Line Options.
Configuring reek
via configuration file is by far the most powerful way.
There are 3 ways of passing reek
a configuration file:
- Using the cli "-c" switch (see "Command line interface" above)
- Having a file ending with .reek either in your current working directory or in a parent directory (more on that later)
- Having a file ending with .reek in your HOME directory
The order in which reek
tries to find such a configuration file is exactly like above: First reek
checks if we have given it a configuration file explicitly via CLI. Then it checks the current working directory for a file and if it can't find one, it traverses up the directories until it hits the root directory. And lastly, it checks your HOME directory.
As soon as reek
detects a configuration file it stops searching immediately, meaning that from reek
's point of view there exists one configuration file and one configuration only regardless of how many ".reek" files you might have on your filesystem.
The first thing you probably want to check out are the Basic Smell Options which are supported by every smell type. Certain smell types offer a configuration that goes beyond that of the basic smell options - for instance Data Clump. All options that go beyond the Basic Smell Options should be documented in the corresponding smell type wiki page but if you want to get a quick and full overview over all possible configurations you can always check out the default.reek file in this repository.
Here's an excerpt of a reek
configuration file from a commercial project:
---
IrresponsibleModule:
enabled: false
NestedIterators:
exclude:
- "ActiveModelErrorAdder#self.run" # should be refactored
- "BookingRequests::Transfer#remote_validation"
- "BookingRequestsController#vehicle_options" # respond_to block
- "Content::Base#self.expose_fields" # unavoidable due to metaprogramming
DataClump:
max_copies: 3
min_clump_size: 3
reek
is not the police. In case you need to suppress a smell warning and you can't or don't want to use configuration files for whatever reasons you can also use source code comments like this:
# This method smells of :reek:NestedIterators
def smelly_method foo
foo.each {|bar| bar.each {|baz| baz.qux}}
end
This is further explained here
Besides the obvious
reek [options] [dir_or_source_file]*
there are quite a few other ways how to use reek in your projects:
- Use
reek
's Rake Task to automate detecting code smells - Add
reek
's custom matcher to your RSpec examples - Include
reek
using the Developer API
The first thing you want to do after checking out the source code is to run bundler
bundle install
and then to run the tests:
bundle exec rspec spec/your/file_spec.rb # Runs all tests in spec/your/file_spec.rb
bundle exec rspec spec/your/file_spec.rb:23 # Runs test in line 23
bundle exec cucumber features/your_file.feature # Runs all scenarios in your_file.feature
bundle exec cucumber features/your_file.feature:23 # Runs scenario at line 23
Or just run the whole test suite by running
bundle exec rake
From then on continue by following the establish pull request workflow.
If you don't feel like getting your hands dirty with code there are still other ways you can help us:
reek
supports 3 output formats:
- plain text (default)
- html (
--format html
) - yaml (
--format yaml
) - json (
--format json
)
- Vim plugin for
reek
- TextMate Bundle for
reek
- Colorful output for
reek
(also with Guard::Preek) - Atom plugin for
reek
A non exhaustive list:
- Kevin Rutherford
- Matijs van Zuijlen
- Andrew Wagner
- Gilles Leblanc
- Timo Rößner