QuerlyでRubyをgrepする

# 何がうれしいの？

通常の [`grep`](https://linuxjm.osdn.jp/html/GNU_grep/man1/grep.1.html) コマンドよりも、Rubyコードに適したきめ細かい検索ができます。

# どうやって？

まず、インストールします。

```shell-session
$ gem install querly
```

次に `querly find` コマンドを実行します。

`app/` ディレクトリ内のRubyファイルに対して、任意の引数を与えた `save!` メソッド呼び出しを検索します。

```shell-session
$ querly find 'save!(...)' app/
  app/models/tweet.rb:100:6           tweet.save!
  app/models/notification.rb:36:8               notification.save!
  app/models/notification.rb:173:8              notification.save!
  app/models/html_variant.rb:16:4           save!
  app/models/github_issue.rb:32:4           issue.save!
  app/controllers/comments_controller.rb:134:6        @comment.save!
  app/controllers/internal/users_controller.rb:127:6          MentorRelationship.new(mentee_id: mentee.id, mentor_id: @user.id).save!
  app/controllers/internal/users_controller.rb:131:6          MentorRelationship.new(mentee_id: @user.id, mentor_id: mentor.id).save!
  app/controllers/users_controller.rb:82:7          if current_user.save!
  app/controllers/giveaways_controller.rb:50:9        if @user.save!
  app/services/authorization_service.rb:68:6          user.save!
  app/services/authorization_service.rb:100:6         identity.save!
  app/labor/podcast_feed.rb:42:4            ep.save!
13 results
```

`app/` ディレクトリ内のRubyファイルに対して、 `self` レシーバからの任意の引数を与えた `save!` メソッド呼び出しを検索します。

```shell-session
$ querly find 'self.save!(...)' app/
  app/models/html_variant.rb:16:4           save!
1 results
```

`name=` メソッド呼び出し（代入）を検索します。

```shell-session
$ querly find 'name=(...)' app/
  app/controllers/html_variants_controller.rb:20:6            @html_variant.name = @fork.name + " FORK-#{rand(10000)}"
  app/services/authorization_service.rb:61:8            user.name = auth.info.nickname
  app/services/rss_reader/assembler.rb:72:10              iframe.name = "p"
  app/services/rss_reader/assembler.rb:89:10              bq.name = "p"
  app/services/rss_reader/assembler.rb:99:10              iframe.name = "p"
  app/labor/html_cleaner.rb:18:6              el.name = "p"
6 results
```

引数に `String` を指定した `name=` メソッド呼び出しを検索します。

```shell-session
$ querly find 'name=(:string:)' app/
  app/services/rss_reader/assembler.rb:72:10              iframe.name = "p"
  app/services/rss_reader/assembler.rb:89:10              bq.name = "p"
  app/services/rss_reader/assembler.rb:99:10              iframe.name = "p"
  app/labor/html_cleaner.rb:18:6              el.name = "p"
4 results
```

`Article.where` メソッド呼び出しで…、引数のパターンは実際の例をご覧ください。 :point_down: 
（コードを見た方が早いかと :sweat_smile: ）

```shell-session
$ querly find 'Article.where(published: :bool:, user_id: _)' app/
  app/controllers/pages_controller.rb:78:4          Article.where(user_id: ApplicationConfig["DEVTO_USER_ID"], published: true).
  app/labor/article_analytics_fetcher.rb:7:24       articles_to_check = Article.where(user_id: user_id, published: true)
2 results
```

＊上記は [thepracticaldev/dev.to](https://github.com/thepracticaldev/dev.to) リポジトリに対して実行した結果です。

# そもそもQuerlyとは

[特有のパターン言語](https://github.com/soutaro/querly/blob/master/manual/patterns.md)によって、Rubyのメソッド呼び出しパターンを検出するためのツールです。

[soutaro/querly: Query Method Calls from Ruby Programs](https://github.com/soutaro/querly)

普通は、 `querly.yml` というYAMLファイルにプロジェクト固有のパターンをルール化して、自動でチェックする、といった使い方をします。

```yaml
rules:
  - id: sample.debug_print
    pattern:
      - self.p
      - self.pp
    message: Delete debug print
```

詳しくは、以下の記事が参考になります。

- [プロジェクト固有のルールを指定できるLinterであるところのQuerlyがめちゃ便利 - Islands in the byte stream](https://gfx.hatenablog.com/entry/2018/08/10/143417)
- [自分の足を撃たない技術 - Qiita](https://qiita.com/pocke/items/71dc9c6a9ea33dd95aac)

# まとめ

`grep` コマンドよりは遅いですが、きめ細かい検索ができるのでめちゃくちゃ便利です。

`querly find` とタイプするのが面倒なので、私は普段 `ruby-grep` というエイリアスを使っています。

```bash:.bashrc
alias ruby-grep='querly find'
```

Rubyistの皆さん、ぜひ試してみてください！