読者です 読者をやめる 読者になる 読者になる

SideCI Blog

自動コードレビューサービスSideCIを提供している株式会社アクトキャットのコーポレートブログです。



RuboCopをRailsオプションやLintオプションで使ってみよう

Ruby Other

RuboCopというRuby向けの静的解析ツールについてご紹介致します。
SideCIでも昨日からRuboCopによる自動コードレビュー機能がお使いになれるようになりました。ぜひこの機会にRuboCopをお試し頂ければと思います。

RuboCopについて、RuboCopのRails向けオプション、バグに繋がりやすい内容だけを検出するlintオプションなど、規約以外の部分で役に立つオプションについても紹介しておりますので、ぜひご一読下さい!

Table Of Contents

  • RuboCopとは
  • RuboCopを利用するメリット
  • RuboCopのインストール
  • RuboCopの基本的な使い方
  • -R(--rails)オプション
  • -l(--lint)オプション
  • .rubocop.ymlの書き方について
  • .rubocop_todo.ymlの使い方
  • -a(--auto-correct)オプション
  • .rubocop.ymlの例

RuboCopとは

RuboCopは、あなたのプロジェクトのrubyコードが「コーディング規約どおりに書かれているか」をチェックする静的コード解析ツールです。

デフォルトのコーディング規約はRuby style guideに基いています。 (日本語訳)

設定ファイル(.rubocop.yml)を編集することにより、自分自身でコーディング規約を追加したり削除することができます。

RuboCopを利用するメリット

Rubocopの設定は.rubocop.ymlに集中するので、自然とプロジェクトのコーディング規約が明文化されます。

Rubocopを活用しコードをきれいに保つことで、瑣末な指摘が減り、レビューの効率化につながります。

RuboCopのインストール

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

Bundlerを利用する場合、Gemfileに

gem 'rubocop', require: false

を追加します。

railsのようなWEBアプリケーションの場合、 大抵はdevelopment環境だけで動かすことが多いと思われますので、以下のようで良いでしょう。

group 'development' do
  ...
  gem 'rubocop', require: false
end

Gemfileを編集した後、

$ bundle

でインストールできます。

Bundlerを利用しない場合、$ gem install rubocop でインストールできます。

RuboCopの基本的な使い方

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

  • ruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]
  • rubocop 0.29.1

rubocopコマンド

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

$ rubocop [options] [file1, file2, ...]
  • プロジェクトの中のrubyファイルを全てチェックするには、単に以下のようにします。
$ rubocop
  • ヘルプを参照するには、以下のようにします。
$ rubocop --help

rubocopの実行~出力結果の解釈

例として、以下の様なrubyファイル(test.rb)を準備します。

def badName
  if something
    test
    end
end

このtest.rbに対してRuboCopを実行します。

$ rubocop test.rb

出力は以下のようになります。

Inspecting 1 file
W

Offenses:

test.rb:1:5: C: Use snake_case for method names.
def badName
    ^^^^^^^
test.rb:2:3: C: Use a guard clause instead of wrapping the code inside a conditional expression.
  if something
  ^^
test.rb:2:3: C: Favor modifier if usage when having a single-line body. Another good alternative is the usage of control flow &&/||.
  if something
  ^^
test.rb:4:5: W: end at 4, 4 is not aligned with if at 2, 2
    end
    ^^^

1 file inspected, 4 offenses detected

上記の出力は、以下のように解釈できます。

  • 1行目のInspecting 1 fileは、チェックしたrubyファイルの数を表しています。

  • 2行目のWは、チェックしたrubyファイルごとに見つかった違反(Convention, Warning, Error, Fatalの順に深刻)で一番深刻なものを頭文字1文字で表します。 今回の例では4個の違反が見つかり、一番深刻なものはWarningでしたので、その頭文字のWが出力されています。

  • 3行目以降は、各違反ごとに詳しい箇所(ファイル名:行箇所:列箇所)と違反内容が出力されています。

  • 最後の行は、何個のファイルがチェックされ、何個の違反があったかが出力されています。

では、以下のように全ての違反内容を修正した場合の出力を見てみましょう。

def good_name
  test if something
end
$ rubocop test.rb
Inspecting 1 file
.

1 file inspected, no offenses detected

no offenses detected、つまりコーディング違反は0になりました。

-R(--rails)オプション

railsプロジェクトの場合、実行時に-Rオプションをつけることで、 追加としてRails Cop(railsに特化したCop)が実行されます。

以下のrubyファイル(test.rb)はRuboCopデフォルトではコーディング違反はありませんが、

def good_name
  puts 'test' if something
end

これをrailsのapp/modelsの下に配置し、-Rをつけて実行すると

$ rubocop -R app/models/test.rb
Inspecting 1 file
C

Offenses:

app/models/test.rb:2:3: C: Do not write to stdout. Use Rails' logger if you want to log.
  print 'test' if something
  ^^^^^

1 file inspected, 1 offense detected

と、「標準出力ではなくRailsのロガーを使え」というRails特有の違反が出力されることがわかります。

Rails Copはgemのlib/rubocop/cop/rails以下に定義されていますので、内容についてはここを参照ください。

また、.rubocop.yml内で

AllCops:
  RunRailsCops: true

とすることで、毎回-Rオプションを指定しているのと同様、常にRails Copを有効にできます。

-l(--lint)オプション

