このエラーの正体
Assignment Branch Condition size for search is too high. [<10, 21, 5> 23.79/20]
Rails開発中に、RuboCopからこのようなエラーが出た。読んでみると「searchメソッドのABCサイズが大きすぎる」という指摘のようだ。このエラーの意味と、実際に行った対処方法を整理しておく。
RuboCopとは
RuboCopはRuby用の静的コード解析ツールで、コードの長さやインデント、命名規則など、スタイルの乱れを検出して教えてくれる。指摘によっては自動修正してくれるものもあるが、設計に関わる指摘は自分で判断して直す必要がある。RuboCopの指摘がすべて自分のコードに当てはまるとは限らないので、内容を理解した上で対応することが大事。
ABCサイズとは何か
今回の警告は「Metrics/AbcSize」というRuboCopのルールによるもの。ABCサイズとは、1つのメソッド内にある代入(Assignment)、メソッド呼び出しなどの分岐(Branch)、条件分岐(Condition)の数から計算される複雑度の指標。数値が大きいほど、そのメソッドが多くのことをやりすぎている可能性が高い。エラー文の[<10, 21, 5> 23.79/20]は、A=10、B=21、C=5から算出したスコアが23.79で、設定上の上限(Max)20を超えている、という意味になる。
実際に指摘されたコード
指摘を受けたのは以下のsearchメソッド。
def search
if params[:city_id]
pagy, offices = pagy Office.where(city_id: params[:city_id])
pagy_headers_merge(pagy)
elsif params[:keyword]
keywords = params[:keyword].split(/[[:blank:]]+/).select(&:present?)
pagy, offices = pagy_array([])
keywords.each do |keyword|
offices += Office.where('name LIKE (?) OR
address LIKE (?) OR
near_station LIKE (?) OR
introduction LIKE (?) OR
company LIKE (?)',
"%#{keyword}%",
"%#{keyword}%",
"%#{keyword}%",
"%#{keyword}%",
"%#{keyword}%")
end
else
pagy, offices = pagy(Office.all)
pagy_headers_merge(pagy)
end
render json: offices, each_serializer: OfficeIndexSerializer, include: '**'
end
if / elsif / else の分岐が多く、さらにelsifの中にeachやOffice.whereなど複数の処理が詰め込まれている。縦に長いコードで、ABCサイズが上がりやすい典型的な形になっていた。
対処方法
今回はひとまず、.rubocop.ymlの設定を変更して警告を回避する方法を選んだ。
Metrics/AbcSize:
# The ABC size is a calculated magnitude, so this number can be a Fixnum or
# a Float.
Max: 15
デフォルトのMaxは15になっていたので、これを25に変更してコミット。これでRuboCopからの指摘は出なくなった。
ただ、これはあくまで警告を黙らせるための対処であって、コードの複雑さそのものは解決していない。本来であれば、if / elsifの分岐ごとに別メソッドへ切り出したり、検索条件の組み立てをオブジェクトやスコープに委譲したりして、searchメソッド自体をシンプルにするのが望ましい対応だと思う。Maxの数値変更はあくまで最終手段として、まずはリファクタリングを検討したい。
まとめ
RuboCopの「Assignment Branch Condition size for search is too high」は、メソッドの代入・分岐・条件の数から算出されるABCサイズが上限を超えたときに出る警告。.rubocop.ymlのMetrics/AbcSizeのMax値を変更すれば一時的に回避できるが、根本的にはメソッドを分割してシンプルにすることが望ましい。設定でも変更できるということを知っておくと、対応の選択肢が広がるはず。
Qiita
Qiitaでも記事を書いています。

