Ruby on Rails 7.2の主要な新機能・機能追加・変更点

## はじめに

Rubyを使用したOSSのWebアプリケーションフレームワーク、Ruby on Rails（以下Rails）の最新バージョンである7.2（以下Rails 7.2）が[2024年8月10日にリリースされました](https://rubyonrails.org/2024/8/10/Rails-7-2-0-has-been-released)。

Rails 7.2は、開発用のDockerコンテナの設定の生成、YJITのデフォルト化、RuboCop / Brackeman / GitHub CIの設定の生成など、主に、プロジェクトを開発、運用するに当たって必要な設定をあらかじめフレームワーク側で用意したリリースとなっています。

本記事では、[Railsの公式ブログ](https://rubyonrails.org/blog/)や[Railsガイド](https://guides.rubyonrails.org/v7.2/)、GitHubのRailsプロジェクトの[Issues](https://github.com/rails/rails/issues)や[Pull Requests](https://github.com/rails/rails/pulls)の内容をもとに、Rails 7.2の主要な機能追加・変更点の紹介を行います。

※ 以前のバージョンのRailsの主要な新機能・機能追加・変更点については以下を参照してください。

* [Ruby on Rails 7.1の主要な新機能・機能追加・変更点](https://qiita.com/ryohashimoto/items/ed669f2e529d489e9fe4)
* [Ruby on Rails 7の主要な新機能・機能追加・変更点](https://qiita.com/ryohashimoto/items/f5382478c78f296d8291)

## 注意点

### Rubyのバージョン

Rails 7.2を動かすには、Ruby 3.1.0以上が必要になります。

### Railsのサポート対象のバージョン

Rails 7.2のリリース後は、バグ修正の対象がバージョン7.2、セキュリティ修正の対象が7.2、7.1、深刻なセキュリティ修正の対象が7.2、7.1、7.0、6.1系列となります。それより前のバージョンに関してはサポートは行われません。

**参考**

* [Maintenance Policy for Ruby on Rails](https://guides.rubyonrails.org/maintenance_policy.html)

## 新機能

### 開発用のDockerコンテナ（devコンテナ）

Dockerコンテナをフル機能の開発環境として使用できるdevコンテナと呼ばれる新機能が追加されました。

デフォルトでは、以下のようなコンテナが含まれています。

* データベース（SQLite、PostgreSQL、MySQL、MariaDBなど）
* Active Storage
* Redis
* システムテスト（Headless Chrome）

新規のアプリケーションでdevコンテナを使用するには、`rails new`でアプリケーションを生成する際に`--devcontainer`オプションを追加します。

```
$ rails new myapp --devcontainer
```

既存のアプリケーションは、Railsの`devcontainer`コマンドを使用することで、devコンテナの設定を生成することができます。

```
$ rails devcontainer
Generating Dev Container with the following options:
app_name: blog
database: sqlite3
active_storage: true
redis: true
system_test: true
node: false
      create  .devcontainer
      create  .devcontainer/devcontainer.json
      create  .devcontainer/Dockerfile
      create  .devcontainer/compose.yaml
        gsub  test/application_system_test_case.rb
```

`.devcontainer`ディレクトリ以下に生成された、これらの設定ファイルは[Development Container Specification](https://containers.dev/implementors/spec/)に基づいており、VSCodeなどの対応したエディタで開くことによって、Dockerイメージを取得し、コンテナを生成、起動することが可能です。

**参考**

* [Getting Started with Dev Containers — Ruby on Rails Guides](https://edgeguides.rubyonrails.org/getting_started_with_devcontainer.html)
* [Development containers](https://containers.dev/)

### YJITのデフォルト化

YJITはRubyのJITコンパイラで、有効にすることでRailsアプリケーションのパフォーマンスを大幅に向上させ、レイテンシを15〜25%改善することができます。
Rails 7.2でRuby 3.3以上を使用している場合は、YJITがデフォルトで有効になっています。
無効にする場合は、以下のように設定を行う必要があります。

```ruby
Rails.application.config.yjit = false
```

### ブラウザのバージョンのGuardのデフォルト化

アプリケーション生成時に以下の設定がApplication Controllerに追加されるようになりました。

```ruby
allow_browser versions: :modern
```

これは、リクエストのヘッダのUser-Agentに基づいて、モダンブラウザ（WebP画像、Import maps、CSS入れ子などの機能をサポート）のアクセスを許可するというもので、古いバージョンのブラウザに対しては許可せず、HTTP status 406を返すようになっています。
User-Agentがリクエストのヘッダに含まれない場合もアクセスを許可する点には注意してください。

この設定は各コントローラでカスタマイズ可能で、以下のように特定のブラウザの特定のバージョン以上を許可することもできます。

```ruby
# Safari 16.4+、Firefox 121+をサポートし、IEをサポートしない
allow_browser versions: { safari: 16.4, firefox: 121, ie: false }
```

また、以下のように`only`オプションをつけることによって、コントローラの特定のアクションのみ、ブラウザのバージョンの制限をかけることができるようになっています。

```ruby
# showアクションで、Opera 104+、Chrome 119+をサポートする
allow_browser versions: { opera: 104, chrome: 119 }, only: :show
```

**参考**
* [ActionController::AllowBrowser::ClassMethods - Ruby on Rails API](https://edgeapi.rubyonrails.org/classes/ActionController/AllowBrowser/ClassMethods.html#method-i-allow_browser)


### PWA (Progressive Web Application)ファイルのデフォルト化

RailsでPWAアプリケーションを作成可能なように、デフォルトで`manifest.json`ファイルと`service-worker.js`ファイルを`app/view/pwa`以下に生成し、以下のルーティングを追加するようになりました。

```ruby
get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker
get "manifest" => "rails/pwa#manifest", as: :pwa_manifest
```

**参考**
* [Add default pwa manifest and service worker file by dhh · Pull Request #50528 · rails/rails](https://github.com/rails/rails/pull/50528)

### RuboCopのデフォルト化

デフォルトでRuby静的構文アナライザとフォーマッタである[RuboCop](https://github.com/rubocop/rubocop)が含まれるようになり、RuboCopのルール集として[rubocop-rails-omakase](https://github.com/rails/rubocop-rails-omakase)を使用するようになりました。
`rails new`でRailsアプリケーションを作成後にすぐに`rubocop`コマンドでフォーマットを行うことができます。

このルール集は、Rails作者のDHHが作成したもので、チーム内で特定のコーディングスタイルを適用していない場合の出発点として便利に使うことができそうです。

**参考**

* [rails/rubocop-rails-omakase: Omakase Ruby styling for Rails](https://github.com/rails/rubocop-rails-omakase)

### Brakemanのデフォルト化

デフォルトでRubyのセキュリティ脆弱性ツールの[Brakeman](https://brakemanscanner.org/)が含まれるようになりました。
`brakeman`コマンドでセキュリティチェックを行うことが可能になり、また、後述するGitHub CIワークフローでも、GitHubにコミットがpushされる毎に、Brakemanを走らせることができます。

**参考**

* [Add Brakeman by default to new apps by vipulnsward · Pull Request #50507 · rails/rails](https://github.com/rails/rails/pull/50507)

### GitHub CIワークフローのデフォルト化

デフォルトでGitHub CIワークフローが作成されるようになり、`.github/workflows/ci.yml`、`.github/dependabot.yml`ファイルが生成されるようになりました。
(`rails new`を実行する際に、`--skip-ci`オプションをつけることで、ファイルの生成をスキップすることができます。）
初期状態ではGitHub CIでテストと、前述したRuboCopとBrakemanを実行することができます。テストでRSpecを実行したり、カスタマイズを行う場合は`.github/workflows/ci.yml`ファイルを修正する必要があります。

**参考**

* [Default to creating GitHub CI files by dhh · Pull Request #50508 · rails/rails](https://github.com/rails/rails/pull/50508)

## 機能追加

### DBのトランザクションへのコールバックの追加

DBのトランザクションのコミットやロールバックの前後にコールバックを指定して、実行できるようになりました。

具体的には、`ActiveRecord::Base.transaction`メソッドが、`ActiveRecord::Transaction`オブジェクトをブロックに渡すようになり、例えば以下のように`transaction.after_commit`でトランザクション内でコールバックを指定できるようになっています。

```ruby
Article.transaction do |transaction|
  article.update(published: true)

  transaction.after_commit do
    PublishNotificationMailer.with(article: article).deliver_later
  end
end
```

上記の例ではトランザクション内でメール送信のジョブをエンキューするのではなく、トランザクションのコミットを行ってから、エンキューを行うことができます。

**参考**

* [Allow to register transaction callbacks outside of a record by casperisfine · Pull Request #51474 · rails/rails](https://github.com/rails/rails/pull/51474)

### DBのトランザクション内でジョブをエンキューした場合はトランザクション外で実行するように

トランザクション内にジョブをエンキューするコードを含む場合に、自動で上記の`after_commit`コールバックを使用して、コミット後に処理を行うようになりました。コミットに失敗した場合は、エンキューされたジョブは破棄されます。

以下のようなコードで、

```ruby
Topic.transaction do
  topic = Topic.create

  NewTopicNotificationJob.perform_later(topic)
end
```

以前は`topic`の保存がコミットされる前に`NewTopicNotificationJob`が実行され、別プロセスのジョブが永続化された`topic`を取得できず、エラーを発生することがありました。Rails 7.2以降では、同じコードでも`topic`の保存のコミット後にジョブが実行されるようになります。

ジョブ内で以下のように設定することで、元の挙動（トランザクション内でコミット前にエンキュー）にすることも可能です。

```ruby
self.enqueue_after_transaction_commit = :never
```

**参考**

* [Automatically delay Active Job enqueues to after commit by casperisfine · Pull Request #51426 · rails/rails](https://github.com/rails/rails/pull/51426)

## 変更点

### Pumaのスレッド数のデフォルトの変更

Pumaのデフォルトのスレッド数が5から3へと変更になりました。

これは、本番環境で運用されているアプリケーションを分析した結果、スレッド数が多すぎるとレイテンシに影響を及ぼすことが判明したためだそうです。

具体的には、元の5スレッドは低速のSQLクエリやインラインの処理を想定してましたが、複数の高速なSQLクエリや、ジョブ経由の処理には向いておらず、新しい3スレッドはこれらに向けて最適化した形だそうです。

このスレッド数は37signalsのアプリケーションに合わせて設定された値なので、それぞれのアプリケーションの処理やインフラ構成に応じてスレッド数を調整するのが良さそうです。

**参考**

* [Set a new default for the Puma thread count · Issue #50450 · rails/rails](https://github.com/rails/rails/issues/50450)


## 参考

* [Ruby on Rails 7.2 Release Notes — Ruby on Rails Guides](https://edgeguides.rubyonrails.org/7_2_release_notes.html)