Chapter 05

2-2. Rubocop

Masuyama
Masuyama
2022.10.15に更新

Rubocop

今回は Rubocopという gem を利用し、ソースコードの改善ポイントを検査するための設定を行います。

Rubocop とは?

Rubocop とは、ソースコード中の無駄なインデントの有無や、Ruby で推奨されない書き方をしていた場合に指摘してくれる生活指導のようなツールです。
複数人で開発を行っていても書き方を統一するというメリットがあるため、チーム開発を行う多くの現場で導入されています。

また、簡単な修正で済むような指摘であれば半自動的に修正できることも Rubocop の特徴です。

導入

Rubocop は gem であるため、Gemfile に書き込んで bundle installを実行するとインストールできます。

techlog-app 内の Gemfile を開き、group :developmentという欄に以下のように追記してください。
本カリキュラムでは、本体である rubocopの他に Rails 用、パフォーマンスチェック用、RSpec(テスト)用の rubocop も使用します。

group :development do
  ...
  gem 'rubocop', require: false # 追加
  gem 'rubocop-performance', require: false # 追加
  gem 'rubocop-rails', require: false # 追加
  gem 'rubocop-rspec' # 追加
end

Gemfile に追記したら bundle install を実行します。

$ bundle install
...
Fetching rubocop 1.30.1
Installing rubocop 1.30.1
Bundle complete! xx Gemfile dependencies, xx gems now installed.

これで Rubocop が使えるようになりました。

次に、Rubocop の設定ファイルを作成します。

設定ファイルの準備

Rubocop では .rubocop.yml という名前のファイルに書かれた設定に従い、ソースコードの検査を行います。
例えば「このファイルは検査対象外としたい」「デフォルトの検査は厳しすぎる」といった時に、この設定ファイルにより適宜調整を行うことが可能です。

また、チーム開発ではこの設定ファイルを共有しておくことで、チーム内でソースコードの品質を一律に保つことができます。

それでは設定ファイルを準備しましょう。
まずは新規作成するため、Rails プロジェクトのルートディレクトリ (techlop-app ディレクトリの直下) で以下のコマンドを実行します。

$ touch .rubocop.yml

作成できたら、VSCode などのエディタで編集して中身を以下のように設定します。

.rubocop.yml

require: rubocop-rails

AllCops:
  # 除外するディレクトリ(自動生成されるファイル)
  Exclude:
    - "vendor/**/*"
    - "db/**/*"
    - "config/**/*"
    - "bin/*"
    - "node_modules/**/*"

  # 新ルールを有効化
  NewCops: enable

# 1行あたりの文字数をチェックする
Layout/LineLength:
  Max: 130
  # 下記ファイルはチェックの対象から外す
  Exclude:
    - "Rakefile"
    - "spec/rails_helper.rb"
    - "spec/spec_helper.rb"

# RSpecは1つのブロックあたりの行数が多くなるため、チェックの除外から外す
# ブロック内の行数をチェックする
Metrics/BlockLength:
  Exclude:
    - "spec/**/*"

# Assignment: 変数への代入
# Branch: メソッド呼び出し
# Condition: 条件文
# 上記項目をRubocopが計算して基準値を超えると警告を出す(上記頭文字をとって'Abc')
Metrics/AbcSize:
  Max: 50

# メソッドの中身が複雑になっていないか、Rubocopが計算して基準値を超えると警告を出す
Metrics/PerceivedComplexity:
  Max: 8

# 循環的複雑度が高すぎないかをチェック(ifやforなどを1メソッド内で使いすぎている)
Metrics/CyclomaticComplexity:
  Max: 10

# メソッドの行数が多すぎないかをチェック
Metrics/MethodLength:
  Max: 30

# ネストが深すぎないかをチェック(if文のネストもチェック)
Metrics/BlockNesting:
  Max: 5

# クラスの行数をチェック(無効)
Metrics/ClassLength:
  Enabled: false

# 空メソッドの場合に、1行のスタイルにしない NG例:def style1; end
Style/EmptyMethod:
  EnforcedStyle: expanded

# クラス内にクラスが定義されていないかチェック(無効)
Style/ClassAndModuleChildren:
  Enabled: false

# 日本語でのコメントを許可
Style/AsciiComments:
  Enabled: false

# クラスやモジュール定義前に、それらの説明書きがあるかをチェック(無効)
Style/Documentation:
  Enabled: false

# %i()構文を使用していないシンボルで構成される配列リテラルをチェック(無効)
Style/SymbolArray:
  Enabled: false

# 文字列に値が代入されて変わっていないかチェック(無効)
Style/FrozenStringLiteralComment:
  Enabled: false

# メソッドパラメータ名の最小文字数を設定
Naming/MethodParameterName:
  MinNameLength: 1

