Rubyのコードスメルチェックツールreekで「こんなコードは嫌!」っていうのを防ごう

  • 投稿者:
  • 投稿の最終変更日:2020-11-25
  • 投稿カテゴリー:Other
  • Reading time:3 mins read

reekというRubyのコードスメルチェックツールを皆さんご存じですか?
この記事では、Code Smellチェックツールであるreekの利用方法を説明します。

Table Of Contents

  • こんなコードは嫌!なコードを検出する
  • reekのインストール・基本的な使い方
  • 多種多様なCode Smellたち
  • さすがに厳しすぎ…?なチェック項目
  • 適度にチェック項目をゆるくした.reekファイルサンプル

こんなコードは嫌!なコードを検出する

reekは、rubyアプリのコードに潜んでいる「臭う」部分を指摘してくれるチェックツールです。

以下の様なコードを目にしたら、どのように思うでしょうか。

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

ネストが深く、読みづらいと考える人も多いように思えます。

また、以下の様なコードを目にしたら、どのように思うでしょうか。

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

無駄は無いかもしれませんが、少し行数が多いですね。

reekは、こういった、「なんとなく嫌な感じがする」・「臭う(smell)」ようなコードを検出して警告を出してくれます。

Smellは、reekのドキュメントによると、「コードが読みにくい、または保守しづらい場所を示唆するもの(indicators of where your code might be hard to read, maintain or evolve)」とのことです。

現在、28個ほどのSmell(チェック項目)が定義されています。

基本的な使い方

reekは、gemで提供されます。

