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 as being explicit:
reek .
Additionally you can 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 a configuration file is by far the most powerful way.
There are three 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 the above: first it 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; 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
exactly one configuration file and one configuration, regardless
of how many *.reek
files you might have on your filesystem.
We put a lot of effort into making reek
's configuration as self explanatory as possible so the
best way to understand it is by looking at a simple
example (e.g. config.reek
in your project directory):
---
### Generic smell configuration
# You can disable smells completely
IrresponsibleModule:
enabled: false
# You can use filters to silence reek warnings.
# Either because you simply disagree with reek (we are not the police) or
# because you want to fix this at a later point in time.
NestedIterators:
exclude:
- "MyWorker#self.class_method" # should be refactored
- "AnotherWorker#instance_method" # should be refactored as well
# A lot of smells allow fine tuning their configuration. You can look up all available options
# in the corresponding smell documentation in /docs. In most cases you probably can just go
# with the defaults we set in config/defaults.reek.
DataClump:
max_copies: 3
min_clump_size: 3
### Directory specific configuration
# You can configure smells on a per-directory base.
# E.g. the classic Rails case: controllers smell of NestedIterators (see /docs/Nested-Iterators.md) and
# helpers smell of UtilityFunction (see docs/Utility-Function.md)
"web_app/app/controllers":
NestedIterators:
enabled: false
"web_app/app/helpers":
UtilityFunction:
enabled: false
### Excluding directories
# Directories below will not be scanned at all
exclude_paths:
- lib/legacy
- lib/rake/legacy_tasks
Note you do not need a configuration file at all. If you're fine with all the defaults we set you can skip this completely.
For more details please check out the Basic Smell Options
which are supported by every smell type. As you can see above, 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
are documented in the corresponding smell type /docs page (if you want to get a quick overview over all possible
configurations you can also check out the config/default.reek
file in this repository.
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 under Smell Suppresion.
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 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:
bundle exec rake
From then on you should check out:
If you don't feel like getting your hands dirty with code there are still other ways you can help us:
- Open up an issue and report bugs
- Suggest other improvements like additional smells for instance
reek
supports 5 output formats:
- plain text (default)
- HTML (
--format html
) - YAML (
--format yaml
, see also YAML Reports) - JSON (
--format json
) - XML (
--format xml
)
Making reek
"Rails"-friendly is fairly simple since we support directory specific configurations (directory directives
in reek
talk).
Just add this to your configuration file:
"app/controllers":
IrresponsibleModule:
enabled: false
NestedIterators:
max_allowed_nesting: 2
"app/helpers":
IrresponsibleModule:
enabled: false
UtilityFunction:
enabled: false
Be careful though, reek
does not merge your configuration entries, so if you already have a directory directive for "app/controllers" or "app/helpers" you need to update those directives instead of copying the above YAML sample into your configuration file.
- overcommit - a Git commit hook manager with support for
reek
- ruby-critic - gem that wraps around static analysis gems such as
reek
, flay and flog - pronto-reek -
reek
integration for pronto
- Colorful output for
reek
(also with Guard::Preek)
A non-exhaustive list of other static code analyzers you might want to look into:
- debride - analyze code for potentially uncalled / dead methods
- flay - analyze code for structural similarities
- flog - reports the most tortured code in an easy to read pain report
- SandiMeter - checking your Ruby code for Sandi Metz' four rules
- ruby-lint - static code analysis tool
- Fasterer - Fasterer will suggest some speed improvements based on fast-ruby
The reek
core team consists of:
The original author of reek
is Kevin Rutherford.
Notable contributions came from: