RuboCop Rails (rubocop-rails) のリリース内容で Rails の情報をキャッチアップする

# この記事は？

Ruby の静的コード解析の定番 Gem の [RuboCop](https://github.com/rubocop/rubocop)。そのひとつひとつのルールのことを Cop と呼びます。そして、[RuboCop Rails](https://github.com/rubocop/rubocop-rails) は Rails 向けのルールを RuboCop に追加するための Gem です。

ふと、RuboCop Rails で追加された Cop (ルール) を確認することで、Rails の知らない機能を知るいい機会になるのではと思いました。そこで、試しに 2022/09/09 (金) にリリースされたバージョン 2.16.0 の内容を見てみました :eyes: 

https://github.com/rubocop/rubocop-rails/releases/tag/v2.16.0

そうすると、案の定知らないことばかりで非常に勉強になりました！そこで、実際にどんな学びがあったかこの記事で共有したいと思います 🙌

# 例: RuboCop Rails 2.16.0 追加された Cop

## [#714](https://github.com/rubocop/rubocop-rails/pull/714): Add new `Rails/FreezeTime` cop.

- [travel_to](https://api.rubyonrails.org/v7.0.3.1/classes/ActiveSupport/Testing/TimeHelpers.html#method-i-travel_to) メソッドの引数に現在日時を渡す場合は、代わりに [freeze_time](https://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html#method-i-freeze_time) メソッドを使うべし。
  - freeze_time メソッドは Rails 5.2 で追加されました。
  - わたしはいつも travel_to を使ってしまっていました :innocent: 

```ruby
# Bad 🙅
travel_to(Time.now)
travel_to(DateTime.now)
travel_to(Time.current)
travel_to(Time.zone.now)
travel_to(Time.now.in_time_zone)
travel_to(Time.current.to_time)

# Good 🙆
freeze_time
```

## [#744](https://github.com/rubocop/rubocop-rails/pull/744): Add new `Rails/WhereMissing` cop.

- LEFT JOIN して関連レコードがないレコードに絞り込みたい場合は [missing](https://api.rubyonrails.org/classes/ActiveRecord/QueryMethods/WhereChain.html#method-i-missing) メソッドを使うべし。
  - missing メソッドは Rails 6.1 で追加されました。

```ruby
# Bad 🙅
Post.left_joins(:author).where(authors: { id: nil })

# Good 🙆
Post.where.missing(:author)
```

## [#587](https://github.com/rubocop/rubocop-rails/pull/587): Add new `Rails/RootPathnameMethods` cop.

- Rails.root.join が返す [Pathname](https://docs.ruby-lang.org/ja/latest/class/Pathname.html) オブジェクトを使用してファイルを操作する場合は、File クラスを使わずに Pathname オブジェクトのメソッドを使うべし。

```ruby
# Bad 🙅
File.open(Rails.root.join('db', 'schema.rb'))
File.open(Rails.root.join('db', 'schema.rb'), 'w')
File.read(Rails.root.join('db', 'schema.rb'))
File.binread(Rails.root.join('db', 'schema.rb'))
File.write(Rails.root.join('db', 'schema.rb'), content)
File.binwrite(Rails.root.join('db', 'schema.rb'), content)

# Good 🙆
Rails.root.join('db', 'schema.rb').open
Rails.root.join('db', 'schema.rb').open('w')
Rails.root.join('db', 'schema.rb').read
Rails.root.join('db', 'schema.rb').binread
Rails.root.join('db', 'schema.rb').write(content)
Rails.root.join('db', 'schema.rb').binwrite(content)
```

## [#752](https://github.com/rubocop/rubocop-rails/pull/752): Add `Rails/TopLevelHashWithIndifferentAccess` cop.

- `::HashWithIndifferentAccess` は非推奨なので、代わりに `ActiveSupport::HashWithIndifferentAccess` を使うべし。
  - Rails 5.1 から非推奨 (soft-deprecated) です。

```ruby
# Bad 🙅
HashWithIndifferentAccess.new(foo: 'bar')

# Good 🙆
ActiveSupport::HashWithIndifferentAccess.new(foo: 'bar')
```

## [#759](https://github.com/rubocop/rubocop-rails/pull/759): Add new `Rails/ActionControllerFlashBeforeRender` cop.

- `render` の前で flash メッセージを設定する場合は `flash.now` を使うべし。
  - さもないと、次のアクションまで flash メッセージが意図せず残り続ける場合があります。
  - 参考: [flashとflash.nowの違いを検証してみた - Qiita](https://qiita.com/taraontara/items/2db82e6a0b528b06b949)

```ruby
# Bad 🙅
class HomeController < ApplicationController
  def create
    flash[:alert] = "msg"
    render :index
  end
end

# Good 🙆
class HomeController < ApplicationController
  def create
    flash.now[:alert] = "msg"
    render :index
  end
end
```

## [#749](https://github.com/rubocop/rubocop-rails/pull/749): Add new `Rails/ActiveSupportOnLoad` cop.

- Rails が提供するクラスにモンキーパッチを当てる場合は [ActiveSupport.on_load](https://api.rubyonrails.org/classes/ActiveSupport/LazyLoadHooks.html#method-i-on_load) を使うべし。
  - そうすることで遅延読み込みになり、必要になって初めてパッチが読み込まれるようになります。

```ruby
# Bad 🙅
ActiveRecord::Base.include(MyClass)

# Good 🙆
ActiveSupport.on_load(:active_record) { include MyClass }
```

## [#747](https://github.com/rubocop/rubocop-rails/pull/747): Add `Rails/ToSWithArgument` cop.

- `to_s(format)` は Rails 7.0 から非推奨であるためです。
  - Ruby 3.1 で to_s メソッドが最適化されているため、Rails 7.0 ではこのメソッドのオーバーライドを避けて最適化の恩恵を受けるように対応されています。
  - 参考: [Deprecate `to_s(format)` in favor of `to_formatted_s(format)` by rafaelfranca · Pull Request #43772 · rails/rails · GitHub](https://github.com/rails/rails/pull/43772)

```ruby
# Bad 🙅
obj.to_s(:delimited)

# Good 🙆
obj.to_formatted_s(:delimited)
```

# まとめ

RuboCop Rails の特定のリリース内容を確認して Cop が追加された背景を調べることで、Rails の知らない機能や非推奨な機能を知ることができます。今回の調査はとても有用だと感じたので、今度もバージョン更新のたびにリリース内容を確認していこうと思いました。みなさんもぜひ、気が向いたときに確認してみてくださいね :muscle: 

# 参考

- [GitHub - rubocop/rubocop-rails: A RuboCop extension focused on enforcing Rails best practices and coding conventions.](https://github.com/rubocop/rubocop-rails)