こんにちは、夜な夜なフォーチュンタワーに登っている nsgc です。
複数人でプロダクトやサービスを作る際に可読性や保守性を向上させるため、 コーディング規約を用意してフォーマットを統一したり、あきらかに不要な記述を静的解析ツールでチェックすることはよくあるかと思います。
Ruby でプログラミングしている場合、そういった Formatter/Linter としては RuboCop が有名ですが、 RuboCop 標準で用意されているルールだけではもの足りず、プロジェクトのコンテキストに特化した独自ルールが欲しい時がありませんか?
そんな時には、オリジナルの Cop の作成をオススメしたい!
tapp
をチェックする Cop を作ってみよう
突然ですが tapp というツールをご存知でしょうか? tapp はメソッドチェーンの途中でもオブジェクトを表示できる優れものなのですが、プロダクトコードには入れたくありません。
コードレビューの時に「デバッグ用途の tapp が残ってますよ」 という指摘をしますが、本来はその前に CI で検知してほしいものです。そこで、今回 "tapp が呼ばれているか検知する" 独自 Cop を用意してみましょう。
まずは、Cop クラスの作成です。
RuboCop::Cop::Base
を継承したクラスにコールバックを定義し、その中で問題になる条件がないかチェックし、最終的には警告メッセージを出すためのメソッドを呼びだします。
下記例では、メソッド呼び出し時に呼ばれる on_send
コールバックを定義し、警告表示のための add_offense
を呼んでいます。
RESTRICT_ON_SEND
定数に指定することで on_send
で呼ばれる対象を tapp
のみに限定し、add_offense
で表示するメッセージを MSG
定数で設定しています。
# frozen_string_literal: true module RuboCop module Cop module InternalAffairs # # # bad (ok during development) # # using tapp # def some_method # do_something.tapp # end # # # good # def some_method # do_something # end # class Tapp < Base RESTRICT_ON_SEND = %i(tapp).freeze MSG = 'Remove debugger'.freeze def on_send(node) add_offense(node) end end end end end
Cop クラスが用意できたら、次にそのクラスを .rubocop.yml で読み込み、他の Cop 同様に有効にすると使えます。
require: - ./lib/rubocop/cop/internal_affairs/tapp InternalAffairs/Tapp: Enabled: true
ここまで読んでどうでしょう?単純なメソッドコールの有り無し位ならとても簡単に作れそうですね。
もう少しリッチな構文チェックをしたい場合やオートコレクトを用意したい場合は公式で用意されている記事 が丁寧に書かれていますし、既存の Cop と近い Cop を作りたい場合は既にある実装 がリファレンスとして参考になります。
さいごに
私が参加しているプロジェクトでは、 特定のロールの利用者が操作時の証跡をデータベース上に残す必要があり、 controller でその証跡レコーディング用のメソッドが呼ばれているかを Cop でチェックしています。
また、Time::DATE_FORMATS
で独自のフォーマットを用意しているのですが、それを用いないで書かれた場合にアラートをあげる Cop もいます。
チームのコードレビューで機械的に検出できるものを何度も指摘をしているなら、適応する Cop がないか探してみたり、なければ、自分たちで Cop を用意してチェックを任せ、人間にしかできないレビューに注力していきましょう!