今回の .rubocop.yml は一例です。
チームや開発方針によってルールが変わってくるため、入った現場の .rubocop.yml を使うようにしましょう。

では、ここまでで一旦コミットしておきます。

$ git add .
$ git commit -m "Rubocopの初期設定を完了"

Rubocop を実行

それでは、これで Rubocop によるソースコードの静的解析(検査)を行えるようになったので実行してみましょう。
実行するには、以下のコマンドを使います。

$ bundle exec rubocop

実行すると、以下のような内容がターミナルに出力されたはずです。

$ bundle exec rubocop
Inspecting 13 files
CC.....C.CCCC

Offenses:
...
test/test_helper.rb:3:9: C: [Correctable] Style/StringLiterals: Prefer single-quoted strings when you don't need string interpolation or special symbols.
require "rails/test_help"
        ^^^^^^^^^^^^^^^^^

13 files inspected, 35 offenses detected, 35 offenses autocorrectable

上記の出力内容をもとに、簡単に解説します。

test/test_helper.rb:3:9: C: [Correctable] Style/StringLiterals: Prefer single-quoted strings when you don't need string interpolation or special symbols.
require "rails/test_help"
        ^^^^^^^^^^^^^^^^^
13 files inspected, 35 offenses detected, 35 offenses autocorrectable

解説:

  • test/test_helper.rb:3
    • 修正が望ましいファイルとその行
  • [Correctable]
    • 自動で修正可能かどうか(後述)
  • Style/StringLiterals
    • 指摘の種類:ここでは、コードスタイルに関する指摘
  • Prefer single-quoted strings when you don't need string interpolation or special symbols.
    • 指摘対照となった理由と、改善の方針:ここでは、文字列を囲うには " よりも ' を使うべきという指摘
  • 35 offenses detected
    • 指摘の数:ここでは 35 ヶ所の指摘
  • 35 offenses autocorrectable
    • 指摘のうち自動修正が可能な数:ここでは 35 ヶ所が自動修正可能

それでは、次はこれらの指摘事項に従って修正をしましょう。

-a オプションで自動修正

今回の指摘事項を確認してみると、ほとんどがダブルクォーテーション " を使っていることに関する指摘です。
それ以外は Do not use spaces inside percent literal delimiters.という、空白の使い方に関する指摘でした。

さて、このようにコードスタイルに関する指摘というのは何も考えずに修正できますが、ファイル数も多く一つひとつ書き換えていくのは大変ですよね。

このように修正後の形が明確であり、数が多い場合には Rubocop の自動修正機能が便利です。

先ほどみた通り、今回は 35 ヶ所の指摘のうち 35 ヶ所、つまりすべてが自動修正可能でしたので Rubocop の自動修正機能を使ってまとめて修正します。

なお、この時に意図しない変更を防ぐためにも事前にコミットしておくことをおすすめします。
.rubocop.yml を作成した時点でコミットしていたのはこのためです。

ただし、よく分からないまま自動修正を適当に使ってしまうと、予期せぬバグを生むことに繋がります。
Rubocop での指摘は数が多い&英語で読むのは大変ですが、必ず全ての指摘事項は一つ一つ確認するようにしましょう!

今回は自動修正で問題ない範囲ですので、自動修正していきます。

自動修正機能を使うには -a オプションをつければ OK です。
オプション付き Rubocop を実行します。

$ bundle exec rubocop -a
...
13 files inspected, 35 offenses detected, 35 offenses corrected

35 offenses correctedと表示されており、すべての指摘が自動で修正されたことが分かります。

もう一度 Rubocop で検査(自動修正なし)を実行すると、指摘事項がなくなっているはずです。

$ bundle exec rubocop
Inspecting 13 files
.............

13 files inspected, no offenses detected

これでコードの品質を担保することができました。

さて、ここまでの差分が Rubocop による指摘に対する修正だとわかるようにコミットしつつ、リモートリポジトリと同期しておきます。

$ git add .
$ git commit -m "Rubocop による指摘事項修正(Lint)"
$ git push

これで Rubocop の導入は完了です。

コミットの前に必ず Rubocop での検査を行い、コード品質を最善に保つように心がけましょう。

宿題

Rubocop の概要

今回は Rubocop の導入に重きを置いていたのですが、Rubocop そのものの概要については別途、落ち着いて理解を進めておきましょう。

Rubocop の指摘(違反)について

今回は同じ違反レベルの指摘ばかりでしたが、Rubocop では指摘内容に応じて違反レベルに段階がつけられています。
特にセキュリティに関する違反であればすぐに修正すべき、といった指摘になります。

他にも指摘内容の読み方・調べ方についても把握しておくと違反時の対応に困らなくて済むので、以下の参考サイトを一度読んでおきましょう。