rubocop custom copの作成

## 背景
`Time.zone.now`を`Time.current`に変更したため、ついにルールとしてRuboCopに追加しました。

## 環境
```
❯ ./bin/rails --version
Rails 7.1.1

❯ bundle exec rubocop --version
1.57.2
```

## Custom Copを追加
- lib/custom_cops/ban_time_zone_now.rb

### install parser ([AST用](https://docs.rubocop.org/rubocop/development.html#basics))
```
❯ gem install parser
Fetching ast-2.4.2.gem
Fetching parser-3.2.2.4.gem
Successfully installed ast-2.4.2
Successfully installed parser-3.2.2.4
Parsing documentation for ast-2.4.2
Installing ri documentation for ast-2.4.2
Parsing documentation for parser-3.2.2.4
Installing ri documentation for parser-3.2.2.4
Done installing documentation for ast, parser after 26 seconds
2 gems installed
```

### patternを定義する（Time.zone.now）
```rb
❯ ruby-parse -e 'Time.zone.now'
(send
  (send
    (const nil :Time) :zone) :now)
```

[`nil`を`nil?`にするべき](https://stackoverflow.com/questions/59396748/why-doent-this-send-pattern-match)なので、patternは下のようになります。

```
(send (send (const nil? :Time) :zone) :now)
```

### RESTRICT_ON_SEND
これは特に定義しなくても動作しますが、RuboCopをより効率的に動かすために、定義しておくべきです。

> 定数 RESTRICT_ON_SEND は最適化のための特別な配列です。この中に含まれるメソッドが呼び出されたときだけ on_send を実行するように制限します。この制限がない場合、すべてのメソッドに対して on_send を呼び出し、パターンマッチの計算を行うために実行時間が増えてしまいます。

**注意点**

上記を見ると、`zone`か`now`のどちらかを入れても良さそうですが、実は`zone`を使用すると問題があります。

ソースコードを読めば、これが理解できるはずです。下の`node.method_name`は`now`であるためです。

```rb
 def operator?(node)
   (node.send_type? && RESTRICT_ON_SEND.include?(node.method_name)) || node.operator_keyword?
end
```

https://github.com/rubocop/rubocop/blob/master/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb#L85-L87

### Custom Cop Class
```rb
# frozen_string_literal: true

return unless defined?(RuboCop)

module CustomCops
  class BanTimeZoneNow < RuboCop::Cop::Base

    def_node_matcher :time_zone_now_call?, <<~PATTERN
      (send (send (const nil? :Time) :zone) :now)
    PATTERN

    MSG = 'Do not use Time.zone.now. Use Time.current instead.'

    def on_send(node)
      # マッチしない場合は、そのままreturnします
      return unless time_zone_now_call?(node)

      add_offense(node, message: MSG)
    end
  end
end
```

## Auto correctを追加
- extend `RuboCop::Cop::AutoCorrector`

```rb
extend RuboCop::Cop::AutoCorrector
```

- replace pattern

```rb
add_offense(node, message: MSG) do |corrector|
  corrector.replace(node, 'Time.current')
end
```

### 最終的なコード
```rb
# frozen_string_literal: true

return unless defined?(RuboCop)

module CustomCops
  class BanTimeZoneNow < RuboCop::Cop::Base
    extend RuboCop::Cop::AutoCorrector

    def_node_matcher :time_zone_now_call?, <<~PATTERN
      (send (send (const nil? :Time) :zone) :now)
    PATTERN

    MSG = 'Do not use Time.zone.now. Use Time.current instead.'
    RESTRICT_ON_SEND = [:now].freeze

    def on_send(node)
      # マッチしない場合は、そのままreturnします
      return unless time_zone_now_call?(node)

      add_offense(node, message: MSG) do |corrector|
        corrector.replace(node, 'Time.current')
      end
    end
  end
end

```

## Custom Copを適用
- .rubocop.yml

```yml
require:
  - rubocop-rails
  - ./lib/custom_cops/ban_time_zone_now.rb

CustomCops/BanTimeZoneNow:
  Enabled: true
```

## テスト
```rb
❯ rubocop
Inspecting 241 files
................................................................................C................................................................................................................................................................

Offenses:

app/models/program.rb:46:47: C: [Correctable] CustomCops/BanTimeZoneNow: Do not use Time.zone.now. Use Time.current instead.
  scope :unexpired, -> { where('end_at >= ?', Time.zone.now) }
                                              ^^^^^^^^^^^^^

241 files inspected, 1 offense detected, 1 offense autocorrectable
```

## 参照
- [rubocop \- Why doen't this send pattern match? \- Stack Overflow](https://stackoverflow.com/questions/59396748/why-doent-this-send-pattern-match)
- [Development :: RuboCop Docs](https://docs.rubocop.org/rubocop/development.html#basics)
- [rubocop/lib/rubocop/cop/lint/ambiguous\_operator\_precedence\.rb at master · rubocop/rubocop](https://github.com/rubocop/rubocop/blob/master/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb#L85-L87)
- [RuboCopのカスタムルールで、Time\.currentに促す](https://zenn.dev/greendrop/articles/6b21fa5a8ce705)