Prevent the “unpleasant codes” by Ruby code smell verification tool “reek”

  • Post author:
  • Post last modified:2020-11-25
  • Post category:Code Review
  • Reading time:7 mins read

Hi, there. Do you know anything about Ruby code smell verification tool called reek? This entry will explain you how to use Code Smell verification tool reek.

Table Of Contents

  • Detect unpleasant codes
  • How to install and use reek
  • Various types of Code Smell
  • Extremely strict… verification items
  • reek file sample which verification items are modified

Detect unpleasant codes

reek is a verification tool that identifies codes which “smell” in ruby applications.

What would you think when you look at the code below?

def duck_names
File.open(@output_path, "w") do |file|
%i!tick trick track!.each do |surname|
%i!duck!.each do |last_name|
file.puts "full name is #{surname} #{last_name}"
end
end
end
end

A lot of people may think that the nest is quite deep and it is not easy to read.

Similarly, how about the code below?

def password
if request.post?
unless @member.authenticated?(params[:password])
@member.errors.add :password, "is invalid"
return
end
@member.crypted_password = ""
@member.password = params[:new_password]
@member.password_confirmation = params[:new_password_confirmation]
@member.save
end
end

They don’t have issues but it seems that the rows are very long.

reek will identify and call out the codes which “I feel somehow unpleasant”/”smell”.

Based on the reek document, Smell refers to 「the indicators of where your code might be hard to read, maintain or evolve」.

Currently, there are around 28 verification items for Smell that are defined.

Basic usage

reek is provided via gem.

You can install by using $ gem install reek`.

The description below has been confirmed using the following versions.

  • MacOSX 10.10.3
  • ruby 2.2.2p95 (2015–04–13 revision 50295) [x86_64-darwin14]
  • reek 2.2.0

The execution command is as follows.
If you are using Bundler for the followings, pleas add bundle exec in the top.

$ reek [options] [path]

Output the results in html

Save “unpleasant codes” with a filename of demo.rb.

Run the following command in the same directory.

$ reek -f html demo.rb
Html file saved

We shall output the results in a HTML report format.

You can specify the output format with -f(--format) option.

By executing the command above, a file reek.html will be created in the current directory.

You will see the below in your browser.

$ open reek.html
Reek output sample
duck_names contains iterators nested 3 deep (NestedIterators)
Line [4]
demo.rb
password has approx 6 statements (TooManyStatements)
Line [11]
demo.rb

There will be 2 warnings as seen above.
Those mean as below.
The first one is in the method of “duck_names” near the 4th line in demo.rb file, this warning is NextedIterators.
This is saying “Too many nests by iterator”.
The second one is in the method of “parse” near the 11th line in demo.rb file, this warning is TooManyStatements.
This is saying “Too many statements in 1 method”.
The verification items (Code Smell) that reek checks are explained in reek’s docs.
Here, we shall fix the 2nd warning “TooManyStatements”.
before:

def password
if request.post?
unless @member.authenticated?(params[:password])
@member.errors.add :password, "is invalid"
return
end
@member.crypted_password = ""
@member.password = params[:new_password]
@member.password_confirmation = params[:new_password_confirmation]
@member.save
end
end

after

def password
if request.post?
unless @member.authenticated?(params[:password])
@member.errors.add :password, "is invalid"
return
end
@member.update_password(params[:new_password], params[:new_password_confirmation])
end
end
# Model side
class Member
def update_password(new_password, new_password_confirmation)
self.crypted_password = ""
self.password = new_password
self.password_confirmation = new_password_confirmation
save
end
end

You can confirm that there are no more warnings when executing $ reek demo.rb afterwards.

How to customize by using YAML file(.reek file)

Similar to other metrics tools ( RuboCop and rails_best_practices ), it is possible to configure the detection content by YAML file in reek.
You can just simply save this YAML file with an extension of .reek in the directory when the reek command is being executed.
As a YAML configuration example, we explain here in case you would like to customize the Code Smell named NestedIterators shown above.
To simply disable all the warnings for NestedIterators, configure it enabled:false as shown below.

NestedIterators:
enabled: false

Moreover, you can specify to exclude specific methods in NestedIterators by using ( exclude).
The following configuration is to exclude the verification named #duck_names method, as well as the #index method within AboutController.

NestedIterators:
exclude:
- duck_names
- AboutController#index

Additionally, the option which can be added by respective Code Smell is defined.
Please refer to below document for individual smell (NestedIterators examples here).

Various types of Code Smell

There are currently 28 Code Smell defined in reek.
We will pick up and explain the 4 unique ones among them.

Data Clump

It checks the name of the argument and warns if there are multiple areas where the same argument name is being used.
Warning cases:

class Dummy
def x(y1,y2); end
def y(y1,y2); end
def z(y1,y2); end
end

It suggests making similar arguments names (y1, y2) to an object which are repeated.

Utility Function

It warns the instance methods which are not depend on the status of instance.
Warning cases:

class Dummy
def showcase(argument)
argument.to_s + argument.to_i
end
end

Since this method only depends on the argument status, it suggests that this method should be in the argument side.

Feature Envy

It warns when only calling a method of a different object.
Warning cases:

class Cart
def price
@item.price + @item.tax
end
end

This method is interested in only the @item feature and it seems as if it envies.
It suggests placing this tax calculation method in the @item side instead.

Prima Donna Method

For instance, when there is a method named destroy!, and if a method named destroy which doesn’t have ! is defined, this method will be warned.
It means that (!) has another method which is same name but without (!), this should be used only when to indicate this one has some risk.
Warning cases:

class C
def foo; end
def foo!; end
def bar!; end
end

Although foo! isn’t be warned but since bar! lacks bar, it is warned.
Since this can be considered as coding style, you can consider to use/not to use by each projects.

Extremely strict… verification items

Here we introduce some reeks which could be too strict if you use it by default configuration.

IrresponsibleModule

This verification item warns if it lacks the comment of class description.
It is better to disable for projects that do not include comments.

TooManyStatements

This verification item warns if the methods have many statements.
The default is 6 which is quite strict, its better modify around 10 instead.

NestedIterators

It warns when there are many nests by iterator.
The default is 1 which is quite strict, its better modify around 3 instead.

DuplicateMethodCall

It warns when same argument is calling a same method over and over.
Example

class SomeController < ApplicationController
def index
...
if @feed
respond_to do |format|
format.html { render file: ... } # 1
format.json { render json: ... } # 2
end
else
respond_to do |format|
format.html { render file: ... } same contents as # 1
format.json { render json: ... } same contents as # 2
end
end
end
end

In such above case, it will warn.
The default is 1 which is quite strict, its better modify around 2 instead.

Recommended configuration for the .reek file

Based on above, this is recommended .reek file sample which is less strict.

IrresponsibleModule:
enabled: false
TooManyStatements:
enabled: true
exclude:
- initialize
max_statements: 10
NestedIterators:
max_allowed_nesting: 3
DuplicateMethodCall:
max_calls: 2

Aside from this, if some configurations are not suitable for the project coding style, you can consider to modify them to enabled: false.

Summary

How do you think about reek now?

As you read, we have introduced how to use and configure the gem called “reek” which is a code smell verification tool for Ruby.
For those who would like to use this continuously or to introduce to your team, why not trying out “reek” in Sider!
This is the only web service that can automatically review codes using GitHub Pull Request x reek. (As of the post of this article)


For more information about Sider, please go to our website.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.