$ gem install reek` でインストールできます。

これより先の説明は、以下のバージョンで動作確認しています。

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

実行コマンドは以下のとおりです。
以下、Bundlerを利用している場合は適宜bundle execを先頭につけてください。

$ reek [options] [path]

htmlに結果を出力する

先ほどの、「なんとなく臭う感じのコード」をdemo.rbというファイル名で保存しておきます。

同じディレクトリで、以下のコマンドを実行します。

$ reek -f html demo.rb
Html file saved

今回はレポートフォーマットをhtmlとして結果出力してみます。

-f(--format)オプションで出力形式を指定できます。

上記コマンドを実行するとカレントディレクトリにreek.htmlというファイルが出力されます。

ブラウザで開いてみると以下の様な画面で確認することが出来ます。

$ open reek.html

alt="reek output"

duck_names contains iterators nested 3 deep (NestedIterators)
Line [4]
demo.rb
password has approx 6 statements (TooManyStatements)
Line [11]
demo.rb

上記の通り、2つの警告が確認出ました。

以下のとおりに解釈できます。

1つ目は、demo.rbの4行目付近のduck_namesというメソッドに、NextedIterators というCode Smellによる警告が出ています。

これは「イテレータによるネストが多い」ということを検出したものです。

2つ目は、demo.rbの11行目付近のparseというメソッドに、TooManyStatementsというCode Smellによる警告が出ています。

これは、「1つのメソッドに命令文が多い」という警告です。

reekがチェックするさまざまなチェック項目(Code Smell)は、reekのdocsで1項目1ページ毎に解説されており、そこで確認することが出来ます。

試しに、この2つ目の警告「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
# モデル側
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

このあと$ reek demo.rb を実行すると警告がなくなっていることが確認できます。

YAMLファイル(.reekファイル)によるカスタマイズ方法

reekでは、他のメトリクスツール(RuboCoprails_best_practices)と同様に、YAMLファイルによって検出内容を設定することが可能です。

このYAMLファイルは、reekコマンドを実行するディレクトリにファイル名が.reekで終わるファイルとして保存しておけばOKです。

YAMLの設定例として、先ほどのNestedIteratorsというCode Smellについてカスタマイズしたい場合についてを示します。

単純にNestedIteratorsの警告を全て抑えるには、以下のようにenabled: falseとします。

NestedIterators:
enabled: false

また、NestedIteratorsにおいて、ある特定のメソッド名だけはチェック対象としない(exclude)、という設定もできます。

以下は#duck_namesという名前のメソッド、または、AboutControllerにおける#indexメソッドにおいてはチェック対象としないという設定です。

NestedIterators:
exclude:
- duck_names
- AboutController#index

また、Code Smellの種類ごとに、追加で渡せるオプションが定義されています。
詳しくは個々のsmellについてのドキュメント(NestedIteratorsの例はこちら)を参照してみてください。

多種多様なCode Smellたち

reekでは現在28個ほどのCode Smellが定義されています。

その中からreekならではのユニークなものを4つ、ピックアップして説明します。

Data Clump

引数の名前をチェックして、同じような引数名の組が複数箇所で使われていると警告を出します。

警告が出るケース:

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

何度も出てくる同じような引数名の組(y1, y2)は、オブジェクト化したほうが良いという示唆を与えます。

Utility Function

インスタンスの状態に依存していないインスタンスメソッドに対し、警告を出します。

警告が出るケース:

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

このメソッドはargumentの状態にしか依らないので、このメソッド自体がargument側にあるべきではないかということを示唆しています。

Feature Envy

ある別のオブジェクトのメソッドばかりを呼び出していると警告を出します。

警告が出るケース:

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

このメソッドは、@itemの機能(feature)ばかりに興味があり、まるで妬んでいる(envy)かのようです。

この税込み計算のメソッドは、@item側に置くことを検討すべきではないか、ということを示唆しています。

Prima Donna Method

bang(!)付きのメソッド名で、かつ、同じ名前でbang無しのものが定義されていなかった場合、警告します。

(!)は同名の(!)なしのメソッドがあり、危険な方であるということを示す際にのみ使うべき、という思想のようです。

警告が出るケース:

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

foo!は警告されませんが、bar!barが無いため、警告されます。

このあたりはコーディングスタイルともいえそうな部分なので、プロジェクトごとに採用不採用を検討してもよいと考えます。

さすがに厳しすぎ…? なチェック項目

デフォルトでそのまま使うと、警告が出すぎて煩わしさを感じてしまうと思われるものを紹介します。

IrresponsibleModule

クラスの説明のコメントが無いと警告が発生するチェック項目です。

コメントを書いていないプロジェクトではオフにしたほうが良いと思われます。

TooManyStatements

メソッドの中に命令文が多すぎると警告を出します。

デフォルトが許容量が6と、厳しい設定になっているので、10ぐらいにするのが良いと思われます。

NestedIterators

イテレータによるネストが多いと警告を出します。

デフォルトの許容量が1と、厳しい設定になっているので、3ぐらいにするのが良いと思われます。

DuplicateMethodCall

同じ引数で何度も同じメソッドを呼ぶと警告になります。

例えば

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: ... } # 1と同じ内容
format.json { render json: ... } # 2と同じ内容
end
end
end
end

のというふうにしたときに、警告が出てしまいます。

デフォルトの許容量が1と、厳しい設定になっているので、2ぐらいにするのが良いと思われます。

.reekファイルのおすすめの設定

以上を踏まえ、適度にチェック項目をゆるくした.reekファイルサンプルを下記に示します。

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

この他にも、プロジェクトのコーディングスタイルにマッチしなかったりするものは、enabled: falseにすることなどを検討してもよいでしょう。

まとめ

いかがでしたでしょうか。
本エントリーでは、Rubyのコードスメルチェックツール、reekというgemの使い方、設定例などをご紹介させて頂きました。

継続的に使っていきたい方やチームに対して導入していきたい方はぜひSideCI上で動くreekをお試し下さい!

GitHub Pull Request x reek による自動コードレビューが出来る唯一のウェブサービスです。(本記事投稿時点)

1件のコメントがあります

  1. アバター
    matsubobo

    reekのrails向け設定のテンプレを見たことありますか?
    railsでreekを使うと、自動生成されたコードでかなりうるさく言われてしまうので、テンプレがあると嬉しいなぁと思ってます。

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください