あなたの知らないRuboCopの設定

@vividmuimui
2019/04/03

社内LT

あなたの知らないRuboCopの設定

僕の知らなかったRuboCopの設定

話すこと

便利copとかの話ではないです。
継承(inherit)に関する3点の話です。

  • 初級: URLでinherit
  • 中級: inherit_mode
  • 上級: 相対パスはどこから?

初級: URLでinherit

inherit(継承)

RuboCop では、共通設定ファイルを利用する方法がいくつかある。

  • inhert_from
    • ローカルファイル
    • .rubocop_todo.ymlが代表的
  • inherit_gem
    • Gem
    • onkcop, meowcopが代表的
inherit_from: .rubocop_todo.yml

inherit_gem:
  onkcop:
    - "config/rubocop.yml"
    - "config/rails.yml"

inherit_from にはURLもかける!

inherit_from:
  - http://www.example.com/rubocop.yml

会社共通の設定とかはGitHubにあげて
https://raw.githubusercontent.com/xxxを参照するのが手っ取り早そう?

Rubyのバージョンが最新に追従できてない、とかもあると思うので、現実的にはGemで提供するのが正しそう。

中級: inherit_mode

前提1

子ファイル(例: .rubocop.yml)で親ファイル(例: .rubocop_todo.yml)の設定を継承するときに、
子ファイルで親ファイルの設定を上書きできる

# .rubocop_todo.yml
Style/CollectionMethods:
  PreferredMethods:
    collect: map

# .rubocop.yml
inherit_from: '.rubocop_todo.yml'

Style/CollectionMethods:
  PreferredMethods:
    collect: collect # こっちの設定が有効

前提2

RuboCopの設定は、HashArrayで設定される。

Style/CollectionMethods:
  # hashの例
  PreferredMethods:
    collect: map
  # arrayの例
  Exclude:
    - 'foo/bar.rb'

継承したときに、

  • Hashは設定がMergeされる
  • Arrayは設定がOverrideされる

:memo: ArrayがOverrideなのは、そうじゃないと親の設定を子で無効化できないから。MergeだとYamlで空Arrayを書いても、親の設定が残ってしまう。
Hashの場合は~(Yamlのnull)で、親の設定をキャンセルできる。

inherit_mode

inherit_modeでArrayの継承時の挙動を変更できる。
OverrideからMergeに変更できる

inherit_from:
  - 'shared.yml'

inherit_mode:
  merge:
    - Exclude

Style/For:
  inherit_mode:
    override:
      - Exclude

上級: 相対パスはどこから?

パスを書ける部分

  • inherit_from
    • 継承するファイル
  • Include/Exclude
    • Copの対象とするファイル
inherit_from: .rubocop_todo.yml

Style/CollectionMethods:
  Exclude:
    - 'foo/bar.rb'

どちらも、パスは絶対パス・相対パスどちらも書ける。
相対パスはどこからの相対なのか?

inherit_fromの相対パス

inherit_fromに書く相対パスは、inherit_fromが書いてあるファイルからの相対パス

# .rubocop.yml
inherit_from: '../.rubocop_todo.yml'

こう書いた場合、
.rubocop.ymlからみて../にある.rubocop_todo.ymlを読み込む。

Include/Excludeの相対パス

Style/CollectionMethods:
  Exclude:
    - 'foo/bar.rb'

これが難しい

Include/Excludeの設定が書かれているファイル名によって変わる!

Include/Excludeの相対パス

  • .rubocopで始まる設定ファイルの場合
    • その設定ファイルからの相対パス
    • .rubocop.yml.rubocop_todo.ymlなど
  • それ以外の設定ファイルの場合
    • rubocopコマンドを実行したディレクトリからの相対パス
    • .my_rubocop_config.ymlrubocop.yml(ドットなし)など
 .
├── config
│   ├── .my_rubocop_config.yml
│   ├── .rubocop_todo.yml
│   └── .rubocop.yml
└── src
     └── sample.rb
# .rubocop.yml
inherit_from:
    - .my_rubocop_config.yml
    - .rubocop_todo.yml

このような状況とします。
rubocopコマンドは、ルートディレクトリで以下のように実行するとします

$ rubocop -c config/.rubocop.yml
 .
├── config
│   ├── .my_rubocop_config.yml
│   ├── .rubocop_todo.yml
│   └── .rubocop.yml
└── src
     └── sample.rb
# config/.rubocop.yml
# config/.rubocop_todo.yml
Style/CollectionMethods:
  Exclude:
    # .rubocopで始まるので、設定ファイルからの相対
    - '../src/sample.rb'

# config/.my_rubocop_config.yml
Style/CollectionMethods:
  Exclude:
    # .rubocopではないので、実行ディレクトリからの相対
    - 'src/sample.rb'

.rubocop.ymlがリポジトリルートに置いてあるケースでは、ほぼ問題にならない。

ただ、tools/rubocop/などに設定ファイル(.rubocop.yml など)をおいてるときには注意しなければいけない。

例えば、以下のようなケースの場合、.rubocopで始めないファイル名のほうが楽。

.
├── src
│   └── server
│       └── test
│           └── foo.rb
└── tool
    └── rubocop
        └── config
            └── rubocopの設定ファイル

rubocopの設定ファイルがルートになく、
srcディレクトリでrubocopを動かすようなケース

実行コマンドが次のようになるケース

$ rubocop -c ../tool/rubocop/config/{rubocopの設定ファイル}

コマンドの実行ディレクトリが設定ファイルと同じ場所の場合は話が別です。
(tools/rubocop/configでrubocopコマンドを動かすようなケース。そういうケースはあまりなさそうだが。。)

.rubocopで始めないファイル名のほうが楽な理由

例えば、testディレクトリはまるっと無視したいCopがあったとき、

# rubocopの設定ファイル
Foo/BarCop:
    Exclude:
        - '**/test/**/*.rb'

というように書きたくなるが、

  • .rubocopで始まるファイルの場合
    • その設定ファイルからの相対パスなので、**/が効かない。
  • .rubocopで始まらないファイルの場合
    • rubocopコマンドの実行ディレクトリからの相対パスなので、上記の設定で問題ない。
    • 実行ディレクトリが、プロジェクトルートでも、srcでも、src/serverでも問題ない。

まとめ

まとめ

  • 初級: URLでinherit
    • inherit_fromにはURLが書ける
  • 中級: inherit_mode
    • 子ファイルで設定を上書きするとき、HashはMerge, ArrayはOverride
    • inherit_modeでArrayの上書き設定をMergeに変更できる
  • 上級: 相対パスはどこから?
    • inherit_fromに書くパスは、inherit_fromが書かれた設定ファイルからの相対
    • Include/Excludeに書くパスは
      • .rubocopで始まる場合は、設定ファイルからの相対
      • それ以外は、実行コマンドからの相対
      • 設定ファイルをtools/などに置くときは、.rubocopで始まらないファイル名のほうが楽