Railsプロジェクトのコードをきれいに保つために、Rubocopを使っています。ただし、既存のスタイルそのままではなく、プロジェクトに合わせて各種の変更を行いつつ運用しています。
大原則
まず、運用ルールとして、「Rubocopの評価は行うけど、コミットを止めたりはしない」という方針で運用しています。というのも、(もちろん構文エラーがあるなど、どうしようもないものについては別途考えないといけないかもしれませんが)コミットを止めてしまうぐらいに強制すると、急ぎのときに「なんとかその場しのぎで(ルールだけ通過させる/ルール自体を一時停止させてしまう)」、より対応困難なコードを生む危険性があると考えたからです1。
機能拡張とコード整理を別に行うということで、このようにゆるふわでやっていくのがいいのかなと思っています。
おことわり
説明の都合上、実際の.rubocop.yml
ではひとまとめに書いている部分を分解していることがあります。
初期設定
まずは、.rubocop.yml
でRubyのバージョン設定などをしておきます(バージョンが上がれば、文法やメソッドが変化していきます)。
AllCops:
TargetRubyVersion: 2.2
# Rails用の設定を有効化
Rails:
Enabled: true
とりあえずこれで動くようにはなりますが、とはいえこのままでは過剰にかかる部分も多いので、適宜調整して行かなければいけません。
真っ先に止めるもの
まず、Railsが自動生成する、Bundlerでインストールするなどで、人の手で変更しないことが前提のファイルは、問題点を指摘されても対応不能ですので、チェックの対象から外しておきます。
AllCops:
Exclude:
- bin/*
- db/schema.rb
- vendor/**/*
そして、Gemとしてなど全世界に公開するプロジェクトならともかく、開発もユーザーも日本人、というようなRailsシステムでは、コメントに日本語を書けないのは邪魔にしかならないので、Style/AsciiCommentsも問答無用で停止します。
Style/AsciiComments:
Enabled: false
特殊なファイルには特殊な設定
Railsでは、メインのプログラムを書くapp/
以下以外にもコードを書く場所があります。たとえば、db/migrate/
以下に書くマイグレーションは、10カラムを越えるようなテーブルを作っただけで行数やABCサイズが限界値を超えてしまいます。そして、これを複数のメソッドに分けるというのも非効率なだけで意味がありません。さらに、(内部でデータ移行を行うなど凝ったものを作るのでもない限り)マイグレーションの内容はクラス名や中身で明らかですし、マイグレーションクラスを他のコードに使うこともないので、ドキュメントコメントの必要性も薄くなります。ということで、これだけの設定を止めました。
Documentation:
Exclude:
- 'db/migrate/*'
Metrics/MethodLength:
Exclude:
- 'db/migrate/*'
Metrics/AbcSize:
Exclude:
- 'db/migrate/*'
また、初期データ用にseed-fuを使っていますが、データリストはRubyコードとは言えあくまでデータの管理用なので、ケツカンマを認めてあとから追記をやりやすくしたほうが運用上便利ということで、ここだけ外しています。
Style/TrailingCommaInArguments:
Exclude:
- 'db/fixtures/*'
スタイル設定
分量設定
メソッド長とかABCサイズとかは、デフォルト設定だときついように思うかもしれませんが、慣れれば案外書けるようになります。ただ、80桁制限はきつすぎるので、100桁まで緩和してあります2。
Metrics/LineLength:
Max: 100
コーディングスタイル
字下げはともかく(private
以降を下げるRailsスタイル)、空行は多少入っていてもいなくても読みやすさにそこまで大きく影響するものではないと、全部止めてしまいました。
Style/IndentationConsistency:
EnforcedStyle: rails
Style/EmptyLines:
Enabled: false
Style/EmptyLinesAroundClassBody:
Enabled: false
Style/EmptyLinesAroundModuleBody:
Enabled: false
Style/EmptyLinesAroundMethodBody:
Enabled: false
Style/EmptyLinesAroundBlockBody:
Enabled: false
好みの問題
パーセントリテラルの区切り文字も特定のものを推奨してきますが(Style/PercentLiteralDelimiters)、(正規表現以外では)好みで縦棒区切りを使っています。
Style/PercentLiteralDelimiters:
PreferredDelimiters:
'%i': '||'
'%w': '||'
'%W': '||'
似て非なるもの
モジュールメソッド的なもの
以前記事にまとめましたが、SomeModule.some_method
のように呼べるメソッドは、extend self
とmodule_function
のどちらでも作れます。Rubocopにもどちらかを推奨させるオプション(Style/ModuleFunction)があります…が、この2つは挙動が異なります。意識して使い分けたいので、これは停止させてあります。
Style/ModuleFunction:
Enabled: false
一部箇所だけ止める
Style/NilComparisonは、a == nil
ではなくa.nil?
と書く、という設定です。ふつうは問題ないのですが、ActiveRecordのSQLをDSLで書けるbaby_squeelはcolumn_name == nil
でしか動かないので、それを使う箇所だけ、# rubocop:disable Style/NilComparison
のようなコメントを入れて止めています。
あと、二重否定(!!
)を使わない、というStyle/DoubleNegationもあります。普段の条件判定に使う必要はほぼないのですが、?
で終わるメソッドの返り値としてtrue
かfalse
かだけを渡したい、というシチュエーションでは便利なので、そういう箇所だけ限定的に解除しています。
新しい設定を取り入れる
他のオブジェクトのメソッドを呼ぶ.send
ですが、Ruby 1.9以降はpublic_send
が登場しています。そこで、「パブリックでいいのならpublic_send
」「private
が必要なら__send__
」というように使い分けさせる、というStyle/Sendがあります。この世界観に納得して、そして自動では入らないのであえてonにして運用しています3。
まとめに代えて
Rubyとして処理させる以上、(どれだけ納得できなくても)文法は守らないと動きませんが、そうでないコーディング規約には、設定するだけの理由があります。以前にまとめましたが、このようなルールに対して「盲目的に従う」のも「守らなくても動くからと、ただ無視する」のも、同じように危険な行為だと思います。
前々からそんなことを考えつつRubocopの設定をテーマに書いてみようと思ったところに、JavaScriptでの強烈かつ天下り的なルール設定があったので、自分も記事にした次第です。