RuboCopでのそれぞれのチェック規則(Cop)は以下の4つに分類されます。

  • Style (スタイルについてのCop)
  • Lint (誤りである可能性が高い部分やbad practiceを指摘するCop)
  • Metrics (クラスの行数や1行の文字数などに関してのCop)
  • Rails (Rails特有のCop)

-lオプションは、このうちのLintのみをチェックします。

.rubocop.ymlの書き方について

プロジェクトのルートディレクトリに.rubocop.ymlという設定ファイルを配置すると、 RuboCopはこれを自動的に読み取ります。または、-cオプションで任意の場所にあるymlファイルを設定ファイルとして指定することも可能です。

以下、.rubocop.yml内における、代表的な書き方を説明します。

RuboCopの対象から除外するファイルを指定する

railsが自動的に生成するファイル(db/schema.rb)や、vendor/bundle以下に配置されるgemなどを、 RuboCopの対象から除外したい場合は、Exclude:を利用します。

例えばdb/schema.rbというファイルをRuboCopの対象から外す場合、以下のようにします。

AllCops:
  Exclude:
    - db/schema.rb

ただし、このように書くと、デフォルトで設定されているExcludeが無効化されてしまいます。 そのため、デフォルトExcludeされている'vendor/**/*'も引き続きしたい場合には、

AllCops:
  Exclude:
    - db/schema.rb
    - 'vendor/**/*'

というように記載しないと'vendor/**/*'が除外対象になりません。

参考: rubocop gemを使うためにたった1つの重要なこと

Copの無効化/有効化(Enabled: false/true)

あるCopが、プロジェクトにそぐわないと考えられる場合、個別に無効化することが出来ます。

例えばStyle/StringLiteralsというCopがあります。 これは「変数展開が不要なStringのリテラルについては、常にシングルクオートを利用する」というルールです。

これを無効化する場合、以下のように、Copの名前に続けてEnabled: falseとします。

Style/TrailingComma:
  Enabled: false

オプションのあるCopの設定例

Copによっては、設定できるパラメータ(Configuration parameters)が決まっているものがあります。 例えば前出のStyle/StringLiteralsというCopは、変数展開が不要なStringのリテラルについて、 常にシングルクオートを用いる(デフォルト)か、常にダブルクオートを用いるか、 という2種類の選択肢があります。

EnforcedStyle: single_quotes # デフォルト
EnforcedStyle: double_quotes

デフォルトはsingle_quotesですが、double_quotesのほうが好みの場合は

Style/StringLiterals:
  EnforcedStyle: double_quotes

というようにします。

その他の設定

その他の設定については、rubocop gemの中のconfig/default.yml(githubのソース) にデフォルトのルールがありますので、そこを参照してください。

.rubocop_todo.ymlの使い方

もし直さなければならない違反の数がとても多い場合は、.rubocop_todo.ymlというファイルを生成し、それをTODOリストとして活用するという方法があります。

.rubocop_todo.yml--auto-gen-configオプションを使って簡単に生成することが出来ます。

先ほど作成したtest.rbという名前のファイルをもう一度以下の内容で作成します。

def badName
  if something
    test
    end
end

つぎに--auto-gen-configオプションをつけてrubocopを実行します。

$ rubocop --auto-gen-config test.rb

すると.rubocop_todo.ymlというファイルが生成されます。 内容は下記のようなものです。

$ cat .rubocop_todo.yml

# This configuration was generated by `rubocop --auto-gen-config`
# on 2015-03-09 19:07:44 +0900 using RuboCop version 0.29.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.

# Offense count: 1
# Configuration parameters: SupportedStyles.
Lint/EndAlignment:
  AlignWith: variable

# Offense count: 1
# Configuration parameters: MinBodyLength.
Style/GuardClause:
  Enabled: false

....

このymlファイルは、.rubocop.ymlと同じ様式のymlファイルであり、 現状のコードが違反したCopをそれぞれ無効化する設定を出力します。

こうして生成されたファイルを.rubocop.yml側から呼び出します。

inherit_from: .rubocop_todo.yml

これにより

  • 「守るべきルール」である.rubocop.yml

  • 「今後直すべき違反」である.rubocop_todo.yml

とを明示的に分けることができます。

違反を直す作業にとりかかる際は、.rubocop_todo.ymlにある項目を1つずつ消しながら修正する、というようにすれば良いでしょう。

-a(--auto-correct)オプション

一部の違反については、-aオプションをつけてrubocopを実行することで、コードが自動的に修正することができます。

--auto-gen-configオプションを使って生成された.rubocop_todo.ymlの中に、auto-correct可能なものはCop supports --auto-correctと書かれています。

ただし、この機能は実験的な機能とのことなので、注意が必要です。

.rubocop.ymlの例

オープンソースで公開されている.rubocop.ymlの例を挙げます。

最後に

RuboCopはSideCI上でもご利用頂けます。GitHub上でPull RequestをOpen, もしくは更新した際に、RuboCopがSideCI上で自動的に実行され、Pull Request上に指摘内容がコメントされます。

.rubocop.ymlでのカスタマイズを行わない場合には、基本的にはRuboCop標準の設定で解析は行われます。また、プロジェクトがRailsの場合には、RailsかどうかをSideCI側で自動判定し、RuboCopをRails向けの-Rオプションにて実行します。

ぜひSideCIをご試用下さい。
https://www.sideci.com/