ESM アジャイル事業部 開発者ブログ

永和システムマネジメント アジャイル事業部の開発者ブログです。

RuboCop の設定ファイルで、無効な cop を明示する書き方から有効な cop を明示する書き方に直してみた

はじめに

こんにちは、@wai-doi です。

本記事では RuboCop の設定ファイルの .rubocop.ymlで、特定の cop の無効化を明示している書き方から、逆に、有効になっている cop の有効化を明示する書き方に直す方法を考えました。

やりたかったこと

まず以下のような .rubocop.yml がありました。

AllCops:
  DisabledByDefault: true

Layout:
  Enabled: true

Layout/ArgumentAlignment:
  Enabled: false

Layout/ArrayAlignment:
  Enabled: false

# 省略

Layout/SpaceInsideHashLiteralBraces:
  Enabled: false

上から順に見ていくと、まず AllCops: DisabledByDefault: true で全ての cop を無効化しています。そして、Layout: Enabled: true で Layout の cop を有効化にしています。最後に Layout の中で特定の cop を Layout/ArgumentAlignment: Enabled: false のように無効化にしています。

この状態から、逆に、どの cop が有効になっているかがわかりやすいよう、有効化している cop だけを明示した書き方に直したいということがありました。

期待する書き方は以下です。

AllCops:
  DisabledByDefault: true

Layout/AccessModifierIndentation:
  Enabled: true

# Layout/ArgumentAlignment Layout/ArrayAlignment は書かれていない

Layout/BeginEndAlignment:
  Enabled: true

# 省略

Layout/TrailingWhitespace:
  Enabled: true

つまり以下のように書き換えたかったです。

  • Layout: Enabled: true を削除する
  • Layout cops でデフォルトに有効になっている cop を明示的に Enabled: true で書く
  • ただし今 Enabled: false を書いている cop については何も書かない

解決したスクリプト

明示的に Enabled: true を書くために、RuboCop のドキュメントにあるたくさん Layout cops を書き写すのは面倒なので、以下の Ruby スクリプトを書いてみました。

rubocop v1.60.2 を用いました。

require 'rubocop'

# Layout cops を取得します
layout_cops = RuboCop::Cop::Registry.new(RuboCop::Cop::Registry.all).with_department(:Layout)

# デフォルトで有効になる Layout cops だけを取得する
default_enabled_layout_cops = layout_cops.enabled(RuboCop::ConfigLoader.default_configuration)

# .rubocop.yml から Enabled: false を書いている cop名 を取り出す
disabled_layout_cop_names = RuboCop::ConfigLoader
  .load_file('.rubocop.yml')
  .to_h
  .select {|cop_name, cop_config| cop_name.start_with?('Layout/') && cop_config['Enabled'] == false }
  .keys

# デフォルトで有効になる cop から Enabled: false を書いている cop を除く
enabled_layout_cop_names = default_enabled_layout_cops.map(&:cop_name) - disabled_layout_cop_names

# .rubocop.yml に Enabled: true をコピペできる形式にして出力する
puts enabled_layout_cop_names.map {|cop_name|
  <<~YAML
    #{cop_name}:
      Enabled: true
  YAML
}.join("\n")

1行目と2行目の RuboCop::Cop::RegistryRuboCop::ConfigLoader の使ったコードは @koic さんに教えていただきました。 RuboCop::Cop::Registry は RubCop が内部で持っている cop の一覧情報が取得できるクラスで、RuboCop::ConfigLoader は RuboCop の設定ファイルを処理するクラスです。

工夫した点

工夫したところは3行目の disabled_layout_cop_names を取得するところです。 以下のように RuboCop::Cop::Registry#disabled を使って .rubocop.yml で無効になっている cop を取得しようとしましたが、これではうまくできませんでした。

disabled_layout_cop_names = layout_cops.disabled(RuboCop::ConfigLoader.load_file('.rubocop.yml'))

#disabled実装を見ると、.rubocop.yml に Enabled:true と書かれている cop を除くという処理であることがわかりました。.rubocop.ymlには Enabled: true は書いていないためなにも除かれなかったため、これはうまくできませんでした。

別の方法で取れないかいろいろ探すと、RuboCop::ConfigLoader.load_file('.rubocop.yml') の結果を to_h でハッシュに変換できるようだったため、正規表現などを使って Enabled: false の cop を取得する方法で実現できました。

後からわかったことですが、ここで .rubocop.yml をハッシュへの変換しているのは単純にYAMLをパースしているだけなので、 YAML.load_file('.rubocop.yml') でも同じでした。

RuboCop にあるメソッドで Enabled: false と書いている cop をもっとスマートに取得できる方法もあるかもしれないですが、わかりませんでした。

おわりに

今回やってみて、RuboCop::Cop::RegistryRuboCop::ConfigLoader など RuboCop を普段使うだけでは知らなかったクラスなどを知ることができてよかったです。


永和システムマネジメントでは、Ruby とアジャイルソフトウェア開発を通じてコミュニティと成長したいエンジニアを絶賛募集しています。

agile.esm.co.jp