【Ruby on Rails】RuboCopでコードの治安を守ろう

## まえがき

本記事は、**[DMM WEBCAMP Advent Calendar 2023](https://qiita.com/advent-calendar/2023/infratop)** 5日目記事です。

Ruby on Railsを中心に、DWCメンター・卒業生が記事を投稿しておりますので、是非他の記事もご確認ください！

## はじめに

こんにちは、DMM WEBCAMP メンターの @ukwhatn です。

本カレンダー3回目の担当日となりますが、今日はRuby on RailsにおけるRuboCopの使い方について説明していきます。

## Rubocopとは？

[RuboCop](https://github.com/rubocop/rubocop)とは、Ruby向けのlinter、つまり静的コード解析ツールです。

:::note warn
**待ってよ、そもそもlinterって？**

linter(リンター)とは、ソースコードを実行前に解析することで、事前にバグやコーディングミスを検出したり、コミュニティやプロジェクトが定めたコーディング規約から逸脱している箇所を警告してくれるツールです。

日本語では一般に「静的解析」と言ったりします。

全てのバグを発見してくれるわけではありませんが、少なくともポカミスによるバグやエラーを防いだり、PJ内で最低限維持するべき秩序を守ってくれることが期待されます。
:::

ロボコップではありません。
ロゴ（下記）から考えて明らかに元ネタにはしていますが、ルボコップです。

> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428968/389596f9-5f6d-b30b-828e-145483c79867.png)
> source: https://github.com/rubocop/rubocop


RuboCopのデフォルト設定では、[Ruby Style Guide](https://rubystyle.guide/)に記載されている規約から逸脱したコードを警告対象としています。

## RailsにおけるRuboCopの使い方

### インストール

RailsでRuboCopを使うときは、Gemfileに追加するのが楽です。
スタンドアロンで動作するツールなので、`require: false`をつけ、`development/test`グループに追加します。
```rb
group :development, :test do
  # ...
  # 元々あるGem
  # ...
  gem "rubocop", require:false
  gem "rubocop-performance", require:false
  gem "rubocop-rails", require:false
  gem "rubocop-rspec", require:false
end
```
:::note warn
**4つもGemを入れるの？**

かつてのRuboCopには、RailsやRspecに関するlinterが含まれていましたが、数年前に独立し、個別のGemになっています。

今回はRailsアプリを対象としたlintingを行うので、これらも同時にインストールします。
:::

なお、公式ではバージョンロックが推奨されています。
特に理由がなければ、以下のようにバージョンを明示しておきましょう。
```rb
gem 'rubocop', '~> 1.58', require: false
```

記述したら`bundle install`コマンドを実行してインストールしましょう。

### 実行
実行は、ターミナルで以下コマンドを実行するだけです。
```shell
$ rubocop
```

以下のように、何も表示されなければ怒られるところのないコードということです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428968/5ded76fc-efe1-9eed-1901-8f4e99324894.png)

なにか規約に違反している部分があれば、以下のように警告が出ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428968/05015a02-73e2-03c3-19cd-2cdef18f04e0.png)

ここでは、引数名の後のコロンと、渡している変数名の間に空白がありません。

さて、このエラーメッセージの中に `[Correctable]`と記載されていることに気付いたでしょうか。

これが付記されているエラーについては、以下コマンドで自動修正できます。
```sh
rubocop -a
```
もちろん全てがこれで治るわけではありませんが、軽微な修正であればこのコマンドで事足りるので、覚えておくと良いでしょう。

また、行数の後ろに書いてある`C`は違反レベルを示します。
上から深刻度が高く、
```
F : Fatal
W : Warning
E : Error
C : Convention
```
となっています。
修正検討時の参考にしてください。

## RuboCopのカスタマイズ

### コードの特定部分のみrubocopの除外対象とする

コードの可読性などの観点から、どうしても一部分だけRuboCopの規約に反する書き方をしたい場合は、以下で除外対象に設定できます。

```rb
# rubocop:disable Style/StringLiterals. Layout/LineLength

def example

end

def 
...

# rubocop:enable Style/StringLiterals. Layout/LineLength
```

これにより、`disable` 〜 `enable` の区間で `Style/StringLiterals`, `Layout/LineLength`の2つのCop(lintルールのカテゴリのことです)が抑止されます。

全てのCopを無効にしたい場合は `rubocop:disable all`と記載しましょう。

なお、特定のメソッドにだけ適用したい場合は
```rb
def example # rubocop:disable all
  ...
end
```
とだけ書けばOKです。

また、1行だけ適用したい場合は
```rb
example() # rubocop:disable all
```
のように行末にdisableコメントを付けてください。

### 特定のCopのみ実行／規約を変更

PJによっては、特定のlintのみ実行したい場合や、コーディング規約をカスタムしたい場合があるでしょう。

その場合は、`.rubocop.yml`で設定しましょう。

まずは、設定ファイル生成のために以下のコマンドを実行します。
```
$ rubocop --auto-gen-config
```

実行すると、`.rubocop_todo.yml`と`.rubocop.yml`の2つのファイルが生成されます。

### .rubocop_todo.yml

`.rubocop_todo.yml`は、「今違反している部分を一旦除外対象として無視する」ために生成されます。
PJの途中からRuboCopを導入した場合、往々にして大量の怒られが発生します。

これを一旦TODOとして置いておいて、これから各部分だけを対象にするために生成されるものです。

記述は各カテゴリごとになっており、`Exclude`に該当するファイルが記載されています。
```
Style/ClassAndModuleChildren:
  Exclude:
    - 'test/channels/application_cable/connection_test.rb'
    - 'test/test_helper.rb'
```
ここに記載されているファイルを適宜修正し、`.rubocop_todo.yml`から削除していきましょう。


### .rubocop.yml

本題の「規約のカスタマイズ」や「どのCopを実行するか」はここで設定していきます。

設定方法や設定例は別記事にお任せしますが、必要に応じて設定してください。

:::note
**参考記事**

- 設定例
    - [Ruby on Railsプロジェクトのrubocop.ymlテンプレ](https://zenn.dev/minedia/articles/a88546e4efa1f3)
    - [Railsリポジトリの.rubocop.yml](https://github.com/rails/rails/blob/main/.rubocop.yml)
- 設定方法
    - [【Rails】RuboCopの導入 ~ おすすめルールの設定方法](https://blog.to-ko-s.com/rubocop-setting/#i-9)
:::


---

今回は、Rubyの静的コード解析ツール「RuboCop」のRailsにおける使い方をざっと解説しました。

これをGitHub Actionsに組み込むことで、Rspec等とあわせてCI（Continuous Integration）と呼ばれる自動テストの一部とすることができます。
是非お試しください！