pockestrap

Programmer's memo

正規表現でかんたんにCopを書けるRuboCop拡張 rubocop-grep をリリースしました

こんにちは。正規表現でかんたんにCopを書けるRuboCop拡張を作ってみたので紹介します。

github.com

使い方

まず、ほかのRuboCop拡張と同様にgemをインストールし、.rubocop.ymlrequireにこのgemを指定してください。

この拡張は設定をしないと何も動きません。次のように.rubocop.ymlに設定をします。

Grep/Grep:
  Rules:
    # シンプルな例。正規表現にマッチした場合に、このCopが警告をします。
    - Pattern: '\bENV\b'
      Message: Do not refer ENV directly.

    # パターンはArrayにして複数の正規表現を含めることができます。
    - Pattern:
        - 'binding\.irb'
        - 'binding\.pry'
      Message: 'Debug code remains'

    # MatchInComment オプションを指定すると、正規表現がコメントの中でもマッチするようになります。(デフォルトではマッチしない)
    - Pattern: 'Rspec'
      Message: 'Rspec is a typo of RSpec'
      MatchInComment: true

このようにPattern正規表現を書くことで、それにマッチした場合に警告を出すCopをかんたんに作ることができます。

この他にも、それぞれ正規表現のオプションに対応するMultiline, Ignorecase, Extendedオプションも存在します。

モチベーション

このRuboCop拡張を作ろうと思ったのは、主に2つの方向のモチベーションがあります。1つはRuboCopというインターフェイス、もう1つはカスタムCopを作ることへの敷居です。

RuboCopというインターフェイス

RuboCopというインターフェイスには価値があります。

rubocop-grepと近いことはほかのツールでもできます。たとえばgrepgit grepを素朴に使っても実現できるでしょう。またGoodcheckQuerlyなどのより高レベルなツールも存在します。これらのツールでもrubocop-grepと同等かそれ以上の問題を検出できます。

ですが、これらはRuboCopとは全く別のソフトウェアです。そのためこれらのツールを使うには、それぞれ別の設定が必要となります。grep (1)を使って問題を検出するならそれ用のスクリプトを書くことになるでしょうし、GoodcheckやQuerlyの場合にはそれぞれ別の設定ファイルが必要です。そして、それらをエディタやCIで有効にするには更に別の設定が必要となります。

一方でrubocop-grepはこの問題を解決しています。このgemはRuboCop拡張なので、どのような問題を検出したいかを.rubocop.ymlに書いてしまえばそれで完了です。 CIやエディタのために別途の設定は必要ありません。通常通りRuboCopを実行すれば、rubocop-grepによる問題の検出も行われます。.rubocop_todo.ymlなどの機能も使うことができます。

このように、RuboCopの拡張として実装することで多くの恩恵を得ることができます。これはただのgrepにはない価値と言えるでしょう。

これは以前私が作成したrubocop-rubycwと同様の思想です。

pocke.hatenablog.com

カスタムCopを作ることへの敷居

カスタムCopを作るには、いくつかのことを知っている必要があります。ASTを扱うプログラミング、解析したいRubyコードの構文木がどんな形になるのか、RuboCopが提供するAPIなどです。これらは通常のアプリケーションの開発ではあまり意識がされない情報で、これらを扱うのに慣れていない人からすると敷居が高く感じてしまうかもしれません。

これらの知識が必要なくかんたんにCopを作れたら便利なのではないか、と思っています。

この拡張を使うべきケースと、そうでないケース

上記モチベーションで取り上げたようなRuboCopというインターフェイスが重要になるケースでは、この拡張を使うメリットが大きくなります。

一方で正規表現には限界があることも認識する必要があります。検出したい問題によっては正規表現が難しくなりすぎてしまったり、そもそも正規表現では解けない問題だったりすることがあります。そのようなときにはQuerlyを使ったり、RuboCopのカスタムCopを自分で作ったりする必要があるでしょう。

このあたりの話はリポジトリのREADMEにも詳しく書いたので、そちらも参照してみてください。 https://github.com/pocke/rubocop-grep#similar-projects

現状の課題と今後

rubocop-grep gemにはまだ課題があると思っています。ここでは現時点でわかっている課題と、今後について簡単に説明します。

運用

1つ目は運用です。rubocop-grep gemはどちらかというと思いつきでパッと作ったgemなので、まだ全く実運用には乗っていません。運用するうちに見えていなかった課題が見つかり、うまく運用できないことがあるかもしれません。

「こういうツール作れそう!」で作ったため、実際にどのようなルールを便利に作れるのかもまだあまり考えていません。READMEの例が適当だったり、サンプルの設定ファイルがなかったりするのは少し残念だなと思っているので、このあたりにフィードバックをいただけるととても嬉しいです。

速度

運用とも絡んでくるのですが、現時点の実装はパフォーマンスのことを何も考えていません。「ここは遅くなりそうだな…」と思いつつも素朴な実装のままにした箇所がいくつかあります。もしパフォーマンス上大きな問題がありそうであれば、フィードバックをいただけると助かります。

auto-correction

現状ではrubocop-grep gemは問題を検出するだけで、autocorrectは行いません。単に置換するだけであればそう難しくないはずなので、将来的に実装してみようかと思っています。


かんたんにCopを書けるRuboCop拡張の、rubocop-grepを紹介しました。役立てていただけるととても嬉しいです。

最後に私の好きな言葉を引用してこの記事を締めくくります。

Some people, when confronted with a problem, think “I know, I'll use regular expressions.” Now they have two problems.

http://regex.info/blog/2006-09-15/247