Skip to content

Ruby on Rails PR Digest - 2025年 12月

このページは rails/rails リポジトリにマージされたPull Requestを自動的に収集し、AIで要約したものです。

#56484 Remove deprecated fetch cast type

マージ日: 2025/12/30 | 作成者: @seuros

  1. 概要 (1-2文で)
    Rails 8.1 以降では schema cache dump に常に cast_type が含まれるようになったため、接続オブジェクトから cast_type(キャスト用の型情報)をフォールバック取得する古い互換コードが削除されました。これにより Active Record の型キャスト周りの実装が簡素化され、古いバージョン向けのデプリケート経路が整理されています。

  1. 変更内容の詳細

※この PR は「振る舞いの変更」よりも「古い経路の削除/整理」が中心です。主なポイントを機能単位でまとめます。

(1) cast_type のフォールバック削除

これまでは、以下のような流れのコードが内部的に存在していました(概念的な例):

ruby
# 旧来のイメージ
column = schema_cache.columns(table_name)[column_name]

# 新しめの経路(schema cache に cast_type がある場合)
cast_type = column.cast_type if column.respond_to?(:cast_type)

# 互換用フォールバック(古い schema cache には cast_type が無い可能性があった)
cast_type ||= connection.lookup_cast_type_from_column(column)

Rails 8.1 以降では:

  • schema cache dump には常に cast_type が含まれている
  • よって、「なければ接続から取得する」処理が不要

この PR で、上記のような ||= connection.xxx といったフォールバック経路が各所から削除されています。

影響する主な場所:

  • activerecord/lib/active_record/attributes.rb
  • activerecord/lib/active_record/connection_adapters/column.rb
  • activerecord/lib/active_record/type_caster/connection.rb
  • 各種 adapter(PostgreSQL, SQLite3)まわりの quoting/database_statements

結果として:

  • カラムの型キャストは「schema cache の cast_type を使う」という前提に一本化
  • connection 経由で動的に cast_type を問い合わせる古いパスは削除

(2) quoting / database_statements 周りの簡素化

quoting.rbdatabase_statements.rb では、型情報を取得して値を SQL に埋め込むためのロジックがありましたが、そこでも「cast_type があればそれを使う/なければ connection から取得」という if/else・条件分岐が存在していました。

この PR で:

  • cast_type が schema cache に存在する前提で記述できるようになったため、条件分岐が減少
  • 引数として渡る型オブジェクトやカラム定義から、直接 cast/quote するだけのシンプルな形に整理

(3) schema 定義・dumper 周りの TODO 解消

abstract/schema_definitions.rbschema_dumper.rb などに残っていた「cast_type がない古い形式との互換性」のための TODO コメントや分岐が削除されています。

説明文にもある通り:

Now that Rails 8.1 is released, all schema cache dumps include the cast_type attribute, so we no longer need to fall back to fetching it from the connection.

という TODO に対応した「片付け」の PR です。

(4) テストコードの更新

テスト側では以下の整理が行われています。

  • schema_cache_test.rb の中で「cast_type がない schema cache を扱うケース」用のテストや、フォールバックの挙動を確認していたテストが削除
  • migration 関連テスト (change_schema_test.rb, columns_test.rb) も、新しい前提(cast_type が常に存在する)に沿うように期待値やセットアップが簡素化

これにより、テストコードも現行バージョンの前提のみを検証するものになっています。


  1. 影響範囲・注意点

対象バージョン・互換性

  • Rails 8.1 以降を前提としたクリーンアップなので、「Rails 本体だけを通常利用しているアプリケーション」に対して目に見える挙動変更はほぼありません。
  • 影響が出る可能性があるのは、内部 API に依存しているライブラリやメタプログラミングを多用したコードです。

影響しうるケース

  1. 独自に schema cache dump を生成・加工している場合

    • Rails 8.1 より前形式の schema cache(cast_type を含まない)を、そのまま新しい Rails に読み込ませようとしていると、今回のフォールバック削除により想定外の動作をする可能性があります。
    • 対応策: Rails 8.1 以降の環境で schema cache を再生成しておくことを推奨します。
  2. ActiveRecord::ConnectionAdapters::Column や type_caster の private/内部的 API に依存している場合

    • cast_type がなければ connection から調べる」といった挙動に期待していたコードは壊れます。
    • 新しい前提: カラムには常に cast_type が紐付いているとみなすべきで、なければそれは想定外(= バグ)扱いです。
  3. 独自 adapter / connection adapter 拡張

    • もし独自 adapter で schema cache の dump/load ロジックをカスタマイズしている場合、cast_type を含めて dump しているかどうかを確認しておく必要があります。

パフォーマンス上の意味合い

  • cast_type を逐次 connection から問い合わせる経路がなくなるため、理論上はキャッシュミス時の余計な問い合わせが減り、型キャストまわりがわずかにシンプルかつ高速になります(ただし体感できるほどではない可能性が高いです)。

  1. 参考情報 (あれば)
  • 対象 PR: https://github.com/rails/rails/pull/56484

  • 関連する前提機能: Rails 8.1 における schema cache dump のフォーマット(cast_type を常に含む)がすでにリリース済み

  • schema cache 再生成例:

    bash
    # 本番相当環境などで
    bin/rails db:schema:cache:dump

    これにより、最新の Rails が期待する形式(cast_type 含む)で schema cache が再生成されます。


#56482 Fix merging relations with arel equality predicates with null relations

マージ日: 2025/12/29 | 作成者: @fatkodima

  1. 概要 (1-2文で)
    Rails の ActiveRecord::Relationmerge する際、Arel の等価比較 (=) を含む where 句と、**NULL を返す relation(例: Model.none や空条件)**をマージしたときに発生していた不具合を修正する PR です。
    これにより、where(x: y) などの等価条件を含む relation を他の relation と安全にマージできるようになりました。

  1. 変更内容の詳細

どんな問題だったか

関連 Issue: #56481 で報告されているのは、例えば以下のようなケースです(イメージ):

ruby
scope :by_status, ->(status) { where(status: status) }      # Arel 等価条件を持つ relation
scope :maybe_filtered, ->(flag) { flag ? where(active: true) : none }

rel1 = User.by_status("active")        # => WHERE "users"."status" = 'active'
rel2 = User.maybe_filtered(false)      # => User.none (内部的には特殊な NULL relation 扱い)

merged = rel1.merge(rel2)
# ここで例外が出たり、不正な SQL が生成されたりする不具合があった

ポイントは:

  • arel_table[:column].eq(value) のような Arel の等価述語 (=) を含む where 句がある
  • それと 「null relation」的なもの(none など)を merge したときに
    • 例外が発生する
    • あるいは予期しない where 条件になる
      といった挙動になっていた、という問題です。

実際の変更点

変更ファイルは 2 つのみです。

1. activerecord/lib/active_record/relation/where_clause.rb (+1 / -1)

WhereClause のマージロジック周辺で、NULL relation と Arel 等価述語を含む where_clause をマージしたときの扱いが 1 行修正されています。

このあたりのコードは簡略化すると以下のようなイメージです(実際のコードとは多少異なりますが、趣旨を説明するための疑似コードです):

ruby
def +(other)
  # 従来: null な side(他方の where_clause が空もしくは特殊な null)とのマージで
  # Arel 等価述語を正しく保持できないケースがあった
  WhereClause.new(predicates + other.predicates)
end

修正により、「null な relation とのマージ時に Arel 等価述語が壊れない」ような条件分岐・扱いに変わっています。
実際の変更は 1 行ですが、WhereClause の内部で「空/null の条件」と「等価比較を含む実際の条件」とを安全に合成できるようにしたものです。

2. activerecord/test/cases/relation/merging_test.rb (+8 / 0)

再発防止のため、マージ処理に関するテストが追加されています。
内容としては、

  • 等価比較(arel_table[:column].eq(value)where(column: value))を含む relation と
  • 何も条件を持たない、あるいは NULL 的な relation

merge したときに、

  • 例外が起きないこと
  • 期待した WHERE 句が得られること(等価条件が失われない/壊れないこと)

を確認するテストです。

擬似コード例:

ruby
def test_merging_equality_predicate_with_null_relation
  relation_with_eq = Post.where(Post.arel_table[:id].eq(1))
  null_relation     = Post.none # または内部的に null 的扱いをされる relation

  merged = relation_with_eq.merge(null_relation)

  assert_equal relation_with_eq.where_clause, merged.where_clause
end

  1. 影響範囲・注意点
  • 影響範囲:
    • ActiveRecord::Relation#merge を多用しているコードベース全般
    • とくに Arel で自前の predicate を組み立てているケース(arel_table[:col].eq(...) など)
    • スコープチェーンで none, 条件分岐付きスコープなどを組み合わせている箇所
  • 期待される挙動:
    • これまで merge 時に例外やおかしな where 句が出ていた箇所が、正常に動作するようになる はずです。
    • 逆に「既存の動作に依存したワークアラウンド」を書いていた場合、その前提が崩れる可能性はありますが、そのようなコードは基本的にバグを前提にしているため削除・簡略化が望ましいです。
  • マイグレーションの必要性:
    • データベーススキーマやマイグレーションには影響しないため、特別な対応は不要です。
  • バージョンアップ時のテスト:
    • scopemerge を組み合わせて複雑な検索条件を組んでいる箇所(検索画面・管理画面など)を中心に、
      • 例外が出ていないか
      • 生成される SQL(to_sql)が想定どおりか
        を確認すると安心です。

  1. 参考情報 (あれば)
  • PR: https://github.com/rails/rails/pull/56482
  • 関連 Issue: https://github.com/rails/rails/issues/56481
  • 関連コード:
    • ActiveRecord::Relation#merge
    • ActiveRecord::Relation::WhereClauseactiverecord/lib/active_record/relation/where_clause.rb
  • 類似するバグパターンとして、「mergeor, not, none などを含む relation を組み合わせたときの where 句の壊れ方」が過去にも度々修正されており、今回もその一種と考えると理解しやすいです。

#56458 Select only relevant SQL for payload name on eagerload test

マージ日: 2025/12/29 | 作成者: @zzak

  1. 概要 (1-2文で)
    このPRは、Active Record の eager load に関するインストゥルメンテーションテストで、sql.active_record 通知のうち「books テーブルに対するクエリ」だけを対象にするようフィルタリングを追加した修正です。これにより、SHOW max_identifier_length など別目的の SQL が混ざってテストが不安定になる問題 (#56456) を防ぎます。

  1. 変更内容の詳細

対象ファイル:

  • activerecord/test/cases/instrumentation_test.rb (+2 / -1)

背景:
sql.active_record の通知は、アプリケーションレベルのクエリだけでなく、アダプタの初期化などで発行される内部的なクエリ(例: SHOW max_identifier_length)も含まれます。
本テストでは eager loading 時の SQL を検証したいにもかかわらず、そのすべての sql.active_record 通知を一括で対象にしていたため、想定外の SQL が混入してテスト結果の判定に影響していました。

修正内容の要点:

  • テストで収集する sql.active_record 通知の中から、books テーブルに対する SELECT 文だけを対象にするフィルタを追加/変更しています。

  • 説明にあるように、テストが本当に見たいのは以下のようなクエリです:

    sql
    SELECT "books"."id" AS t0_r0, "books"."author_id" AS t0_r1, ...
    FROM "books"
    LEFT OUTER JOIN "authors" ON "authors"."id" = "books"."author_id"
  • 一方で、例えば以下のようなクエリも sql.active_record として通知されるため、従来はこれらもテストの対象になってしまっていました:

    sql
    SHOW max_identifier_length
  • このPRでは、「このファイル内の他のテストと同様に」テスト内で SQL をフィルタして、books テーブルに対するクエリだけを payload 名・SQL 判定に使うように変更しています。

    • 典型的には、payload[:sql]name に対する条件で FROM "books" を含むか、あるいは不要な SHOW などを除外する、といったフィルタリングが行われます。
    • 実際のコードは1行削除・2行追加程度の小さな差分で、notifications を集計するブロック内、もしくはそこから取り出す段階で、対象となるエントリを select / reject などで絞り込んでいると考えられます。

サンプルイメージ(実際のコードイメージ、概念的なもの):

ruby
# 変更前イメージ
sql = @queries.find { |event| event.payload[:name] == "SQL" }

# 変更後イメージ(例)
sql = @queries
  .select { |event| event.payload[:sql].include?('FROM "books"') }
  .first

実際にはこのファイル内の他のテストと同様のスタイルで、SHOW 系のクエリを除外する、あるいは books を含む SQL だけを選ぶような条件が追加されています。


  1. 影響範囲・注意点
  • 影響範囲:

    • Rails 本体の挙動(本番コードパス)は一切変更されず、テストコード (instrumentation_test.rb) のみが変更対象です。
    • したがって、Rails を利用しているアプリケーションへの直接の影響はありません。
    • 主な効果は、CI 環境やローカルでの Rails 本体テストスイート実行時に、eager load 関連のインストゥルメンテーションテストが安定することです。
  • 注意点:

    • この種のフィルタリングは、DBアダプタ・バージョン・設定などにより発行される余分な SQL が増えた場合にも有効ですが、フィルタ条件が厳しすぎる/緩すぎると再びテスト不安定要因になりうるため、今後 SQL 文字列のフォーマットが変わった場合は再調整が必要になる可能性があります。
    • テストでインストゥルメンテーション (ActiveSupport::Notifications) を扱う場合には、「目的の SQL かどうかを判定するフィルタ」を明示的に入れるのが推奨される、という指針にもなります。

  1. 参考情報 (あれば)
  • 対応 Issue:
    • #56456 – eager load テストで意図しない sql.active_record 通知(例: SHOW max_identifier_length)が紛れ込む問題の修正
  • 関連箇所:
    • activerecord/test/cases/instrumentation_test.rb 内の、ActiveRecord::Base の SQL インストゥルメンテーション (sql.active_record) を検証するテスト群
    • 同ファイルの「他のテスト」も同様に特定の SQL をフィルタリングしているため、本PRはそれと整合した形に揃える変更でもあります。

#56463 Bump Ruby version to 4.0.0 in devcontainer

マージ日: 2025/12/29 | 作成者: @yahonda

  1. 概要 (1-2文で)
    Rails の開発用 Dev Container(.devcontainer)で使用する Ruby のバージョンが 3.x 系などから Ruby 4.0.0 に更新されました。VS Code Dev Containers / GitHub Codespaces などで Rails を開発する際、デフォルトで Ruby 4.0.0 が使われるようになります。

  1. 変更内容の詳細
  • 対象ファイル: .devcontainer/Dockerfile
  • 変更内容は「コンテナ内で利用する Ruby バージョン指定」を Ruby 4.0.0 に上げる、という一点のみです(+2/-2 行)。

推測される変更イメージは以下のようなものです(実際の PR を要約した擬似コード):

Dockerfile
# 変更前(例)
ARG RUBY_VERSION=3.3.0

# 変更後(例)
ARG RUBY_VERSION=4.0.0

PR の説明にあるように、DevContainer 内で ruby -v を実行すると以下のようになります:

bash
$ ruby -v
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [aarch64-linux]

ここから分かるポイント:

  • Ruby 本体は 4.0.0 (2025-12-25 リリース版想定)
  • PRISM パーサがデフォルト有効のビルド
  • アーキテクチャは aarch64-linux(ARM64 Linux)向けイメージ

また、PR 説明にある通り、この変更は先に rails/devcontainer リポジトリ側で Ruby 4.0 のサポートが入った (rails/devcontainer#106) ことを前提にしています。その新しいベースイメージを使うように Rails 本体の .devcontainer を追従させた PR、という位置付けです。


  1. 影響範囲・注意点

影響範囲

  • 対象となるのは Dev Container 環境のみ です。
    • VS Code Remote - Containers / Dev Containers
    • GitHub Codespaces
    • .devcontainer/Dockerfile をベースにしたローカル Docker 開発環境
  • Rails 自体の gem コードやテストコードには変更はありません。

想定される影響・注意点

  1. Ruby 4.0.0 での互換性確認が必要

    • Rails 本体の開発・CI では Ruby 4.0.0 でテストが回ることが前提となるため、Ruby 4 系での非互換・警告・新機能に依存した問題が早期に表面化します。
    • プロジェクト独自の開発でこの DevContainer を流用している場合、アプリ側の gem / アプリコードが Ruby 4.0.0 に対応しているか確認が必要です。
  2. PRISM パーサ利用による挙動差

    • +PRISM ビルドであるため、Ruby の構文解析周りで微妙な挙動差やパフォーマンス差が発生する可能性があります。
    • RuboCop や Syntax 解析系ツールを使っている場合、Ruby 4 + PRISM を前提とした更新が追いついていないと警告やエラーが増えることがあります。
  3. ローカル環境との差異

    • 手元で Ruby 3.x を使い続けている開発者は、DevContainer の Ruby 4.0.0 と挙動が異なる可能性があります(特に:
      • 新しい警告
      • deprecated機能の削除
      • パフォーマンス特性の差 など)。
    • Rails のコントリビュート用には、可能であればローカルも Ruby 4.0.0 に合わせるのが望ましいです。
  4. テスト追加・CHANGELOG 更新は無し

    • この PR は開発環境構築用コンテナのバージョンアップのみで、Rails の挙動そのものに直接影響を与える変更ではないため、テスト・CHANGELOG の更新は行われていません。

  1. 参考情報 (あれば)
  • この PR が参照している DevContainer 側の対応:
  • Rails の Dev Container 全体像を把握したい場合:
    • Rails 本体リポジトリの .devcontainer/ ディレクトリ一式
      (Dockerfile / devcontainer.json など)を参照すると、VS Code / Codespaces 向けの標準開発環境構成を確認できます。

#56474 Add ActionDispatch::Request#bearer_token to extract the bearer token

マージ日: 2025/12/28 | 作成者: @dhh

  1. 概要 (1-2文で)
    ActionDispatch::Request#bearer_token メソッドが追加され、Authorization ヘッダから Bearer トークンを簡単かつ統一的に取得できるようになりました。主に API や MCP (Model Context Protocol) リクエストでのトークン認証を想定したユーティリティです。

  1. 変更内容の詳細

2-1. 追加されたメソッド

ActionDispatch::Request#bearer_token が追加されました。
目的:Authorization ヘッダから Bearer トークンだけを安全に抽出するためのヘルパです。

典型的には、以下のようなヘッダを想定しています:

http
Authorization: Bearer abc123xyz

このとき:

ruby
request.bearer_token
# => "abc123xyz"

のようにトークン文字列だけを取得できます。

実装的には以下のような挙動が考えられます(概念的なサンプル):

ruby
def bearer_token
  auth = get_header("HTTP_AUTHORIZATION") || get_header("Authorization")
  return if auth.blank?

  # "Bearer <token>" 形式のみを対象
  scheme, token = auth.to_s.split(" ", 2)
  return unless scheme&.casecmp("Bearer")&.zero?
  token.presence
end

※実際のコード行数(+5行)とテストの内容から、上記のように「Bearer スキームのみを対象に、先頭単語と残りを分離する」シンプルな実装である可能性が高いです。

2-2. テスト追加

actionpack/test/dispatch/request_test.rb に32行分のテストが追加されています。
テストの想定内容は以下のようなケースです:

  • 正常系
    • Authorization: Bearer token123"token123" を返す
  • 異常/境界系
    • Authorization ヘッダが存在しない → nil を返す
    • Authorization が空文字/空白のみ → nil
    • Authorization: Basic xxx など別スキーム → nil を返す
    • Bearer とトークンの間にスペースがないなど、形式が不正 → nil or 無視

これにより、bearer_token の挙動が明確に規定されています。


  1. 影響範囲・注意点

3-1. 影響範囲

  • 対象: ActionDispatch::Request を使うすべての Rails アプリケーション(API モードを含む)
  • 性質: 既存メソッドへの変更ではなく、新規追加のため後方互換性への影響はほぼありません。
  • 利用シーン:
    • API 認証 (JWT, API トークン)
    • MCP 経由のリクエスト処理時の認可判断
    • Rack ミドルウェアやコントローラでのヘッダ解析

3-2. 注意点・設計上のポイント

  • Bearer 以外は対象外
    BasicDigest など他の Authorization スキームには反応せず、nil を返す想定です。
    → 既に Authorization ヘッダを生でパースしている処理がある場合、bearer_token への置き換えは「Bearer 前提」のコードにだけ行うべきです。

  • 形式依存
    たとえば Authorization: Bearer のみ(トークンなし)や、Bearer の前後の空白が異常な形式の場合、nil になる可能性があります。
    → 不正なトークン形式を 401 にしたい場合は、nil チェックを行い、エラー応答を返す責務はアプリ側にあります。

  • 大文字小文字
    多くの場合 Bearer のスキーム名は大文字小文字を無視する実装になりますが、テストで case-insensitive が担保されているか(Bearer / bearer / BEARER)を前提に使うとよいです。

  • 既存実装からの移行
    すでに以下のようなコードがある場合:

    ruby
    auth = request.headers["Authorization"]
    token = auth.to_s.split(" ").last if auth&.start_with?("Bearer ")

    これは次のように簡略化できます:

    ruby
    token = request.bearer_token

    共通ユーティリティを使うことで、認証周りの処理を統一しやすくなります。


  1. 参考情報 (あれば)
  • OAuth 2.0 Bearer Token 仕様(RFC 6750)
    https://datatracker.ietf.org/doc/html/rfc6750
    Rails の bearer_token は、この「Authorization: Bearer <token>」形式の実運用での利用をサポートするためのヘルパです。

  • 関連しそうな既存 API(参考)

    • request.authorization(生の Authorization ヘッダ)
    • request.headers["Authorization"](Rack インターフェースとしてのヘッダアクセス)

この PR によって、アプリケーションコードやライブラリ/エンジン側で独自に Authorization をパースする必要が減り、Bearer ベースの認証の実装がより簡潔になります。


#55788 [Activejob] Enable enqueue_after_transaction_commit by default with ActiveRecord and validate invalid configurations

マージ日: 2025/12/28 | 作成者: @mugitti9

  1. 概要 (1-2文で)
    ActiveJobの enqueue_after_transaction_commit のデフォルト値が、ActiveRecord 利用時には true になるように変更され、トランザクション内でenqueueされたジョブはコミット後にキュー投入される挙動が標準になります。併せて、ActiveRecordがない環境で enqueue_after_transaction_commit = true を設定するとエラーにするバリデーションが追加されました。

  1. 変更内容の詳細

デフォルト挙動の変更

これまで:

  • グローバルには enqueue_after_transaction_commit のデフォルトは false
  • トランザクション内で perform_later すると、トランザクションの成否に関係なく即時enqueue されうる
rb
ActiveRecord::Base.transaction do
  user = User.create!(name: "James")
  UserCreationNotifyJob.perform_later(user:) # トランザクション中でも即enqueueされる可能性
  raise ActiveRecord::Rollback
end
# userは永続化されないが、ジョブはキューに残って実行されうる

このPR以降(ActiveRecordがロードされている場合):

  • デフォルト値: enqueue_after_transaction_commit = true になる
  • トランザクション内で perform_later されたジョブは トランザクションコミット成功後にenqueue
  • ロールバックされた場合はenqueueされない
rb
class ApplicationJob < ActiveJob::Base
  # ActiveRecordありの場合、デフォルトで true になる
  # self.enqueue_after_transaction_commit # => true 相当
end

ActiveRecord::Base.transaction do
  user = User.create!(name: "James")
  UserCreationNotifyJob.perform_later(user:)
  raise ActiveRecord::Rollback
end
# ロールバックされるため、UserCreationNotifyJob は enqueue されない

アダプタ・過去バージョンとの整合性

説明によると、Rails 7.2 までは Sidekiq / Resque / Solid Queue など多くのアダプタが「実質的に enqueue_after_transaction_commit = true 相当」の挙動をとっており、多くのアプリは「トランザクションコミット後にジョブがenqueueされる」前提で動いていました。
mainブランチでのデフォルト false はこの期待を裏切り、トランザクション中の未コミットデータに依存するジョブが先に走ってしまう 問題を引き起こしうるため、このPRで安全側に振り戻しています。

ActiveRecord 非利用時のバリデーション

ActiveRecord が無い環境(例えばActiveJobだけを使う純Rubyアプリや、別ORマッパー使用など)では、トランザクションのコールバックが存在しないため、enqueue_after_transaction_commit = true を設定しても意味がありません。

このため、本PRで次のような検証が入ります:

  • ActiveRecord がロードされていない状態で
  • enqueue_after_transaction_commit = true を設定すると

→ 例外を発生させて、誤った設定を早期に検出

イメージ:

rb
class SomeJob < ActiveJob::Base
  self.enqueue_after_transaction_commit = true
end
# ActiveRecord がロードされていない環境では、ここでエラーになる

設定まわり・ガイドの更新

変更ファイルから分かること:

  • activejob/CHANGELOG.md

    • この挙動変更がリリースノートとして明示される
  • guides/source/configuring.md / 8_2_release_notes.md

    • config.active_job.enqueue_after_transaction_commit の説明や、 Rails 8.2 の挙動変更としてドキュメント更新
  • railties/lib/rails/application/configuration.rb

    • アプリケーション設定として config.active_job.enqueue_after_transaction_commit を扱うための変更
  • config/initializers/new_framework_defaults_8_2.rb.tt

    • Rails 8.2 で新規/アップグレード時に、この設定を明示的に制御できるようなテンプレート行が追加
      (例: 新バージョンのデフォルトを段階的に有効化/無効化できるようにする)

  1. 影響範囲・注意点

(1) 既存アプリでの挙動変化

  • ActiveRecord を使用しているアプリで、これまで「トランザクション中に即enqueueされる」前提でジョブを書いていた場合、挙動が変わる 可能性があります。
    • 例: 「DBトランザクションが終わる前にバックグラウンドで別システムに通知を投げたい」などのユースケース

そうしたケースでは、ジョブクラス側で明示的に元の挙動に戻せます:

rb
class ImmediateNotifyJob < ApplicationJob
  self.enqueue_after_transaction_commit = false
end

あるいはグローバル設定として:

rb
# config/application.rb
config.active_job.enqueue_after_transaction_commit = false

※ ただし、このPRの思想としては「安全側(コミット後enqueue)が標準であるべき」というものなので、
本当に必要な箇所だけを false にするのが推奨されます。

(2) レースコンディション・一貫性バグの解消

  • 多くのケースでは、この変更により以下のような微妙な不整合バグが自然に減る/消える ことが期待されます:
    • トランザクションがロールバックされたのに、関連ジョブだけが走ってしまう
    • 未コミットデータをジョブが参照しようとしてレコードが見つからない、など

そのため、「これまで原因不明だったランダムなエラー」が落ち着く可能性もあります。

(3) ActiveRecord を使わない環境での注意

  • ActiveRecord を使わないアプリケーションで、既に enqueue_after_transaction_commit = true を書いていた場合、今後は起動時に例外で落ちる ようになります。
  • その場合は設定を削除するか、false に戻す必要があります。
  • 「トランザクションにフックしてenqueueを遅らせる」こと自体がサポート対象外なので、代替手段(アプリ側で明示的にコミット後に実行するなど)を検討する必要があります。

  1. 参考情報

#56455 Allow Rails.app.creds to access .env values in dev

マージ日: 2025/12/28 | 作成者: @dhh

  1. 概要 (1-2文で)
    Rails 7.2(?) 以降の開発環境において、Rails.app.creds から .env ファイルの値を参照できるようになり、ENV / .env / 暗号化 credentials を統一的な優先順位で扱えるようになりました。これにより、開発時の秘密情報管理や外部ツール連携 (例: 1Password CLI) が、credentials API に一本化されます。

  1. 変更内容の詳細

2-1. 設定の検索順序の変更

Rails.app.creds(内部的には Rails.application.credentials 相当の仕組み)が、次の優先順位で値を探すようになります(開発環境):

  1. 環境変数 ENV
  2. .env ファイル (新規)
  3. 暗号化 credentials (config/credentials.yml.enc など)

これにより、たとえば以下のようなコード:

ruby
Rails.app.creds[:database_url]
# または
Rails.application.credentials.database_url

が、上記の順序で値を解決します。開発中は .env で上書きしつつ、本番では ENV や credentials で安全に運用する、という構成が取りやすくなります。


2-2. .env ファイルの機能: 変数展開とコマンド実行

新たに追加された ActiveSupport::DotEnvConfiguration は、.env ファイルを次のようなルールで解釈します。

変数展開 (interpolation)

.env 内の値の中で ${VAR_NAME} 形式を使うと、同じ .env 内や ENV の値を参照して組み立てできます:

env
DB_HOST=localhost
DB_PORT=5432
DATABASE_URL=postgres://${DB_HOST}:${DB_PORT}/app

この場合、.env ロード後に DATABASE_URL は以下のように展開されます:

text
DATABASE_URL = "postgres://localhost:5432/app"

Rails.app.creds[:database_url] からは、この展開済みの値を取得できます。

コマンド実行

値の中で $(...) を使うと、シェルコマンドを実行してその結果を値として埋め込めます。例として 1Password CLI の利用が想定されています:

env
DB_HOST=$(op read op://Vault/item/value --account=MyAccount)

これにより、.env 自体には生の秘密情報を書かず、別のシークレットストア (1Password, pass, AWS SSM, etc.) から動的に取得した値を Rails.app.creds 経由で利用できます。

2-3. ActiveSupport::DotEnvConfiguration の追加

activesupport/lib/active_support/dot_env_configuration.rb が新規追加され、ActiveSupport::EnvConfiguration と同じ API を持つクラスとして実装されています。

特徴:

  • .env ファイルを読み込んでキー・値のマップを構築
  • 変数展開 ${VAR} の処理
  • $(command) の実行結果を埋め込む処理
  • [], fetch など、EnvConfiguration と同じ操作感のインターフェース

Rails アプリケーション内部では、既存の EnvConfiguration と組み合わせて「ENV + .env + credentials」の統合的な設定レイヤーを構成します。

2-4. Rails 側の統合

railties/lib/rails/application.rb の変更により、開発環境では .env を読み込む DotEnvConfigurationRails.app.creds の構成に組み込まれます。

また、以下も変更されています:

  • railties/lib/rails/generators/rails/app/templates/env.tt
    • Rails new 時に生成される .env テンプレートが追加され、利用が推奨される方向になっています。
  • 各種 generator テスト (app_generator_test, api_app_generator_test) や creds_test.env 連携の挙動を検証するテストが追加。

  1. 影響範囲・注意点

3-1. 対象は主に「開発環境」

.env の参照は「development mode」に限定されています。
本番環境や test 環境では従来どおり「ENV と credentials(およびその設定)」が主体です。

開発と本番で異なるソースから同じキーを供給するケースでは、挙動差に注意が必要です。

3-2. 優先順位の変化による上書き

優先順は「ENV > .env > encrypted credentials」です。つまり:

  • 既に ENV で設定しているキーは、.env や credentials で「上書きできない」
  • .env があると、credentials の値を「隠してしまう」可能性がある

想定外の値が読まれているときは、この優先順位を疑う必要があります。

3-3. コマンド実行の安全性とパフォーマンス

.env$(...) を使う場合の注意:

  • 任意のコマンドが実行されるため、チーム内の .env を不用意に信用しないこと
  • 1Password CLI など外部コマンドに依存するため、環境セットアップが必須
  • 毎プロセス起動時にコマンド実行が走ると、起動時間や開発体験に影響する可能性

特に CI や一部の開発環境では、CLI ツールが入っていない場合に .env ロードが失敗したり、値が空になることがあります。

3-4. .env のバージョン管理ポリシー

.env をどう扱うかはこれまで通りチームごとに決める必要があります:

  • .env.example だけコミットして、本物の .env は各開発者がローカルで管理
  • ある程度の非機密設定は .env をコミット、機密情報だけ他ストアに逃がす
  • 1Password / AWS Secrets Manager などと組み合わせて、.env ではコマンド参照のみを書く

今回の変更により .env の重要度が上がるため、「どこまで .env に書くか」を明確にしておくと運用がスムーズです。


  1. 参考情報 (あれば)
  • 追加クラス: ActiveSupport::DotEnvConfiguration
    • ActiveSupport::EnvConfiguration と同等 API を持つ .env 向け実装。
  • 関連テスト:
    • activesupport/test/dot_env_configuration_test.rb
      • 変数展開・コマンド実行・優先順位などの詳細な挙動が確認できる。
    • activesupport/test/combined_configuration_test.rb
      • ENV / .env / credentials の組み合わせ時の挙動を確認。
    • railties/test/application/creds_test.rb
      • Rails.app.creds から .env が見えることの統合テスト。

この PR によって、「開発は .env、本番は ENV/credentials」というよくある構成でも、アプリコードからは一貫して Rails.app.creds にアクセスするだけで済むようになり、設定アクセスパターンの統一がしやすくなっています。


#56183 Document how to enable Rate Limiting in tests

マージ日: 2025/12/27 | 作成者: @Kerrick

  1. 概要 (1-2文で)
    このPRは、Railsのrate_limitをテスト環境で有効にするための設定方法を公式ドキュメントに追記したものです。デフォルトのテスト用キャッシュ (NullStore) のままだとレートリミットが動作しない、という「ハマりどころ」を明示的に説明しています。

  1. 変更内容の詳細(あればサンプルコードも含めて)
  • 対象: ActionController::RateLimiting::ClassMethods#rate_limit のAPIドキュメント
  • 変更内容: 「バックエンドのキャッシュストアに関する説明」直後に、テスト環境向けの注意書きを1パラグラフ追加

ドキュメントに追加された主旨は次のような内容です(要約):

  • Railsアプリはデフォルトでテスト環境に ActiveSupport::Cache::NullStore を使うよう生成される。
  • rate_limit は内部でキャッシュストアを利用するため、NullStore のままではレートリミットが機能しない。
  • コントローラのレートリミットをテストしたい場合は、テスト環境でも実際に状態を保持するキャッシュストアを設定する必要がある。

実際の設定イメージ(config/environments/test.rb など):

ruby
# config/environments/test.rb

Rails.application.configure do
  # デフォルト生成だとここが NullStore になっていることが多い
  # config.cache_store = :null_store

  # レートリミットをテストする場合は、状態を保持するストアに変更する
  config.cache_store = :memory_store
  # あるいは
  # config.cache_store = :redis_cache_store, { url: ENV["REDIS_URL"] }
end

あるいは、一部のテストだけで一時的にストアを差し替えるパターンもありえます:

ruby
class RateLimitedControllerTest < ActionDispatch::IntegrationTest
  setup do
    @original_cache_store = Rails.cache
    Rails.cache = ActiveSupport::Cache::MemoryStore.new
  end

  teardown do
    Rails.cache = @original_cache_store
  end

  test "respects rate limit" do
    3.times { get some_rate_limited_path }
    assert_response :too_many_requests
  end
end

※PR自体はコードロジックの変更ではなく、こうした点をドキュメントとして明示しただけです。


  1. 影響範囲・注意点
  • 実行時挙動への影響

    • ランタイムコードの変更はなく、ActionController::RateLimiting の挙動は従来通りです。
    • 既存アプリの挙動が変わることはありません(ドキュメント追記のみ)。
  • テスト環境での実務的な影響

    • これまで「rate_limit をテストで呼んでいるのに、リミットがかからない」という状況に気付きにくかった問題が、ドキュメントで明示されます。
    • レートリミットをテストしたい場合は、テスト環境の cache_store 設定を見直す必要があることを、開発者が理解しやすくなります。
  • 注意点

    • NullStore はリクエスト間で状態を保持しないため、レートリミットのように「一定期間のアクセス回数を数える」機能には不向きです。
    • memory_store はシンプルでテスト向きですが、並列テスト・プロセス分割(parallel tests)をしている場合、プロセスごとに独立したメモリを使うため、「プロセスを跨いだレートリミット挙動」は再現できません。並列実行・実環境に近い挙動をテストしたい場合は redis_cache_store などの外部ストアを検討する必要があります。
    • キャッシュを変えると他のテストの前提に影響する可能性があるため、
      • 環境全体の config.cache_store を変えるか
      • テストクラス/テスト単位で一時的に差し替えるか
        をプロジェクトの方針に沿って選ぶ必要があります。

  1. 参考情報 (あれば)

#56433 don't create unused routes in authentication generator

マージ日: 2025/12/23 | 作成者: @gregmolnar

  1. 概要 (1-2文で)
    Rails の認証用ジェネレータが、実際には使われていない RESTful ルートまで routes.rb に生成していた問題を修正し、必要なルートだけを生成するようにした PR です。これにより、不要なルーティングが減り、生成されるアプリのルーティングがシンプルかつ意図に沿ったものになります。

  1. 変更内容の詳細

※ PR 本文にはコード断片がありませんが、差分から読み取れる範囲での解説です。

何が問題だったか

rails g authentication(Rails 8 で入った認証ジェネレータ)を実行すると、生成される config/routes.rb に「コントローラ側で実装していないアクション用のルート」まで含まれていました。典型的には、以下のようにフルセットの RESTful ルートが生成されていたと考えられます。

ruby
# イメージ(以前の状態の例)
resources :sessions
resources :passwords
resources :registrations

しかし、ジェネレータが作るコントローラは、たとえば new/create/destroy だけを使うのに edit/update/index/show などは一切実装していない、といったケースがありました。この PR は、その「未使用アクション用のルート」を削り、実際に使う分だけのルートを明示的に生成するようにしています。

具体的な修正内容

railties/lib/rails/generators/rails/authentication/authentication_generator.rb の中で、routes.rb に書き込むルーティング定義を変更しています。イメージとしては次のような変更です(擬似コード/概念図):

ruby
# 変更前(例)
route <<~RUBY
  resources :passwords
RUBY

# 変更後(例)
route <<~RUBY
  resource :password, only: [:new, :create, :edit, :update]
RUBY

あるいはセッションの場合:

ruby
# 変更前(例)
resources :sessions

# 変更後(例)
resource :session, only: [:new, :create, :destroy]

ポイントは次の通りです。

  • resourcesresource にして単数系にしたり、
  • only: [...] オプションを付けて、ジェネレータが用意するアクションに合わせてルートを絞り込んだり、
  • あるいはそもそも不要な resources 定義そのものを削除したり、

といった形で、「コントローラが使うアクションだけのルート」を生成するようにしています。

テストの変更

railties/test/generators/authentication_generator_test.rb では、認証ジェネレータ実行後に生成される config/routes.rb の内容を検証するテストが更新されています。主な確認内容は:

  • 不要な RESTful ルートが生成されないこと
  • 必要なルート(例: new_session, session, destroy_session 相当)が正しく生成されていること

テスト行数が少し増えているので、具体的にはマッチさせるルーティングのパターン(assert_file "config/routes.rb" 内の期待値)が変更・追加されたと考えられます。


  1. 影響範囲・注意点
  • 対象は 新しく認証ジェネレータを実行した場合の routes.rb の内容 です。既に生成・編集済みの config/routes.rb には、自動では影響しません。
  • これまで「ジェネレータが勝手に作っていた余分なルート」に依存していたコードは、おそらく存在しない前提ですが、もしカスタマイズした上でそれらを利用していた場合、新規生成したプロジェクトではルートが足りなくなる可能性があります。
    • その場合は、手動で resources に戻すか、必要なアクションを only: / member / collection で追加してください。
  • 不要ルートが消えることで、以下のメリットがあります:
    • ルーティングの衝突(他のリソースの index/show など)リスクの軽減
    • rails routes 出力のノイズ削減
    • 認証に関する “公式な” エンドポイントの意図がより明確になる

  1. 参考情報 (あれば)

#56442 Pin Minitest to version 5 on 7-2-stable

マージ日: 2025/12/23 | 作成者: @yahonda

  1. 概要 (1-2文で)
    Rails 7.2 系(7-2-stable ブランチ)で利用する Minitest のバージョンを「5 系」に固定し、Minitest 6 への自動アップデートを防ぐ変更です。理由は、Minitest 6 が Ruby 3.2 以上を要求するのに対し、Rails 7.2 は Ruby 3.1 をサポート対象としているためです。

  1. 変更内容の詳細
  • 変更ファイル: activesupport/activesupport.gemspec
  • 変更点は 1 行のみで、依存関係として指定している Minitest のバージョン条件が変更されています。

イメージとしては、以下のような変更です(実際の記述はブランチ上で多少異なる可能性がありますが、意味としてはこういう変更です):

ruby
# 変更前(例)
spec.add_development_dependency "minitest"

# 変更後(例)
spec.add_development_dependency "minitest", "~> 5.0"

ポイント:

  • 「Minitest 5 系にピン留め(pin)」することで、bundle update 等を行っても Minitest 6 には上がらないようにしています。
  • Rails 7.2.x はすでに「セキュリティメンテナンスのみ」のフェーズに入っており、大きな互換性変更を入れない方針であるため、このような保守的な対応になっています。

  1. 影響範囲・注意点
  • 対象:

    • Rails 7.2 系(7-2-stable ブランチ)で開発・テストを行う環境
    • 特に Rails 本体やアプリケーションのテストで Minitest を利用している場合
  • 影響:

    • Rails 7.2.x を使っていても、bundle update により Minitest 6 が入って Ruby 3.2 要求に引っかかる、といった問題が起きにくくなります。
    • Minitest 6 の新機能や仕様変更を Rails 7.2.x 上で使うことはできません(Minitest 5 で動く範囲に制限されます)。
  • 注意点:

    • Rails 7.2.x アプリを Ruby 3.1 で運用している場合は、この変更によりテストが動かなくなることは基本的にありません(むしろ、Minitest 6 への意図しない更新で落ちるリスクを下げる変更です)。
    • Ruby 3.2 以上を使っていて「Minitest 6 を積極的に使いたい」場合でも、7-2-stable では Rails 側が 5 系を前提としているため、バージョンを上げると互換性の問題が出る可能性があります。その場合は Rails 7.2 ではなく、より新しい Rails バージョンへのアップグレードを検討した方がよいです。

  1. 参考情報 (あれば)

#56440 Fix live_streaming_excluded_keys docs [ci skip]

マージ日: 2025/12/23 | 作成者: @zzak

  1. 概要 (1-2文で)
    ActionController::Livelive_streaming_excluded_keys に関するドキュメントの誤りが修正されました。実装に合わせて説明が正しくなるように調整しただけで、挙動そのものの変更はありません。

  1. 変更内容の詳細

※PR本文から読み取れる範囲では「コードの挙動変更」ではなく「ドキュメントの修正」が中心です。変更ファイルは以下の2つです。

  • actionpack/CHANGELOG.md
  • actionpack/lib/action_controller/metal/live.rb

live_streaming_excluded_keys は、ActionController::Live を使ったライブストリーミング時に Rack 環境(request.env)からコピーしないキーを定義するための設定です。
PRタイトルが示す通り、「どのキーが除外されるのか」「どういう目的で使うのか」といったドキュメント上の説明が、実際のコードと合っていなかった部分が修正されています。

典型的には、以下のような形でクラス毎に上書きできる設定です:

ruby
class StreamsController < ActionController::Base
  include ActionController::Live

  self.live_streaming_excluded_keys += %w[
    some_custom_header
  ]

  def show
    response.headers['Content-Type'] = 'text/event-stream'
    10.times do |i|
      response.stream.write "data: #{i}\n\n"
      sleep 1
    end
  ensure
    response.stream.close
  end
end

この PR によって、上記のような使い方をするときに参照するコメント・ドキュメントの説明が、実装されている実際の挙動と一致するようになっています。
具体的な差分は +3/-3 行程度で、live_streaming_excluded_keys の意味やデフォルト値についての説明テキストが修正されたと考えられます。


  1. 影響範囲・注意点
  • ランタイムの挙動変更はなし

    • live_streaming_excluded_keys の実際の値や、その適用ロジックは変わっていません。
    • 既存アプリケーションの動作に影響はありません。
  • ドキュメントに依存していた場合の「認識ズレ」が解消

    • これまでドキュメントを読んで設定していた開発者は、「仕様だと思っていた内容」が実装と食い違っている可能性がありましたが、それが修正されます。
    • ライブストリーミング時に「コピーされないはず」「コピーされるはず」と認識していた環境変数・ヘッダキーの扱いを、あらためて実装ベースで確認しておくと安心です。
  • 将来的な追従がしやすくなる

    • ドキュメントとコードが揃ったことで、live_streaming_excluded_keys をカスタマイズするときの判断材料が正しくなり、保守性が向上します。

  1. 参考情報 (あれば)

#56434 [8-1-stable] Minitest 6 support

マージ日: 2025/12/23 | 作成者: @zzak

  1. 概要 (1–2文で)
    このPRは、Rails 8.1 系 (8-1-stable ブランチ) に Minitest 6 対応をバックポートしたものです。Minitest 6 での非互換・仕様変更に追従しつつ、rails test や ActiveSupport::TestCase まわりの動作が壊れないように調整しています。

  1. 変更内容の詳細

※元 PR (#56207, #56385, #56202 の一部) を 8.1 に持ってきたものなので、「Minitest 6 対応の本体」を 8.1 用に再適用している、という位置付けです。

2-1. Gemfile / Gemfile.lock: Minitest 6 系への更新

  • Gemfile での minitest バージョン制約を更新し、Minitest 6 系を利用するように変更
  • Gemfile.lock もそれに合わせて依存関係が更新・調整されています(+6/-11)。

これにより Rails 自体のテスト実行環境が Minitest 6 を前提としたものになります。


2-2. ActiveSupport::TestCase 関連

対象ファイル:

  • activesupport/lib/active_support/test_case.rb
  • activesupport/lib/active_support/testing/assertions.rb
  • activesupport/lib/active_support/testing/autorun.rb
  • activesupport/lib/active_support/testing/parallelization/worker.rb
  • activesupport/test/test_case_test.rb

Minitest 6 での変更点に合わせて、Rails 独自のテスト拡張を調整しています。

主なポイント:

a. ActiveSupport::TestCase の互換層・拡張

ActiveSupport::TestCase が内部で継承している Minitest::Test 側の API 変更に追従するためのコードが追加されています。
典型的には以下のような点が調整対象になります:

  • ライフサイクルフック (before_setup, after_teardown など) の呼び出し順や定義位置
  • Minitest 側の非推奨 API に依存していた場合の差し替え

PR diff から見ると:

ruby
# activesupport/lib/active_support/test_case.rb (抜粋イメージ)
class ActiveSupport::TestCase < Minitest::Test
  # Minitest 6 で必要になる初期化やフックの調整を行うコードが
  # 数行追加されている
end

実際には、Minitest 6 の仕様に合わせて、

  • テスト名の扱い
  • ランナーとの連携
    などの細かい部分を修正している可能性があります。

b. ActiveSupport::Testing::Assertions

ここには Rails 独自の assertion 群が定義されており、Minitest 6 での挙動との整合性をとる修正が入っています。

  • 例: Minitest のコア assertion と名前が衝突する/振る舞いが変わる箇所をラップし直す
  • assert_nothing_raised 的な、Minitest 本体には存在しないが Rails テストではよく使う assertion の扱いなど

行数的には (+5/-3) なので、主に以下のような内容が想定されます:

ruby
module ActiveSupport::Testing::Assertions
  # Minitest 6 での失敗オブジェクト/メッセージ仕様に合わせて、
  # 内部で使う assertion 呼び出しやブロックの扱いを微調整
end

c. autorun と並列実行ワーカー

  • activesupport/lib/active_support/testing/autorun.rb (+5)
  • activesupport/lib/active_support/testing/parallelization/worker.rb (+4/-2)

Minitest の autorun まわり・並列実行 API に仕様変更があるため、それに合わせて:

  • Minitest.run の呼び出し方や戻り値の扱い
  • フォーク / プロセス間通信まわりで Minitest から提供されるメソッド名や引数の変化

などを吸収するコードが入っています。


2-3. Railties: rails test 系の統合

対象ファイル:

  • railties/lib/minitest/rails_plugin.rb
  • railties/lib/rails/test_unit/line_filtering.rb

a. minitest/rails_plugin.rb

rails test コマンドの挙動を定義しているプラグイン部分が、Minitest 6 用にアップデートされています (+7)。

典型的には:

  • Minitest::Runnable.runnables の扱い方
  • Minitest.load_plugins / Minitest.run の呼び出し順序
  • Rails 特有のオプション(--verbose, --fail-fast, --backtrace など)の Minitest オプションへのマッピング

などを、Minitest 6 の新仕様に合わせて調整しています。

b. Rails::TestUnit::LineFiltering

railties/lib/rails/test_unit/line_filtering.rb (+24/-3) は変更量が大きめです。
これは「ファイル:行番号指定で特定のテストだけを実行する」機能 (rails test test/models/user_test.rb:42 のようなもの) を実現している部分です。

Minitest 6 ではテストメソッド名やテストスイートの内部表現が変わったり、
フィルタリング機構 (Minitest::Test.runnable_methods など) が変化しているため、Rails 側フィルタ実装を大きめに書き換えていると考えられます。

想定される実装イメージ:

ruby
module Rails::TestUnit::LineFiltering
  # 対象ファイルの AST / ソースコードから、行番号に対応する
  # テストメソッド名を推論し、Minitest 側のフィルタに渡せる
  # 条件 (正規表現など) に変換する処理が変更されている。
end

Minitest 6 の Minitest::Test#nameMinitest::Runnable#filtered? などの仕様に合わせてフィルタ条件を構築し直している可能性が高いです。


2-4. テストコード・ガイドのテストヘルパ

対象ファイル:

  • actionpack/test/dispatch/routing/route_set_test.rb (+2/-2)
  • activesupport/test/test_case_test.rb (+2/-2)
  • guides/test/test_helper.rb (+10)

これらは主に「Minitest 6 下でもテストが通るように調整する」ための修正です。

例:

  • Minitest 6 で警告やエラーになった API の利用をやめ、推奨 API に切り替える
  • ガイド用の test_helper.rb で Minitest 6 前提の require・設定を追加

guides/test/test_helper.rb (+10) は、ガイド内サンプルテストも Minitest 6 で問題なく動くようにする設定追加(require "minitest/autorun", Rails を読み込む順序の調整、並列化設定など)が主と考えられます。


  1. 影響範囲・注意点

a. Rails 8.1 + Minitest を使うプロジェクト全般

  • この PR により、Rails 8.1 系は Minitest 6 を使うことが前提になります(Gemfile の制約に依存しますが、公式のテスト環境は Minitest 6 対応で固められます)。
  • 既存のテストが「Minitest 5 依存の挙動」に頼っていた場合、Minitest 6 による影響が出る可能性があります。
    • 例:
      • 非推奨だった古い assertion / ランナー API
      • テストメソッド名の解釈の違い
      • 並列実行の挙動の微妙な違い

b. rails test のオプションや挙動の細かい変化

rails test コマンド実装まわり(minitest/rails_plugin.rb, line_filtering.rb)に変更が入っているため:

  • rails test test/models/user_test.rb:10 のような「行番号指定実行」の解釈が、Minitest 6 に揃えられる形で多少変わる可能性があります。
  • フィルタ指定を組み合わせて使っている場合(-n, TESTOPTS 環境変数など)、内部の実装変更による影響を受けるケースがあります。

c. 並列テスト実行を有効にしている場合

  • ActiveSupport::Testing::Parallelization のワーカー側コードが Minitest 6 用に修正されているため、並列実行の挙動に関するバグフィックス・挙動変更が含まれている可能性があります。
  • 自前で Minitest::Parallel などを直接叩いている場合は、Rails の parallelization 層との併用に注意が必要です。

d. ランナー / カスタムプラグインを書いている場合

  • Minitest の API 変更 + Rails 側ラッパの更新の両方の影響を受けます。
  • もし Rails アプリ内で Minitest::Runnable を直接いじるような高度なカスタマイズをしている場合は、Minitest 6 の CHANGELOG と本 PR 周辺の実装を一度確認するのが無難です。

  1. 参考情報 (あれば)
  • 元 PR:
    • #56207, #56385, #56202
      → 本 PR はこれらの「Minitest 6 サポート」関連の変更を 8.1 用にバックポートしたものです(ただし #56202 のコミット c5c678a6... は除外)。
  • Minitest 6 の仕様変更
    • 実際に影響を知りたい場合は Minitest のリリースノート / CHANGELOG を参照してください。
    • 特に Minitest::Test のライフサイクル、autorun, 並列実行、フィルタリングまわりが関係します。
  • Rails 側のテスト関連コード:
    • ActiveSupport::TestCase
    • ActiveSupport::Testing::*
    • Rails::TestUnit::*
    • railties/lib/minitest/rails_plugin.rb

これらを読むと、Minitest 6 環境下で Rails がどのようにテストランナーをラップしているかを把握できます。


#56435 [8-0-stable] Minitest 6 support

マージ日: 2025/12/23 | 作成者: @zzak

  1. 概要 (1–2文で)
    Rails 8.0-stable ブランチに対して、Minitest 6 への対応を行うバックポート PRです。Gemfile の依存関係更新に加え、Minitest 6 での非互換点(autorun、並列実行、ラインフィルタなど)に追随するためのテスト基盤コードの修正が含まれます。

  1. 変更内容の詳細

2-1. Gemfile / Gemfile.lock: Minitest 6 への更新

  • GemfileGemfile.lock で Minitest のバージョンが 6 系に更新。
  • それに伴う依存解決の変更により、lock ファイルの差分(+6/-11)が発生。

目的:

  • Rails 8.0 系でも Minitest 6 を公式にサポートし、最新版の Minitest 環境でテストが動作するようにする。

2-2. ActiveSupport::TestCase 周辺の対応

対象ファイル:

  • activesupport/lib/active_support/test_case.rb
  • activesupport/lib/active_support/testing/assertions.rb
  • activesupport/lib/active_support/testing/autorun.rb
  • activesupport/lib/active_support/testing/parallelization/worker.rb
  • activesupport/test/test_case_test.rb

主なポイント:

a. ActiveSupport::TestCase に Minitest 6 対応の設定を追加

ActiveSupport::TestCase 自体が Minitest::Test を拡張する形で、Minitest 6 の API/挙動に合わせた初期化・設定が追加されています。

例(イメージ):

ruby
module ActiveSupport
  class TestCase < Minitest::Test
    # Minitest 6 用のフックや設定
  end
end

b. assertions.rb のアサーションを Minitest 6 に対応

  • Minitest 6 で変更・非推奨になった API を避ける/ラップする方向の修正。
  • 例えば assert_raises / assert_raises_with_message 相当や deprecation 周りで、Minitest 本体に依存していた部分を、Minitest 6 のインターフェースで動くよう微調整。

c. autorun.rb の修正

  • Minitest 6 の Minitest.run / autorun の仕様変更に対応。
  • Rails 側で提供している rake test / bin/rails test などからの起動方法と、Minitest 6 の自動実行ロジックが衝突しないように調整。

イメージ:

ruby
# activesupport/lib/active_support/testing/autorun.rb
require "minitest/autorun"

Minitest.autorun # or Rails 用のラッパーをかませる

d. 並列実行 worker (parallelization/worker.rb) の修正

  • Minitest 6 における並列実行まわりの変更に対応するため、fork/DRb などを使った worker プロセスの起動・終了、結果収集の処理を微調整。
  • Minitest の内部 API に直接依存していた箇所を、Minitest 6 の想定 API に沿うように修正。

e. test_case_test.rb の更新

  • 上記の変更に伴い、ActiveSupport::TestCase の振る舞いを検証するテストを Minitest 6 向けに調整。
  • Minitest 6 で変わるメソッド名・戻り値・フックタイミングなどを前提とした期待値に変更。

2-3. ActionPack の routing テストの微修正

対象ファイル:

  • actionpack/test/dispatch/routing/route_set_test.rb

内容:

  • Minitest 6 によるエラーメッセージ形式やアサーションの動作変更に合わせて、ルーティング関連テストをわずかに修正(+2/-2)。
  • 例えば、assert_raises のメッセージ検証方法や、assert_equal の expected / actual 引数順などのスタイルを Minitest 6 に揃えた可能性があります。

2-4. Guides テストヘルパーのアップデート

対象ファイル:

  • guides/test/test_helper.rb

内容:

  • ガイド用のテスト(ドキュメント内サンプルの検証など)で使用する test_helper に、Minitest 6 に対応した設定を追加(+10)。
  • require "minitest/autorun" の扱い、ActiveSupport::TestCase の読み込み、ランナーのセットアップなどを最新形に合わせていると考えられます。

2-5. Rails 側の Minitest プラグイン・ラインフィルタ機能の対応

対象ファイル:

  • railties/lib/minitest/rails_plugin.rb
  • railties/lib/rails/test_unit/line_filtering.rb

a. minitest/rails_plugin.rb

  • Rails の bin/rails test 実行時に読み込まれる Minitest プラグインを、Minitest 6 互換に。
  • テストランナーのオプション処理や、Rails 独自の拡張 (--verbose, --fail-fast, --seed など) が Minitest 6 のオプションパーサと整合するように修正。

b. ラインフィルタ (line_filtering.rb) の大幅修正(+24/-3)

bin/rails test test/models/user_test.rb:42 のように、ファイル名 + 行番号で特定のテストだけを実行する機能に関連します。

Minitest 6 対応として:

  • テスト定義位置の取得ロジック(method(source_location) ベースなど)を Minitest 6 に適合。
  • ライン番号指定から該当テストメソッドを特定するアルゴリズムを Minitest 6 のテストクラス/メソッドの表現に合わせて更新。
  • 将来の Minitest 6 の内部仕様変更にも耐えられるよう、Rails 側で責務を持って解決するコードが増えている可能性があります。

疑似イメージ:

ruby
def tests_for_line_filter(file, line)
  Minitest::Runnable.runnables.flat_map do |klass|
    klass.runnable_methods.select do |method_name|
      location_file, location_line = klass.instance_method(method_name).source_location
      location_file == file && location_line <= line
    end
  end
end

  1. 影響範囲・注意点

  2. テスト環境の Minitest バージョン

    • Rails 8.0-stable を使うプロジェクトは、基本的に Minitest 6 を前提とすることになります。
    • Gemfile で古い Minitest を固定している場合は、バージョン競合や非互換が起きる可能性があります。
  3. 独自 Minitest 拡張の互換性

    • プロジェクト側で独自の Minitest プラグインや monkey patch(Minitest::Test 拡張、Minitest.autorun のラップなど)を持っている場合、それらが Minitest 6 の API 変更と衝突しないか要確認です。
    • 特に、autorun 周り・カスタムランナー・並列実行・Minitest::Runnable まわりを直接いじっている場合は注意が必要です。
  4. 行番号指定実行 (bin/rails test ...:line) の挙動変化

    • 行フィルタの実装がかなり書き換えられているため、
      • 以前は動かなかったパターンが動くようになる
      • 逆に、非常に特殊なケースで挙動が変わる
        可能性があります。
    • CI や開発フローで行指定実行を多用している場合は、一度挙動を確認するのが安全です。
  5. テスト出力・エラー表示の細かい変化

    • Minitest 6 のエラーメッセージフォーマットや集計結果表示が従来と微妙に変わることがあります。
    • テスト結果の文字列出力をパースしているようなツール(独自のレポーター、CI 連携スクリプトなど)は影響を受ける可能性があります。

  1. 参考情報 (あれば)

#56439 [ci skip] Clarify behavior of monday and sunday methods in guides

マージ日: 2025/12/23 | 作成者: @Yuhi-Sato

  1. 概要 (1-2文で)
    このPRは、Railsガイドの Date/ActiveSupport 拡張に関するドキュメントを修正し、mondaysunday メソッドの挙動をより正確に説明したものです。コードの挙動自体は変えず、「常に前の月曜日/次の日曜日」ではなく「同じ日付を返す場合がある」ことを明示しました。

  1. 変更内容の詳細 (サンプルコード含む)

修正対象

  • guides/source/active_support_core_extensions.md
    Date(あるいは ActiveSupport::TimeWithZone など)に追加される monday, sunday メソッドの説明文が修正されています。

挙動の明確化

PRの説明にある通り:

monday and sunday methods in Date class return the same day when called on a Monday or Sunday respectively, not strictly the previous Monday or next Sunday.

つまり、ドキュメント上のイメージが以下のような「常に過去/未来の特定曜日を返すメソッド」になっていたのに対して、それを「現在の日付がその曜日なら、そのまま同じ日を返す」という実際の挙動に合わせて説明し直しています。

実際の挙動のイメージ

ruby
require "active_support/all"

d1 = Date.new(2025, 1, 13) # 月曜日
d1.monday  # => 2025-01-13 (同じ日付を返す)

d2 = Date.new(2025, 1, 14) # 火曜日
d2.monday  # => 2025-01-13 (直近の月曜日)

d3 = Date.new(2025, 1, 19) # 日曜日
d3.sunday  # => 2025-01-19 (同じ日付)

d4 = Date.new(2025, 1, 18) # 土曜日
d4.sunday  # => 2025-01-19 (直近の次の日曜日)

ガイド上では、これまで「前の月曜日」「次の日曜日」というような表現や、例示コードのコメントが誤解を生む形になっていた可能性が高く、それを「呼び出し元の日付がすでに該当する曜日なら、その日自身を返す」という文言に修正したと考えられます。

変更行数は +2/-2 のため、具体的には:

  • 「previous Monday」「next Sunday」など**「必ず前/次」だと読める表現**を削除/修正
  • 「when it is already Monday/Sunday, it returns the same date」のような自己返しのケースを説明に追加

といった小さな文言修正であると推測できます。


  1. 影響範囲・注意点
  • コードへの影響はなし
    変更はガイド文書 (active_support_core_extensions.md) のみで、実装・APIは一切変更されていません。既存アプリの挙動が変わることはありません。

  • 認識のズレが解消される
    ドキュメントを読んで「monday は常に“1週間以内の過去の月曜日”を返す」と思い込んでいた場合、実運用では「すでに月曜日なら日付が変わらない」ことに驚く可能性がありました。このPRにより、ガイドベースで設計・実装している開発者との認識ズレが減ります。

  • 「常に前/次を取りたい」場合の注意
    もし以下のような意図がある場合:

    • 「常に現在より過去の月曜日を取りたい」
    • 「常に現在より未来の日曜日を取りたい」

    そのまま monday / sunday を使うと「すでにその曜日のときに日付が進まない/戻らない」ので注意が必要です。例えば:

    ruby
    # 「今日より前の月曜日」に限定したい場合
    today = Date.current
    prev_monday =
      if today.monday?
        today - 7  # 1週間前
      else
        today.monday  # 直近の月曜日(常に過去になる)
      end
    
    # 「今日より後ろの日曜日」に限定したい場合
    next_sunday =
      if Date.current.sunday?
        Date.current + 7
      else
        Date.current.sunday
      end

    この仕様をガイドに明記したことで、このようなロジックを組む際の設計判断がしやすくなります。


  1. 参考情報 (あれば)

#56429 Fixes for default handling in CombinedConfiguration

マージ日: 2025/12/23 | 作成者: @jordan-brough

  1. 概要 (1-2文で)
    CombinedConfiguration まわりの「default」値の扱いに関するバグ修正です。env_configurationencrypted_configuration の両方で「遅延評価」と「false を正しく扱う」ように挙動が統一されました。

  1. 変更内容の詳細

背景

CombinedConfiguration は、複数の設定ソース(例: 環境変数 + 暗号化設定ファイル)をまとめて扱うための仕組みです。その中で default: オプション(値が存在しない場合に使うデフォルト値)が正しく扱われていないケースがあり、それを修正しています。

1. env_configuration.rb の default を「遅延評価」に変更

以前:

  • default: に Proc など「呼び出し可能なオブジェクト」を渡しても、それが「そのままの値」として扱われる、あるいは eager に評価される可能性があり、encrypted_configuration と挙動が揃っていなかった。

今回:

  • encrypted_configuration と同様に、「値が存在しなかった場合にのみ default を呼び出す」形に修正。
  • default が Proc 等の場合は「遅延評価」され、実際に必要になるまで実行されない。

イメージ(擬似コード):

ruby
# 環境変数から設定を読む EnvConfiguration を経由して…
config = AppConfig.some_key(default: -> { heavy_computation })

# some_key が環境変数に存在しない場合にだけ heavy_computation が実行される

これにより、コストの高い計算や I/O を default に仕込んでも、不要なときには実行されなくなります。

2. env_configuration.rb の default: false の扱いを修正

以前:

  • default: false を指定しても、値が存在しない場合に nil が返っていた。

今回:

  • default: false を指定したら、値が存在しないときには正しく false が返るように修正。

例:

ruby
# 以前の挙動(バグ)
value = config.fetch(:FEATURE_ENABLED, default: false)
# 環境変数 FEATURE_ENABLED が無い場合 → value は nil になっていた

# 修正後
value = config.fetch(:FEATURE_ENABLED, default: false)
# 環境変数が無い場合 → value は false

boolean フラグ用途で default: false を使っていた場合、これまで nil が返っていたのが false になるため、条件分岐での挙動が正しくなります。

3. encrypted_configuration.rb の default コールバックが false を返す場合の修正

以前:

  • default: に Proc を渡し、その Proc が false を返した場合に、返り値として false ではなく「Proc オブジェクト自体」が返ってしまうバグがあった。

今回:

  • default コールバック(Proc 等)の戻り値が false の場合でも、その false 自体が正しく設定値として返されるように修正。

例:

ruby
config.read(:secret_key_base, default: -> { false })
# 以前の挙動(バグ)
# => #<Proc: ...> が返ることがあった

# 修正後
# => false が返る

テストの追加・更新

  • encrypted_configuration_test.rb
    • default コールバックが false を返すケースのテストが追加/修正。
  • env_configuration_test.rb
    • default: falsefalse を返すこと
    • default の遅延評価(必要なときのみ呼び出されること) を確認するテストが追加。

  1. 影響範囲・注意点
  • 対象:

    • ActiveSupport::EnvConfiguration 経由で default: オプションを使っている箇所
    • ActiveSupport::EncryptedConfigurationdefault: に Proc(コールバック)を渡している箇所
  • 互換性上のポイント:

    • 以前のバグを前提にしたコードがあると挙動が変わる可能性があります。
      • default: false を指定していて「nil が返る」ことを前提にしていた場合、今後は false が返るようになる。
      • default コールバックが false を返したときに「Proc 自体が返る」ことを利用していた(通常はありえないが)場合、今後は false が返る。
    • いずれも「元々の設計意図(boolean を正しく扱う、遅延評価する)」に沿った修正であり、一般的なコードにとってはむしろバグ修正としてプラスに働きます。
  • パフォーマンス:

    • default の評価が遅延されることで、重い計算や I/O を default に書いている場合、実際に必要な場合にのみ実行されるようになり、無駄な処理が減ります。

  1. 参考情報 (あれば)

#56431 Add "Missing key: ..." to KeyError message in EncryptedConfiguration

マージ日: 2025/12/22 | 作成者: @jordan-brough

  1. 概要 (1-2文で)
    EncryptedConfiguration で KeyError を発生させる際のエラーメッセージに、「Missing key: ...」という具体的なキー名を含めるようにした PR です。既に CombinedConfiguration で行っている挙動と揃えるための、開発者向けの利便性改善です。

  1. 変更内容の詳細

EncryptedConfiguration の KeyError メッセージ改善

ActiveSupport::EncryptedConfiguration 内で設定値を取得するとき、存在しないキーを参照すると KeyError が発生しますが、そのメッセージを以下のように変更しています。

  • 変更前(イメージ)
    text
    key not found: :some_missing_key
  • 変更後(イメージ)
    text
    Missing key: :some_missing_key

実際には、CombinedConfiguration がすでに以下のような形でメッセージを組み立てていたため、EncryptedConfiguration 側もそれに合わせた形です。

ruby
# CombinedConfiguration 側のイメージ
raise KeyError, "Missing key: #{key.inspect}"

この PR では、encrypted_configuration.rb 内の KeyError 発生箇所を同様のメッセージフォーマットに変更しています(1行の差分)。

テストの追加・修正

2つのテストファイルが更新されています。

  • activesupport/test/combined_configuration_test.rb
    • KeyError 発生時のメッセージに "Missing key: ..." が含まれることを検証するテストを追加(+12 行)。
  • activesupport/test/encrypted_configuration_test.rb
    • EncryptedConfiguration 側でも "Missing key: ..." 形式のメッセージになることを検証するようにテストを修正(+3/-3)。

これにより、CombinedConfiguration と EncryptedConfiguration の挙動・メッセージフォーマットが揃い、かつ回 regress しにくくなっています。


  1. 影響範囲・注意点
  • 影響範囲

    • EncryptedConfiguration(例: config/credentials.yml.enc など)から設定値を取得する際、存在しないキーを参照したときの KeyError メッセージが変わります。
    • CombinedConfiguration はすでに同じ形式なので、新たな挙動変更はほぼ EncryptedConfiguration のみです。
  • 互換性

    • 発生する例外の種類は引き続き KeyError であり、アプリケーションコードの rescue ロジックに影響はありません。
    • ただし、エラーメッセージの文字列に依存しているテストやロジックerror.message を文字列比較しているなど)がある場合は、メッセージ変更により失敗する可能性があります。
  • 開発者体験

    • どのキーが不足しているのかがメッセージに明示されるため、設定ミス(typo や環境ごとの差分)を特定しやすくなります。

  1. 参考情報 (あれば)
  • 本 PR:
    • Add "Missing key: ..." to KeyError message in EncryptedConfiguration (#56431)
  • 関連 PR:
    • CombinedConfiguration を導入した/変更した PR: https://github.com/rails/rails/pull/56404
      → こちらで KeyError メッセージに "Missing key: ..." を入れる仕様が導入されており、本 PRはその仕様を EncryptedConfiguration にも適用したものです。

#56432 Fix assert_equal invocations in bug report templates

マージ日: 2025/12/22 | 作成者: @callmesangio

  1. 概要 (1-2文で)
    Rails のバグ報告テンプレート内で使われている assert_equal の引数の順番ミス(actual, expected)を、正しい順番(expected, actual)に修正した PR です。テストコードの書き方のサンプルとして配布されているテンプレートの品質を整えるための、ドキュメント/ガイド系の小さな修正です。

  1. 変更内容の詳細

対象ファイルは以下の2つです。

  • guides/bug_report_templates/action_controller.rb
  • guides/bug_report_templates/action_view.rb

これらはいずれも「バグ報告用テンプレート」であり、利用者が Rails のバグを再現するミニマルアプリ/スクリプトを書く際の雛形になっています。

Rails の assert_equal(Minitest ベース)の正しいシグネチャは:

ruby
assert_equal(expected, actual, failure_message = nil)

ですが、テンプレート内で一部の呼び出しが逆順になっていました:

ruby
# 修正前(例)
assert_equal actual_value, expected_value

これを以下のように修正しています:

ruby
# 修正後(正しい順序)
assert_equal expected_value, actual_value

※PR 本文には具体的な行の実例はありませんが、「bug report templates における assert_equal 呼び出しの引数順を expected, actual に揃えた」という趣旨なので、上記のような変更が 2 ファイルで 1 箇所ずつ行われています。

この修正により、テンプレートに従って書かれたバグ報告用コードも、Minitest/Rails の慣習に沿った読みやすいテストになります。特に assert_equal の失敗メッセージは expected / actual を前提に出力されるため、順番が逆だとログの意味が分かりにくくなる問題を避けられます。


  1. 影響範囲・注意点
  • 影響範囲

    • 変更は ガイド用テンプレートのみ であり、Rails 本体の挙動やテストランナーの実装には一切影響しません。
    • このテンプレートを元に新規でバグ報告用コードを書くユーザーに対して、より正しいテスト記述例が提供されるようになります。
  • 注意点

    • すでにテンプレートをコピペして使っている既存のバグ報告スクリプトは自動的には修正されません。もしテンプレート由来の assert_equal(actual, expected) 記述があれば、手動で assert_equal(expected, actual) に直すと、失敗メッセージの意味が分かりやすくなります。
    • 新たに Rails のバグ報告テンプレートを参考にする際は、「assert_equal(expected, actual) が正」と覚えておくと、Minitest や他の Rails ガイドと一貫したスタイルになります。

  1. 参考情報 (あれば)
  • Minitest / Rails の assert_equal の一般的な使い方:
    ruby
    assert_equal 42, result        # 42 が期待値、result が実際の値
    assert_equal "OK", response.body
  • 期待値と実値を逆にすると、失敗時のメッセージが直感と逆になるため、デバッグ時に混乱を招きやすくなります(例: "Expected 1, not 2" のように、どちらが「本当に欲しかった値」か判断しづらい)。
    この PR はその種の混乱をテンプレートレベルで防ぐための修正といえます。

#56423 Fix link to jemalloc repo [ci skip]

マージ日: 2025/12/22 | 作成者: @andreimaxim

  1. 概要 (1-2文で)
    Railsガイド「tuning_performance_for_deployment.md」に記載されていた jemalloc のリンク先が、アーカイブされた旧リポジトリを指していた問題を修正し、現行の正しいリポジトリへのリンクに差し替えたPRです。ドキュメントのみの更新で、Rails本体のコードや挙動には変更はありません。

  2. 変更内容の詳細

  • 対象ファイル: guides/source/tuning_performance_for_deployment.md
  • 変更内容は 1 行だけで、「jemalloc への参照リンク」の URL を修正するものです。

ガイドでは、アプリケーションのパフォーマンスチューニング/デプロイ時の最適化において jemalloc を利用することが推奨されており、その際に参照すべき jemalloc プロジェクトの GitHub リポジトリURLが掲載されています。
元々は「アーカイブ済みの旧リポジトリ」を指していたため、読者が「もうメンテされていないのでは?」と誤解する可能性がありました。
このPRでは、そのリンクを「現在メンテナンスされている(または正しい公式の)jemalloc リポジトリ」へ更新しています。

※実際の差分イメージ(URLのみ変更・擬似例):

diff
- 詳細は jemalloc のリポジトリ https://github.com/old-archived/jemalloc を参照してください。
+ 詳細は jemalloc のリポジトリ https://github.com/jemalloc/jemalloc を参照してください。
  1. 影響範囲・注意点
  • 影響範囲:
    • Rails ガイドを読む開発者が、最新かつサポートされている jemalloc のリポジトリに直接アクセスできるようになります。
    • Rails 本体のコード、API、挙動、パフォーマンスには一切影響ありません(ドキュメントのみの変更)。
  • 注意点:
    • jemalloc の導入・設定手順そのものはこのPRでは変更されていないため、利用環境や OS ごとのビルド・導入方法は、リンク先の README / ドキュメントを改めて確認する必要があります。
    • すでに古いリンクをブックマーク等している場合は、今後は新しいリンクを参照することが推奨されます。
  1. 参考情報 (あれば)
  • 関連Issue: Fixes #56388
    • ガイドがアーカイブ済みリポジトリを指していることへの報告・改善要望に対応したものです。
  • jemalloc 公式リポジトリ(想定):

#56415 Ensure batched preloaded associations accounts for klass when grouping to avoid issues with STI

マージ日: 2025/12/21 | 作成者: @zzak

  1. 概要 (1-2文で)
  • ActiveRecord の関連プリロード(includes など)のバッチ処理において、STI や class_name を伴う関連で正しいクラスが使われない問題を修正し、グルーピング時に「レコードのクラス情報」も考慮するようにした PR です。
  • これにより、SpecialAuthor / SpecialBook のような STI / テーブル共有モデルを含む複雑な関連を includes したときでも、期待どおりのモデルクラスで関連オブジェクトがロードされます。

  1. 変更内容の詳細

何が問題だったか

再現スクリプトでは、以下のような構造になっています:

ruby
class Author < ActiveRecord::Base
  has_many :books
  belongs_to :origin_author,
             class_name: 'SpecialAuthor',
             foreign_key: :origin_author_id,
             optional: true
end

class SpecialAuthor < ActiveRecord::Base
  self.table_name = 'authors'
  has_many :books,
           class_name: 'SpecialBook',
           foreign_key: :author_id
end

class Book < ActiveRecord::Base
end

class SpecialBook < ActiveRecord::Base
  self.table_name = 'books'
end
  • AuthorSpecialAuthor は同じ authors テーブルを共有
  • BookSpecialBook は同じ books テーブルを共有
  • Author#origin_authorSpecialAuthor を返すべき関連
  • SpecialAuthor#booksSpecialBook を返すべき関連

期待される動作:

ruby
result = Author.includes(books: [], origin_author: { books: [] }).last
result.origin_author.books.map(&:class)
# => [SpecialBook] となるべき

しかし、バッチプリロードの実装上、関連レコードのグルーピング時に「クラス情報」が考慮されず、BookSpecialBook が混ざるようなケースで誤ったクラスでインスタンス化される/紐づけられる可能性がありました。

特に、

  • 同じテーブルを指す複数のモデル(STI や self.table_name = ...
  • class_name を指定した has_many / belongs_to
  • ネストした includesorigin_author: { books: [] } のような)

といった条件が揃うと、不正なクラスの関連オブジェクトが返ってしまう、というのが #56047 の問題です。

どこをどう直したか

主な修正箇所:
activerecord/lib/active_record/associations/preloader/batch.rb

Preloader::Batch は、複数のレコードに対する関連をまとめてプリロードするとき、内部で「どのクエリでどのレコード群を拾うか」をグルーピングしてバッチ化する役割のクラスです。

今回の修正では、このグルーピングキーに「klass(モデルクラス)」を含めるようにしています。

イメージとしては:

  • 変更前:
    • 「テーブル名・関連名・条件」などだけでまとめてしまい、Book 用と SpecialBook 用が同じグループになり得た
  • 変更後:
    • これらに加えて「対象となるモデルクラス」もキーに含めることで、
      • Book 用のバッチ
      • SpecialBook 用のバッチ
        が別々に扱われる

結果として、

ruby
Author.includes(books: [], origin_author: { books: [] }).last

のようなクエリでも、

  • Author#booksBook
  • SpecialAuthor#booksSpecialBook

が正しく区別されてプリロードされるようになります。

テストの追加・変更

変更されたテストファイル:

  • activerecord/test/cases/associations_test.rb
    • この PR で修正したケース(STI/テーブル共有+includes+ネストした関連)をカバーするテストが追加されています。
  • activerecord/test/models/author.rb
    • テスト用 Author モデルに、上記のような origin_authorSpecialAuthor 関連定義が追加されていると考えられます。
  • activerecord/test/schema/schema.rb
    • テストスキーマに、必要なカラム(例: origin_author_id)が追加。
  • activerecord/test/cases/json_serialization_test.rb
    • 関連プリロードの挙動が変わったことで JSON シリアライゼーションの期待値に影響が出る箇所を 1 行だけ調整しています(クラスや関連内容が変わったことによる微修正)。

  1. 影響範囲・注意点

影響範囲

  • 対象となるのは ActiveRecord の関連プリロード(特に includes / preload / eager_load を経由するプリローダ)のバッチ処理部分 です。
  • 主に影響するケース:
    • STI または self.table_name = ...同じテーブルを共有する複数モデル
    • class_name: オプションを指定した has_many / belongs_to
    • ネストした includes でこれらを組み合わせている場合

これらの組み合わせで「実際には不正なクラスが返っていた」場合は、それが修正されます。

互換性上の注意

  • この変更は本来の期待動作(class_name などに従って正しいモデルクラスを返す)に沿う修正です。
  • しかし、もし既存コードが「誤っていた以前の挙動」に依存していた場合(例: SpecialBook ではなく Book が返ってくる前提のコード)、
    • 返るオブジェクトのクラスが変わることで、型チェックやポリモルフィックな処理に影響が出る可能性があります。
  • 特に以下を行っているプロジェクトは、アップデート後の動作確認を推奨します:
    • STI/テーブル共有モデルで複雑な includes を多用している
    • result.association.target.map(&:class) のようにクラスを前提としたテストを書いている

パフォーマンス面では、グルーピングキーに klass 情報が増えただけなので、大きな劣化は想定されませんが、バッチがより細かく分かれることでクエリ回数が「厳密には」変化するパターンはあり得ます(特にこれまで誤ってまとめられていたケース)。


  1. 参考情報 (あれば)

#56416 Fix rerun command on CI for railties

マージ日: 2025/12/21 | 作成者: @byroot

  1. 概要 (1-2文で)
    このPRは、Rails の railties において CI 上で rake test TESTOPTS="--rerun …" を利用した際の「失敗テストの再実行」機能が正しく動作するように修正したものです。テストレポーターと Rake タスクの連携を調整し、rerun 用のコマンドが壊れないようにしています。

  1. 変更内容の詳細

※実際の差分テキストは提示されていないので、ファイル名と行数から推測を交えて解説します。

2-1. railties/Rakefile の修正 (+4)

  • railties 用のテストタスク(おそらく test / test:units など)で、CI から実行されるときに --rerun 用の情報が正しく生成されるように、テストオプション (TESTOPTS) か環境変数の扱いが修正されています。
  • 実際には次のような処理が追加されている可能性が高いです:
    • Rails::TestUnit::Reporter が出力する「rerun コマンド」を壊さないよう、Rake 側でテストコマンドの引数を組み立てるロジックを調整
    • Bundler 経由 (bundle exec) や engine/railties ディレクトリ固有のパスなど、CI 環境特有の実行パスを考慮

たとえばイメージとしては、以下のような修正が入っていることが多いです(擬似コード):

ruby
# 例: test タスクの定義内
task :test => :test_prepare do
  ruby "test/unit", TESTOPTS: ENV["TESTOPTS"]
end

に対して、rerun コマンドが崩れないよう ENV["TESTOPTS"] の扱いを調整したり、CI 通常の実行コマンドを rerun 出力に反映するための変更が入った、という類の修正です。

2-2. railties/lib/rails/test_unit/reporter.rb の修正 (+1/-1)

  • 1 行のみの変更ですが、これは「失敗テストの再実行コマンド」生成ロジックのバグ修正である可能性が高いです。
    • たとえば:
      • コマンド中のパスや引数順序の誤り
      • CI 上では bin/test ではなく bundle exec ruby -Itest を使う必要がある、といった差異
      • --seed などのオプションを正しく引き継げていなかった問題
  • Reporter は、テスト実行後に「失敗したテストを再実行するにはこのコマンドを打て」というメッセージを出すクラスです。その中の rerun_command もしくはそれに類するメソッドの組み立てが、CI 用のパス/コマンドに対応するよう修正されています。

例としてよくある形:

ruby
# 変更前 (例)
"ruby #{file_path} -n #{test_name}"

# 変更後 (例)
"bundle exec ruby #{file_path} -n #{test_name}"

あるいは、railties のテストでは -Itest-Itest/lib が必要で、それが足りない/重複していた…といった微修正が入っているパターンです。

2-3. railties/test/test_unit/reporter_test.rb の修正 (+6)

  • 6 行の追加により、Reporter が生成する rerun コマンドが期待通りであることを検証するテストが強化・追加されています。
  • 想定されるテスト内容:
    • CI で使うコマンドライン(おそらく bundle exec rake test TESTOPTS="..." のような形)から、Reporter が出力する rerun コマンド文字列が正しい形式になっているかをチェック。
    • 特定のパスやオプション(-n "test_name"--seed 12345)が欠けていないこと、余計な環境依存情報が含まれないことなどを確認。

イメージとしては:

ruby
def test_rerun_command_on_ci
  reporter = Rails::TestUnit::Reporter.new(...)
  # 失敗テストを1個セットアップ
  assert_equal expected_command, reporter.rerun_command
end

のようなアサーションを増やしているはずです。


  1. 影響範囲・注意点
  • 影響範囲:
    • railties のテストを CI 上で実行しており、失敗テストの rerun コマンド(多くの場合、テスト結果の末尾に表示される)を使っている開発者/メンテナが対象です。
    • Rails 自体を開発・メンテしている人、もしくは Rails を vendor して同様の test reporter を使っている環境で影響します。
  • 期待される挙動の変化:
    • 以前は CI 環境で出力された rerun コマンドをそのまま実行しても動かなかった、あるいはファイルパスや実行コマンドが不正だったケースが、今回の修正によりそのままコピペで再実行できるようになります。
  • 注意点:
    • 独自に Rails::TestUnit::Reporter を継承・拡張している場合は、今回のコマンド生成ロジック変更と衝突しないか確認したほうがよいです。
    • Rakefile 側でテストタスクをオーバーライドしているアプリ/エンジンでは、この PR と同様のパターンを踏襲していないと、rerun が正しく動かないことがあります。

  1. 参考情報 (あれば)
  • Rails::TestUnit::Reporter は、Rails 標準の test/unit ラッパーで、ミニテストのレポートに Rails 独自の拡張を加えるコンポーネントです。rerun コマンド表示機能は、Rails 6 以降で改善されてきた部分で、今回の PR もその一環として CI 環境での実用性を高める修正と考えられます。
  • 類似の修正は、過去にも Active Record や Active Support のテストタスクで行われており、「CI で実行したときにローカルでそのまま再実行できるコマンドを出す」という方向性で揃えられています。

#56411 Fix code in comment example

マージ日: 2025/12/21 | 作成者: @morgoth

  1. 概要 (1-2文で)
    ActiveSupport::CombinedConfiguration のコメント内に書かれていたサンプルコードの条件式が、実際には到達しない(無意味な)|| を含んでいたため、正しい挙動を反映した形に修正されたドキュメント的変更です。実行コードの挙動そのものではなく、コメント中の例示コードの論理を整えるためのフォローアップ PR です。

  1. 変更内容の詳細

※ この PR は activesupport/lib/active_support/combined_configuration.rb のコメント中のサンプルコードのみを修正する小さな変更です。実行されるロジックには手を入れていません。

PR 説明の “The "||" would not be reached” から分かるように、コメント中の例では以下のような構造になっていたと考えられます(イメージ):

ruby
# 例(修正前・イメージ)
config.some_feature = combined_config[:flag] || default_value
# ただし上の行/前段の処理によって、実際には `|| default_value` に制御が到達しない

あるいは、条件分岐そのものが論理的に死んでいるような形:

ruby
# 修正前(イメージ)
if condition
  do_something
elsif other_condition || fallback_condition
  # `fallback_condition` に論理上到達しない
end

この PR では、その「実際には評価されない || の右側」を含む例示をやめ、実際のコードパスを正しく反映した形のサンプルに書き換えています。
実コードとコメントの例が一致するようにし、「読み手がこの書き方を真似すると、意図しない/意味のない条件式を書くことになる」状態を解消するのが目的です。

(具体的な1行差分は、「コメント中の || を含む条件式 → シンプルな条件 or 別の正しい例」に置き換えた、+2/-2 行の修正です。)


  1. 影響範囲・注意点
  • 実際のクラスの挙動(ActiveSupport::CombinedConfiguration)には変更はありません。
  • 影響があるのは コメントを読んでコードを書く人間の理解 のみです。
    • 以前のコメントを見て、到達しない || を含んだ条件式や設定コードを書いていた場合、そのロジックは本来意図したものではない可能性があります。
  • この PR は前回の PR (#56404) のフォローアップなので、CombinedConfiguration 周辺を利用している場合は、#56404 の内容も含めて読み直すと意図がつかみやすいです。

  1. 参考情報 (あれば)
  • 対象ファイル: activesupport/lib/active_support/combined_configuration.rb
  • 関連 PR: #56404 – この PR で導入/変更された例示コードの論理を正すフォローアップ。
  • CombinedConfiguration 自体は複数の設定ソース(例: デフォルト設定 + 環境ごとの設定 + ユーザー設定)を「合成」して1つの設定オブジェクトとして扱うための仕組みであり、その使い方を示すコメント・サンプルコードの正確さが重要な箇所です。

#56412 Fixed bin/rails notes command to work with CSS /**/ comments

マージ日: 2025/12/21 | 作成者: @davidrhyswhite

  1. 概要 (1-2文で)
    bin/rails notes コマンドが .css ファイル内の TODO / FIXME / OPTIMIZE などを正しく検出できていなかったバグを修正した PR です。CSS のコメント形式 /* ... */ に対応する正規表現へ変更し、期待どおりスタイルシート内のノートを拾えるようにしています。

  1. 変更内容の詳細

問題点

  • bin/rails notes は、ソースコード中の以下のようなコメントを拾って一覧表示する機能です:
    • # TODO: ...
    • // FIXME: ...
  • しかし CSS ファイルに対しては、JavaScript と同じ // コメント形式を前提とした正規表現で検索していたため、一般的な CSS コメント形式である /* ... */ の中の TODO などが検出されていませんでした。

例: これまでは次のようなコメントが無視されていた:

css
/* TODO: adjust spacing */
.button {
  margin: 10px;
}

bin/rails notes を実行しても、この TODO は出力されない状態でした。

修正内容

対象ファイル:

  • railties/lib/rails/source_annotation_extractor.rb
  • railties/test/commands/notes_test.rb

CSS 用正規表現の変更

CSS 用のコメント抽出ロジックで使用する正規表現が、CSS のコメント記法に合わせて修正されました。

新しい正規表現(Ruby コード上のイメージ)は以下です:

ruby
regexp = /\/\*\s*(#{tag}):?\s*(.*?)(?:\*\/)?$/

ポイント:

  • \/\*
    • CSS コメント開始 /* を表現。
  • \s*
    • 開始直後の任意の空白を許可。
  • (#{tag}):?
    • tagTODO / FIXME / OPTIMIZE など。
    • 後ろのコロン : は任意(TODO: xxxTODO xxx の両方を許可)。
  • \s*(.*?)
    • 実際のコメント内容(ノートの本文)をキャプチャ。
  • (?:\*\/)?
    • 行末に */ があればマッチするが、表示時には含めない(非キャプチャかつ任意)。
  • $
    • 行末までを対象とする。

この結果、以下のようなパターンが正しく拾われます:

css
/* TODO: use CSS variables */
.button {
  color: #333;
}

/*FIXME alignment issue */
.header {
  text-align: center;
}

/* OPTIMIZE reduce nesting */
.nav { ... }

行末の */ はノート本文としては出力されないため、bin/rails notes の表示が自然になります。

テスト追加

railties/test/commands/notes_test.rb に、CSS ファイル内の /* ... */ コメントからノートが抽出されることを確認するテストが追加されています。
これにより、今後のリグレッション(後戻りバグ)が防止されます。


  1. 影響範囲・注意点
  • 影響範囲:
    • bin/rails notes コマンドの CSS ファイルに対する挙動のみ。
    • 既存の Ruby / ERB / JavaScript など他のファイル種別の扱いには影響しない想定です。
  • 開発者視点での変化:
    • これまで .css に書いても拾われなかった TODO / FIXME / OPTIMIZE が、/* ... */ 形式であれば一覧に表示されるようになります。
  • 注意点:
    • CSS 側でも「タグの直前に /*」「同じ行にタグとその内容がある」形式を想定しているため、次のような「複数行にまたがる特殊な書き方」は一部期待どおりに扱われない可能性があります:

      css
      /*
        TODO: this might or might not be parsed as expected
      */
    • 通常利用では、以下のように「タグと本文は開始行に書く」スタイルを推奨できます:

      css
      /* TODO: support dark mode */
      body { ... }

  1. 参考情報 (あれば)
  • 該当ファイルの役割:
    • railties/lib/rails/source_annotation_extractor.rb は、bin/rails notes で使用される「ソースコードから注釈コメントを抽出する」ユーティリティクラスで、ファイル種別ごとのコメント形式に応じた正規表現を定義しています。
  • 関連ドキュメント:
    • Rails Guides: Command Line → rails notes
      (この PR 後もコマンドの使い方自体は変わらず、CSS サポートがより正確になった形です。)

#56410 Add block support to ActionController::Parameters#merge

マージ日: 2025/12/21 | 作成者: @Saidbek

  1. 概要 (1-2文で)
    ActionController::Parameters#merge にブロック引数のサポートが追加され、Hash#mergeParameters#merge! と同じインターフェースで使えるようになりました。これにより、パラメータ同士のマージ時にキー競合の解決ロジックをブロックで記述できます。

  1. 変更内容の詳細(あればサンプルコードも含めて)

何が変わったか

  • ActionController::Parameters#merge が、Hash#merge と同様にブロックを取れるように実装変更。
  • すでにブロック対応していた破壊的メソッド Parameters#merge! との API 不整合が解消。
  • CHANGELOG にこの仕様追加が記載され、テスト (parameters_permit_test) も追加。

挙動イメージ

従来(このPR前)は、merge にブロックを渡しても無視される/想定通り動かない状態でしたが、この PR 後は以下のように書けます:

ruby
params = ActionController::Parameters.new(
  foo: 1,
  bar: 2
)

other = ActionController::Parameters.new(
  foo: 10,
  baz: 3
)

merged = params.merge(other) do |key, old_value, new_value|
  # キーがかぶったときの値の決定ロジック
  old_value + new_value
end

# 結果イメージ:
# merged => #<ActionController::Parameters {"foo"=>11, "bar"=>2, "baz"=>3} permitted:false>

Hash#merge と全く同じブロックシグネチャ(|key, old_value, new_value|)で動く想定です。

merge! ですでにできていたことが、非破壊版の merge でも可能になった形です。


  1. 影響範囲・注意点
  • 互換性

    • 既存コードで merge にブロックを渡していなければ挙動は変わりません。
    • これまで「ブロックを渡しても無視されていた」コードがあれば、今後はブロックが実際に評価されるため、意図せぬ挙動変化が起きる可能性があります(ただし通常、そのようなコードはバグ寄りなので、多くは望ましい変化と考えられます)。
  • Strong Parameters の性質

    • ActionController::Parameters は Strong Parameters のオブジェクトなので、permit 状態(permitted / not permitted)やネスト構造への影響は従来の merge と同様です。
    • ブロックはあくまで「キーが重複したときの値をどう決めるか」を制御するものであり、パラメータの許可・非許可判定ロジックを変えるものではありません。
  • 一貫した API

    • これで Hash#merge / Hash#merge!Parameters#merge / Parameters#merge! すべてが、「非破壊・破壊」と「ブロック対応可否」の点で直感的に一致します。
    • ライブラリやアプリ側で HashActionController::Parameters を抽象的に扱う場合に、分岐を減らせます。

  1. 参考情報 (あれば)

#56404 Add Rails.app.creds to provide combined credentials lookup in ENV and encrypted file

マージ日: 2025/12/20 | 作成者: @dhh

  1. 概要 (1-2文で)
    Rails 8系(?) に、ENV と encrypted credentials を意識せずに一元的に参照できる Rails.app.creds が追加されました。これにより、鍵の保管場所を ENV と credentials の間で移行・併用してもアプリケーションコードを変更せずに済む設計になります。

  1. 変更内容の詳細

新しく追加された概念・クラス

1) Rails.app.creds

  • ENV と encrypted credentials をラップした「統合クレデンシャル」インターフェース。
  • 検索順序は ENV → Rails.app.credentials
  • API は require / option の2つで、credentials と同じ感覚で使えます。

サンプル:

ruby
# 必須値: なければ例外 (KeyError) を投げる
Rails.app.creds.require(:db_host)
# = ENV.fetch("DB_HOST") || Rails.app.credentials.require(:db_host)

Rails.app.creds.require(:aws, :access_key_id)
# = ENV.fetch("AWS__ACCESS_KEY_ID") || Rails.app.credentials.require(:aws, :access_key_id)

# 任意値: なければ nil
Rails.app.creds.option(:cache_host)
# = ENV["CACHE_HOST"] || Rails.app.credentials.option(:cache_host)

# 任意値 + デフォルト値 (値が見つからなければデフォルト)
Rails.app.creds.option(:cache_host, default: "cache-host-1")
# = ENV["CACHE_HOST"] || Rails.app.credentials.option(:cache_host) || "cache-host-1"

# デフォルト値を遅延評価 (Proc)
Rails.app.creds.option(:cache_host, default: -> { "cache-host-1" })
# = ENV["CACHE_HOST"] || Rails.app.credentials.option(:cache_host) || "cache-host-1"

ENV 側のキー名は、credentials の階層を __(ダブルアンダースコア)でつなげた形式で解決されます:

ruby
Rails.app.creds.require(:aws, :access_key_id)
# => ENV["AWS__ACCESS_KEY_ID"] が優先
# なければ config/credentials.yml.enc (等) の aws.access_key_id を参照

2) Rails.app.envs

  • ENV 専用の設定ラッパー。
  • require / option の API は Rails.app.creds / Rails.application.credentials と同じ。
  • 内部では ActiveSupport::EnvConfiguration を使う。

サンプル:

ruby
Rails.app.envs.require(:db_host) # => ENV.fetch("DB_HOST")
Rails.app.envs.option(:db_host)  # => ENV["DB_HOST"]

3) ActiveSupport::CombinedConfiguration

  • 複数の設定バックエンド(ENV, credentials, 1Password など)を束ねて、順番に解決するためのクラス。
  • Rails.app.creds の中身に使われている実装。

カスタム構成例:

ruby
Rails.app.creds = ActiveSupport::CombinedConfiguration.new(
  Rails.app.envs,
  OnePasswordConfiguration.new,   # 独自実装の設定プロバイダ
  Rails.app.credentials,
)

これにより「ENV → 1Password → credentials」という優先順位で値を探索することができます。

4) ActiveSupport::EnvConfiguration

  • ENV を require / option インターフェースで扱うためのクラス。
  • Rails.app.envs の実体。

require / option の振る舞いは以下のようなイメージです:

ruby
envs = ActiveSupport::EnvConfiguration.new(ENV)

envs.require(:db_host)         # => ENV.fetch("DB_HOST") (無ければ KeyError)
envs.option(:db_host)          # => ENV["DB_HOST"] || nil
envs.option(:db_host, default: "localhost")  # => ENV["DB_HOST"] || "localhost"

5) ActiveSupport::EncryptedConfiguration の拡張

  • credentials 側も require / option のインターフェースが明確化・テスト強化されています。
  • これにより、envs / creds / credentials で統一的に同じ呼び出しスタイルを使えるようになります。

  1. 影響範囲・注意点

影響範囲

  • 新 API 追加が中心で、既存の Rails.application.credentials の挙動を壊す変更ではありません。
  • Rails.app.creds / Rails.app.envs を使い始めることで、以下が容易になります:
    • ENV から encrypted credentials への移行、またはその逆
    • 一部サービスだけ ENV、他は credentials などの「ハイブリッド運用」
    • 将来の別ストア(例: 1Password)への移行・追加

実践的な使い方の指針

  • 新規コード・リファクタ時は、「設定をどこに置くか」を意識せずに済むように Rails.app.creds を使うのが推奨されます。
    • 例: DB 接続先、S3/AWS/GCP のキー、外部 API のトークンなど。
  • 「ENV にしか無い想定」の値を扱うなら Rails.app.envs を使う選択もできますが、将来的な移行性を考えると creds で統一しておく方が柔軟です。

注意点

  • ENV のキー名は、シンボル名を大文字 + アンダースコアにした形,ネストは __(ダブルアンダースコア) でつなぐという規約に従います:
    • :db_hostDB_HOST
    • :aws, :access_key_idAWS__ACCESS_KEY_ID
  • require は値が見つからないと例外(KeyError 想定)になるので、アプリ起動時・設定ロード時に早期に失敗させたい値に用いると安全です。
  • option は nil や default を許容するため、必須/任意の設計を意識的に分ける必要があります。
  • 複数バックエンドを組み合わせた場合、先に指定したバックエンドが常に優先されます。優先度の誤設定に注意してください。

  1. 参考情報 (あれば)
  • 新規クラス・API:
    • Rails.app.credsActiveSupport::CombinedConfiguration ベース)
    • Rails.app.envsActiveSupport::EnvConfiguration ベース)
    • ActiveSupport::CombinedConfiguration
    • ActiveSupport::EnvConfiguration
  • 関連テスト:
    • activesupport/test/combined_configuration_test.rb
    • activesupport/test/env_configuration_test.rb
    • activesupport/test/encrypted_configuration_test.rb
    • railties/test/application/creds_test.rb
    • railties/test/application/envs_test.rb

これらのテストコードを読むと、優先順位の詳細やエラー時の挙動、ENV 名の解決ルールなどを具体的に確認できます。


#56409 Remove support for psych < 4

マージ日: 2025/12/20 | 作成者: @byroot

  1. 概要 (1-2文で)
    Psych 4未満(古いYAMLライブラリ)への対応コードをRails本体から削除し、「Psych 4以上のみサポート」に整理したPRです。これによりYAML関連の分岐や互換コードが減り、実装とテストがシンプルになっています。

  1. 変更内容の詳細

全体方針

  • Rails側で「psych < 4」を考慮した条件分岐・ワークアラウンドを削除
  • Gem仕様・テスト群を「psych 4が前提」の世界に揃えることで、不要なチェックを廃止

2-1. 依存関係まわり

  • Gemfile.lock
  • activesupport/activesupport.gemspec

ここでpsychのバージョン前提が事実上「>= 4」になるように調整されています(実際には依存関係やロックファイルの更新程度で、アプリ開発者が直接触るコードではありません)。

2-2. ActiveRecord: YAML カラムまわり

  • ファイル: activerecord/lib/active_record/coders/yaml_column.rb
  • 変更: +9/-23 と行数削減が大きく、ここがメインの整理ポイントの一つです。

想定される変更内容:

  • Psych 3と4で異なっていたYAMLシリアライズ/デシリアライズの挙動差を吸収するための分岐削除

  • 例えば、以下のようなコードが簡略化されている可能性が高いです(イメージサンプル):

    ruby
    # 変更前イメージ(バージョン分岐がある)
    def load(yaml)
      return if yaml.nil? || yaml == ""
    
      if Psych::VERSION < "4"
        Psych.safe_load(yaml, permitted_classes, [], aliases: true)
      else
        Psych.safe_load(yaml, permitted_classes, aliases: true)
      end
    end
    ruby
    # 変更後イメージ(Psych 4のみを想定)
    def load(yaml)
      return if yaml.nil? || yaml == ""
    
      Psych.safe_load(yaml, permitted_classes, aliases: true)
    end

    ※上はあくまで典型例イメージですが、「Psychのバージョンによる引数の違い」や「デフォルト挙動の違い」を吸収する分岐が削られていると考えられます。

  • これによりYAMLカラムのエンコード/デコード処理がすっきりし、新しいPsychの仕様に沿った形に一本化されます。

2-3. ActiveRecord: スキーマキャッシュ

  • ファイル: activerecord/lib/active_record/connection_adapters/schema_cache.rb
  • 行数: +1/-5

ここもYAML経由でスキーマキャッシュを保存/読み込みする部分に、psychバージョンごとの分岐があったと考えられます。

典型的には:

  • YAML.dump / YAML.load 周りでのpsych 3/4差異吸収
  • safe_load のオプション(aliases:permitted_classes)の取り扱いなど

これが「Psych 4準拠」の実装に一本化され、余分な条件分岐が削除されています。

2-4. ActiveRecord テスト類の整理

変更ファイル:

  • activerecord/test/cases/yaml_serialization_test.rb (+16/-20)
  • activerecord/test/cases/adapters/postgresql/hstore_test.rb
  • activerecord/test/cases/attribute_methods_test.rb
  • activerecord/test/cases/connection_adapters/schema_cache_test.rb
  • activerecord/test/cases/json_shared_test_cases.rb
  • activerecord/test/cases/locking_test.rb
  • activerecord/test/cases/store_test.rb

主な意図:

  • Psych 3 と Psych 4 で出力されるYAML文字列の違い/ロード挙動の違いに合わせていたテスト期待値を、Psych 4ベースに統一
  • 条件付きアサーション(「PsychのバージョンがX未満のときは〜」など)を削除
  • テストがRailsのサポート対象環境(Psych 4)に対してのみ意味を持つように整理

2-5. ActionPack / ActiveModel テスト

  • actionpack/test/controller/parameters/serialization_test.rb
  • activemodel/test/cases/errors_test.rb

こちらもYAMLシリアライズ・デシリアライズに依存するテストで、Psych 4前提の期待値に変更されています。
例:

  • HashのYAML表現の細かい差(キー順やアンカーの扱いなど)がPsych 3/4で異なる場合、それに合わせた条件分岐を消す・期待文字列を一本化するなど。

2-6. ActiveSupport の設定/暗号化設定

  • activesupport/lib/active_support/configuration_file.rb (+2/-10)
  • activesupport/lib/active_support/encrypted_configuration.rb (+1/-4)

ここでは以下のような整理が行われている可能性が高いです。

  • config/*.yml を読み込む際の YAML.safe_load 呼び出しが、Psych 4のシグネチャに依存した形に統一
  • psych < 4 で必要だったfallbackオプションの明示指定を削除

イメージ:

ruby
# 完全にイメージ
def read_config(path)
  yaml = File.read(path)
  Psych.safe_load(yaml, permitted_classes, aliases: true)
end

以前は「Psych 3では第N引数に空配列を渡す必要がある」などの互換コードがあったのを削除した形です。

2-7. ActiveSupport テスト (Time / Duration / TimeZone)

  • activesupport/test/core_ext/duration_test.rb
  • activesupport/test/core_ext/time_with_zone_test.rb
  • activesupport/test/time_zone_test.rb

ここもYAMLシリアライズ(time/durationのYAML表現)やロード結果がPsych 4で安定している前提に書き換え。
例えば:

  • "!ruby/object:ActiveSupport::TimeWithZone" の表現や内部フィールド名が、Psych 3/4で違っていたことに由来する「バージョン条件付きアサート」を削除、あるいは1パターンに統一していると考えられます。

  1. 影響範囲・注意点

影響範囲

  1. Railsを最新に上げるアプリケーション全般

    • このPRが含まれるRailsバージョン以降では、「Psych 4以上」が前提になります。
    • システムのRuby環境でPsych 3系などを使っている場合、Bundlerの解決時にエラーになる、あるいは動作時に非対応な呼び出しが行われる可能性があります。
  2. YAMLを直接扱うコード・独自拡張

    • RailsのYAMLまわり(ActiveRecordのYAMLカラム、schema cache、configファイル、暗号化設定など)は心理的に「Psych 4のsafe_load挙動」を前提に安定するため、挙動が僅かに変わるケースがあります。
    • ただし、このPR自体は「古いPsychへの互換コード削除」がメインであり、新たな仕様変更ではなく「Rails側の前提バージョンの明確化」に近いです。
  3. テストコード

    • Psych 3/4差を吸収していたテストは削除されているので、「CIがPsych 3でたまたま動いていた」ような環境は今後想定されなくなります。
    • プロジェクト内でRailsテストを参考にしている場合、YAML表現を比較するような箇所の期待値をPsych 4に合わせる必要があるかもしれません。

注意点 / マイグレーション的観点

  • RailsをこのPRを含むバージョンにアップデートする場合:

    1. Gemfile で明示的に古いPsychを指定していないか確認
    2. システムにバンドルされているRubyの標準Psychバージョンが古すぎないか確認
    3. YAMLを直接扱っているアプリケーションコード(特にYAML.safe_load / Psych.safe_load)で、Railsの変更に合わせて「Psych 4前提のオプション指定」に整理しておくと安全
  • 特に、「permitted_classes」や「aliases: true/false」などを利用している場合、Psych 4での推奨シグネチャを確認しておくとよいです。


  1. 参考情報 (あれば)
  • Psych 4のAPI変更点(参考: PsychのCHANGELOG / ドキュメント)
    • YAML.safe_load / Psych.safe_load の引数順やデフォルト値がPsych 3以前と異なる
    • より厳密なセキュリティデフォルト(許可クラスの制限など)
  • 類似PRの傾向:
    • Ruby/Railsエコシステムでは、Ruby本体や標準ライブラリのメジャーバージョンアップに合わせて、古いバージョンへの互換コードを徐々に削除する流れがあり、このPRもその一環と考えられます。

#56408 Make minitest-mock a hard dependency again

マージ日: 2025/12/20 | 作成者: @byroot

  1. 概要 (1-2文で)
    Rails 自身のテストスイートで使っている minitest-mock を「必須依存関係(hard dependency)」に戻す PR です。以前行った「soft dependency 化」の変更をリバートし、Rails テスト実行時に minitest/mock を必ず読み込む形に統一しています。

  1. 変更内容の詳細

対象ファイル:

  • activesupport/lib/active_support/testing/method_call_assertions.rb

このファイル内で行われた主な変更は、「minitest/mock が存在しない場合はスキップする/別コードパスにする」といった“ソフト依存”向けの処理を削除し、常に minitest/mock を前提とした実装に戻した点です。

PR 説明文から分かる背景:

  • 元の変更: #56207 で minitest-mock をソフト依存にしようとした
  • 本 PR: その変更コミット c5c678a6... を Revert して元に戻した

method_call_assertions.rb は、Rails のテストで「メソッド呼び出しをアサートする」ための補助モジュールで、内部で Minitest::Mock を使っている部分があります。
今回の変更により、そこにあった「minitest/mock がロードできない場合のフォールバック」や、条件付き require などのコードが削除され、代わりにシンプルに minitest/mock を前提とする構成に戻っています。

差分イメージ(概念的なもの):

ruby
# 変更前(soft dependency 化されていた状態のイメージ)
begin
  require "minitest/mock"
rescue LoadError
  # minitest-mock なしでも動くためのフォールバックや
  # 定義ガードなどがあった
end

# 変更後(hard dependency)
require "minitest/mock"

module ActiveSupport
  module Testing
    module MethodCallAssertions
      # ここで Minitest::Mock を前提にした実装
    end
  end
end

実際には削除行が 10 行、追加行が 1 行と小さな差分で、主に「例外処理や条件分岐」を消して、単純に minitest/mock に依存するようにしています。


  1. 影響範囲・注意点
  • 影響範囲:
    • Rails 本体の「自前テストスイート」に限定されます。
    • PR 説明にもある通り、ActiveSupport::Testing::MethodCallAssertions は Rails 開発時の内部テストでのみ使われ、一般のアプリケーションコードが直接この「minitest-mock の hard dependency 化」の影響を受けることは基本的にありません。
  • gem 利用者への実務的な影響:
    • Rails を gem として利用し、自分のアプリ側で ActiveSupport::Testing::MethodCallAssertions を直接使っていない限り、動作への変化はほぼありません。
    • Rails コアのテストを実行する開発者は、引き続き minitest-mock をインストールしておく必要があります(=開発環境で bundle exec rake test などを回す場合に必須)。
  • 注意点:
    • もし Rails の内部テストモジュールを流用している特殊なケースがあれば、minitest/mock が存在しない環境ではテストが落ちる/ロードに失敗する可能性があります。その場合は minitest を通常通り導入すれば問題は解消します。
    • 「minitest 自体は使うが minitest/mock はロードしたくない」といった特殊構成は考慮されなくなります。

  1. 参考情報 (あれば)

#53335 Deprecate built-in snekaers adapter

マージ日: 2025/12/19 | 作成者: @dixpac

  1. 概要 (1-2文で)
    Rails の Active Job に含まれていた built-in の sneakers アダプタが非推奨(deprecate)扱いになり、代わりに後継である kicks gem の利用が推奨されるようになりました。これに伴い、非推奨警告の追加と関連テスト、CHANGELOG の更新が行われています。

  1. 変更内容の詳細

2-1. CHANGELOG の更新

activejob/CHANGELOG.md に、以下の趣旨のエントリが追加されています。

  • Active Job が提供している sneakers アダプタを非推奨にしたこと
  • sneakers の開発が kicks に移行したため、今後は kicks を使うべきであること

これにより、Rails 7.x/8.x 以降(バージョンは今後のリリース次第)で sneakers アダプタが将来的に削除される伏線になります。

2-2. sneakers アダプタ本体への非推奨警告追加

activejob/lib/active_job/queue_adapters/sneakers_adapter.rb に非推奨警告が追加されています。
おおよそ以下のようなコードが追加されていると考えられます(内容イメージ):

ruby
module ActiveJob
  module QueueAdapters
    class SneakersAdapter
      def initialize(*)
        ActiveSupport::Deprecation.warn(
          "ActiveJob's built-in SneakersAdapter is deprecated. " \
          "The sneakers gem has moved to kicks. "\
          "Please switch to the kicks gem and its adapter."
        )
        super
      end

      # 既存の実装はそのまま
    end
  end
end

もしくは、クラス定義時や特定メソッド呼び出し時に ActiveSupport::Deprecation.warn が呼ばれる形で、Rails 起動時またはキューアダプタ利用時に deprecation メッセージが出るようになっています。

ポイント:

  • sneakerskicks に移行した」という情報を明示
  • 今後 kicks gem 側のアダプタに移行するように促す文言

2-3. テストの追加

activejob/test/cases/adapter_test.rb に、sneakers アダプタの非推奨警告を検証するテストが追加されています。

想定されるテスト内容のイメージ:

ruby
test "deprecates sneakers adapter" do
  assert_deprecated do
    ActiveJob::Base.queue_adapter = :sneakers
  end
end

もしくは、ActiveJob::QueueAdapters::SneakersAdapter.new などを実行したときに deprecation が出ることを assert_deprecated / assert_deprecated_warning で検証する形になっています。


  1. 影響範囲・注意点

3-1. 現在 sneakers アダプタを使っているアプリへの影響

  • config.active_job.queue_adapter = :sneakers などで sneakers アダプタを指定している場合、Rails の起動時やジョブ実行時に「非推奨です」という deprecation warning がログに出るようになります。
  • ただし、この PR 時点では「削除」ではなく「非推奨宣言」のみなので、即座に動かなくなるわけではありません。

3-2. 今後の推奨対応

  • sneakers gem 自体が kicks に移管されているため、将来的なメンテナンス性や互換性を考えると、kicks gem への移行を検討すべきです。
  • Rails 本体に kicks 用のアダプタが組み込まれるか、もしくは外部 gem として提供されるかは別問題ですが、少なくとも:
    • sneakers に依存した設計や設定を kicks に合わせて見直す
    • 本番環境での AMQP/RabbitMQ 接続設定を kicks 仕様に合わせて移行 といった対応を事前に検討しておくとスムーズです。

3-3. CI / ログ監視への影響

  • deprecation warning をエラー扱いしている CI 設定(例: RAILS_ENV=test で deprecation を raise する設定)では、テストが落ちる可能性があります。
  • そうした場合は一時的に deprecation を許容する設定に変更するか、早期に kicks への移行を行う必要があります。

  1. 参考情報

この PR は「今後 sneakers は Rails/Active Job の一級市民ではなくなる」ことを公式に示す第一段階なので、sneakers を継続利用しているプロジェクトでは、今のうちから kicks への移行計画を立てるのが安全です。


#56207 Bump minitest to V6

マージ日: 2025/12/19 | 作成者: @zenspider

  1. 概要 (1-2文で)
    Rails のテスト周りの依存関係として使っている minitest を 6.0 以上に引き上げ、あわせて minitest-mock を追加し、それに伴って発生したテスト関連の不整合や不具合を修正した PR です。主に ActiveSupport のテスト補助モジュールと並列実行周り、およびガイド用テストヘルパに対する調整が含まれます。

  1. 変更内容の詳細

2-1. 依存関係の更新 (Gemfile, Gemfile.lock)

  • minitest のバージョン要件を「6.0 以上」に更新
    • これにより、Rails のテストは minitest 5 系ではなく 6 系を前提に動作するようになります。
  • 新たに minitest-mock の依存を追加
    • Rails コアのテストやガイド用テストで、minitest 標準のモック機能を明示的に利用できるようにしています。
    • 既に Rails 内部で「モック・スタブ」を使っている箇所や、今後 minitest 6 向けの API を前提にしたモック利用を行う基盤になります。

ポイント:

  • minitest 本体とモック機能を分離して扱う構成になってきているため、Rails 側でも明示的に minitest-mock を依存に入れています。
  • Gemfile.lock の差分は、上記のバージョンアップと新規 gem 追加にともなうものです。

2-2. ActiveSupport: method_call_assertions の調整

対象: activesupport/lib/active_support/testing/method_call_assertions.rb

  • minitest 6 での挙動・API の変化に合わせて、メソッド呼び出しに対するアサーションロジックを更新
    • 例: 期待されるメソッドが何回呼ばれたか、どの引数で呼ばれたかを検証するような補助メソッド (assert_called, assert_called_with など) が内部で minitest のモック/アサーション機能を利用しており、その互換性を保つための修正が入っています。
    • 5 系 → 6 系で、モックや expectation の API 名やエラークラス、メッセージ仕様などが微妙に変わっているため、それに適合するような修正です。

おおまかなイメージ(※擬似コードレベルの例):

ruby
# 旧来: minitest 5 系前提
def assert_called(object, method_name, times: 1)
  mock = Minitest::Mock.new
  mock.expect(:call, nil)
  # ...
end

# 新: minitest 6 系前提
def assert_called(object, method_name, times: 1)
  mock = Minitest::Mock.new
  # 6 系の仕様にあわせた expect / verify / エラー処理に変更
  mock.expect(:call, nil)
  # ...
end

実際の差分は、

  • minitest 6 のインターフェイスに合わせた API 呼び出し
  • エラー発生時のメッセージや検証処理 などが調整されていると考えられます。

2-3. ActiveSupport: 並列実行用 ThreadPoolExecutor の修正

対象: activesupport/lib/active_support/testing/parallelization/thread_pool_executor.rb

  • minitest 6 の並列テスト実行やスレッドまわりの挙動変更で発覚した不具合を修正
    • スレッドプールからのジョブ取り出し処理や、例外処理・終了処理のタイミング調整などが含まれている可能性が高いです。
    • Rails のテストを PARALLEL_WORKERS などで並列実行した際に、minitest 5 → 6 の変更により発生していた新たなテスト失敗・不具合がここで吸収されています。

代表的には、以下のような観点での修正が考えられます:

  • スレッド終了時に未処理ジョブや例外が適切にハンドリングされない問題
  • minitest の Minitest.parallel_executor 周りとの整合性
  • テスト完了待ち (join) のタイミングやエラー伝播の方法

2-4. ガイド用テストヘルパの変更

対象: guides/test/test_helper.rb

  • ガイド内のサンプルテストコードが minitest 6 を前提として動作するように、テストヘルパに 1 行追加
    • 典型的には require "minitest/autorun" ではなく、minitest-mock のロードや minitest 6 用の設定を行う行が追加された可能性があります。
  • Rails ガイドのテストの実行方法をそのままなぞった場合でも、minitest 6 環境で問題なく動くよう配慮した修正です。

  1. 影響範囲・注意点

プロジェクト側における影響:

  • Rails 本体のテスト:

    • Rails 自体を開発している場合、bundle exec rake test 等で走るテストは minitest 6 前提になります。
    • 自前で minitest の内部 API に依存している(例: 独自のアサーションを minitest のプライベートメソッドに依存して実装している)場合は、5→6 で動かなくなる可能性があります。
  • Rails を利用しているアプリケーション:

    • 通常の Rails アプリは、この PR によって直接的に「アプリコード」が壊れる可能性は低いです。
    • ただし、以下のようなケースは注意が必要です:
      • Gemfile で minitest のバージョンを明示ピン止めしている
        • Rails が 6 系を要求するようになった場合、依存関係の競合が起きる可能性があります。
      • minitest 5 系の API を前提にした独自のヘルパーやサポートコードを大量に書いている
        • 例: Minitest::Test の内部実装や、deprecate 済みメソッドに直アクセスしている場合。

対応方針の目安:

  • Rails のテストヘルパや test_helper.rbrequire 'minitest/autorun' 以外の独自設定をしている場合は、minitest 6 向けに問題なく動作するかを確認すると安心です。
  • モックやスタブを Rails 独自ではなく minitest の機能として使っている場合、minitest-mock が別 gem として扱われることを把握しておくと、CI や開発環境構築でのトラブル防止になります。

  1. 参考情報 (あれば)
  • minitest 6 系の変更点・マイグレーションの参考:
  • Rails のテスト周りの公式ガイド:

#56403 Add Rails.app alias for Rails.application

マージ日: 2025/12/19 | 作成者: @dhh

  1. 概要 (1–2文で)
    Rails に Rails.application の短縮エイリアスとして Rails.app が追加され、コアコード・ジェネレータ・ガイド内の一部参照もそれに合わせて置き換えられました。これにより、Rails.application.credentials などの長くなりがちな呼び出しが、Rails.app.credentials のように簡潔に書けるようになります。

  1. 変更内容の詳細

2-1. 新しいエイリアスメソッドの追加

railties/lib/rails.rb に以下のような形でエイリアスが追加されています(概念的にはこんな感じ):

ruby
module Rails
  class << self
    # 既存
    def application
      @application
    end

    # 新規追加
    alias_method :app, :application
  end
end

これにより、フレームワーク全体で次のような記述が可能になります。

ruby
Rails.application.credentials  # 従来
Rails.app.credentials          # 新しい書き方

2-2. 既存コードの Rails.applicationRails.app への置き換え

複数のファイルで Rails.applicationRails.app に置き換えています。主なものは以下です。

Action Mailbox コントローラ

actionmailbox/app/controllers/action_mailbox/* でアプリケーションの設定・資格情報などを参照している箇所が更新されています。

例(イメージ):

ruby
# 変更前
Rails.application.credentials.dig(:mailgun, :api_key)

# 変更後
Rails.app.credentials.dig(:mailgun, :api_key)

test/dummy アプリの storage.yml

Action Mailbox / Action Text / Active Storage の dummy アプリの config/storage.yml がそれぞれ更新されています。

yaml
# 変更前
service: Disk
root: <%= Rails.application.root.join("storage") %>

# 変更後
service: Disk
root: <%= Rails.app.root.join("storage") %>

ActiveRecord テスト

activerecord/test/cases/encryption/envelope_encryption_key_provider_test.rb などで、アプリケーションの key / credentials にアクセスしている箇所が Rails.app に変更されています。

Rails ジェネレータのテンプレート

新規アプリ作成時に生成されるファイルのテンプレートも変更されています。

  • config.ru.tt
  • config/environments/production.rb.tt
  • config/storage.yml.tt

例:

ruby
# config.ru.tt
# 変更前
run Rails.application

# 変更後
run Rails.app
ruby
# config/environments/production.rb.tt
# 変更前
config.active_storage.service = :local if Rails.application.credentials.dig(:aws, :bucket).blank?

# 変更後
config.active_storage.service = :local if Rails.app.credentials.dig(:aws, :bucket).blank?

※実際の条件やキー名は PR 時点の実ファイルに依存しますが、概ねこのように Rails.application 呼び出しが置き換わっています。

ガイドの更新

以下のガイド内のサンプルコードも Rails.app を使うように更新されています。

  • guides/source/action_mailer_basics.md
  • guides/source/active_storage_overview.md

例:

ruby
# 変更前 (ガイド)
Rails.application.credentials.dig(:mailgun, :api_key)

# 変更後
Rails.app.credentials.dig(:mailgun, :api_key)

CHANGELOG

railties/CHANGELOG.mdRails.app 追加に関する項目が追記されています。
これにより、この変更が公式にパブリック API として意図されていることが示されています。


  1. 影響範囲・注意点
  • 後方互換性

    • Rails.application は引き続き利用可能であり、今回の変更はあくまで「エイリアス追加」と「内部・テンプレートの利用スタイル変更」です。既存アプリはそのまま動作します。
  • コードスタイル / ベストプラクティス上の影響

    • 今後は Rails 本体やガイドに倣って、Rails.application よりも Rails.app を使うのが推奨される方向になると考えられます。
    • 特に credentialsconfigroutes 等と組み合わせた長いチェーンを短く書けるメリットがあります:
      ruby
      Rails.application.credentials.dig(:aws, :access_key_id)
      Rails.app.credentials.dig(:aws, :access_key_id)
  • ライブラリ / ジェム作者への影響

    • gem 内で Rails アプリケーションオブジェクトにアクセスする部分は、従来どおり Rails.application でも、新しい Rails.app でも動作します。
    • ドキュメントや README に掲載しているサンプルコードを更新する場合は、新しい書き方 (Rails.app) を採用すると、将来的な公式ドキュメントとの一貫性が保ちやすくなります。
  • 自動生成コードへの影響

    • 新規に rails new で作るアプリや、一部ジェネレータが吐き出すファイルに Rails.app が含まれるようになります。
    • 古いテンプレートを前提にしたドキュメントや記事との表記ゆれが生じる可能性がありますが、機能的な問題はありません。

  1. 参考情報
  • PR: https://github.com/rails/rails/pull/56403
  • 変更概要: Rails.appRails.application のシンタックスシュガーであり、特に Rails.app.credentials などの長い構文を読みやすくすることを目的とした追加です。

#56398 Remove duplicate unique index for token migrations

マージ日: 2025/12/18 | 作成者: @zzak

  1. 概要 (1-2文で)
    Rails の migration / model generator が「token」カラムに対して重複した unique index を作ってしまう問題を修正し、生成されるマイグレーションから不要な重複 index を取り除いた PR です。これにより、add_indexindex: { unique: true } の二重指定などによるエラーや冗長な定義が解消されます。

  1. 変更内容の詳細

※PR本文・差分から推測できる範囲での技術的な整理です。

背景となる問題

Rails の generator(特に Active Record の migration generator)が、token という名前の属性に対して:

  • create_table ブロック内で t.string :token, index: { unique: true } のようなオプション付きカラム定義を行いつつ、
  • 同じマイグレーション内で add_index :table_name, :token, unique: true なども生成する

といった形で、「同じカラムに対する unique index が二重に定義される」ケースが発生していました。
この PR はその重複を取り除き、「token 用の unique index を 1 箇所だけにする」ように generator の挙動を修正したものです。


各ファイルごとの主な変更

1) create_table 用テンプレート

activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt (+2/-2)

  • rails g migration CreateUsers token:string:uniq のような場合に使われる create_table 形式のマイグレーションテンプレートが修正されています。
  • ここから「token 向けに自動で unique index を追加していた部分」もしくはその条件が見直され、
    • すでに index: { unique: true } が付く場合に別途 add_index を生成しない
      または
    • どちらか一方だけで unique index を定義する という形に整理されています。

イメージとしては、以前は以下のように「二重」になっていたパターンを:

ruby
create_table :users do |t|
  t.string :token, index: { unique: true }
end

add_index :users, :token, unique: true

修正後はどちらか一方(おそらく create_table 内の定義か、あるいは add_index 側)に統一するような形になります。

2) 通常の migration テンプレート

activerecord/lib/rails/generators/active_record/migration/templates/migration.rb.tt (+1/-1)

  • 「create_table ではない」一般的な migration(例: add_column など)で使われるテンプレートの index 生成ロジックが調整されています。
  • ここでも token カラムに対して generator が「unique index を 2 回生成しない」ように条件や出力が修正されています。

3) GeneratedAttribute のロジック

railties/lib/rails/generators/generated_attribute.rb (+5/-1)

  • generator が rails g model User token:string:uniq のような属性指定から「index が必要かどうか」「unique index かどうか」を判断するクラスです。
  • ここに「token に対する index または unique index をどう扱うか」のロジックが追加・修正されています。

推測されるポイント:

  • token という名前は API トークン・認証トークンなどでほぼ必ず一意であるべきため、
    • token を指定したときに自動で unique: true を付ける
    • 既に :uniq オプション等で unique と指定されている場合は別途自動付与しない
  • いずれにしても「同じ token カラムに対して 2 回 unique index を生成する」ことが無いように、index?unique? 判定の実装が整理されています。

4) テストの追加

railties/test/generators/migration_generator_test.rb (+21/-0)

  • 上記の修正が効いていることを確認するため、migration generator に関するテストが追加されています。
  • 主な確認内容:
    • token カラムを持つ migration / model / scaffold などを generator で生成したとき、
      • 期待通り 1 つだけ unique index が定義されること
      • 不要な add_index が生成されないこと
      • schema レベルで見ても index が重複していないこと
  • これにより、今後の変更で同じバグが再発しにくくなっています。

5) CHANGELOG の更新

railties/CHANGELOG.md (+4/-0)

  • このバグ修正が Railties の変更点として記録されました。
  • バージョンアップ時に「token 用の重複 unique index が generator から取り除かれた」ことがわかるようになっています。

  1. 影響範囲・注意点
  • 対象:
    • rails g model, rails g migration, rails g scaffold など、Active Record の migration / model generator 全般
    • 特に token カラム(例: token:stringtoken:string:uniq)を含む generator の利用ケース
  • 期待される影響:
    • 新たに generator を使って作るマイグレーションでは、token 用の unique index が 1 つだけになる。
    • これまでのように、migration 実行時に
      • 「同名 index の作成に失敗する」
      • 「DB 側で一意制約が重複している」 といったエラーが起きづらくなる。
  • 既存アプリへの影響:
    • 既に作成済み・マージ済みの古いマイグレーションや DB の index は自動では変更されません。
    • もしすでに「token に対して重複 index がある」場合は、手動で古い index を削除するマイグレーションを追加する必要があります。
  • マルチ DB / DB ごとの制約:
    • PostgreSQL, MySQL, SQLite いずれでも「同じカラムへの同じ unique index 名」の重複は一般にエラーの原因になりやすいため、今回の修正はどの DB を使っていても有益です。

  1. 参考情報 (あれば)
  • 関連 Issue:
    • #56038: token 用の重複 unique index に関する報告
    • #56131: 同様の不具合あるいは改善要望の追跡
  • 影響を確認したい場合のチェックポイント:
    1. db/migrate 以下で token を追加/作成しているマイグレーションを検索
    2. 同じマイグレーションファイル内に
      • t.string :token, index: { unique: true }
      • add_index :..., :token, unique: true の両方が無いか確認
    3. DB 側で \d table_name (PostgreSQL) や SHOW INDEX FROM table_name; (MySQL) などで index が二重にないか確認

新たに Rails の generator を使って token カラムを追加する場合は、この PR が取り込まれたバージョン以降では、原則として「自分では二重に unique index を書かない」ように注意しておくと安全です。


#56400 Yield key to ActionController::Parameters#fetch

マージ日: 2025/12/18 | 作成者: @seanpdoyle

  1. 概要 (1-2文で)
    ActionController::Parameters#fetch が、存在しないキーに対してブロックを呼ぶ際に「キー自体」を引数として渡すように変更され、Ruby の Hash#fetch と同じインターフェースになりました。Strong Parameters を Hash 互換として扱うコードの挙動が、より一貫したものになります。

  1. 変更内容の詳細

何が変わったか

従来:

ruby
params = ActionController::Parameters.new

params.fetch(:user) { |value| p value }
# => ブロックには「何も渡されない」 (引数が nil になる / そもそも引数を取れない前提で書いていることが多い)

この PR 後:

ruby
params = ActionController::Parameters.new

params.fetch(:user) { |key| p key }
# => :user がブロック引数 key に渡される (Hash#fetch と同じ)

Ruby 標準ライブラリの Hash#fetch は以下のような仕様です:

ruby
h = {}
h.fetch(:foo) { |key| "missing: #{key}" }
# => "missing: foo"

今回の変更により、ActionController::Parameters#fetch も同様に、キーが存在しない場合にブロックへ「キー」を渡します。

実装面での変更ポイント

※実際のコードは概略ですが、イメージとしては:

ruby
def fetch(key, *args)
  if has_key?(key)
    self[key]
  elsif block_given?
    # 変更前: yield
    yield key  # 変更後: キーを渡して yield
  else
    # 既存の例外やデフォルト値の挙動はそのまま
    super
  end
end

テスト (actionpack/test/controller/parameters/accessors_test.rb) には、

  • 未定義キーを fetch したときに、
  • 渡したブロックがキーを受け取れること

を確認するケースが追加されています。

actionpack/CHANGELOG.md にも、この動作変更が明記されています。


  1. 影響範囲・注意点

影響しうるコード

  1. ActionController::Parameters#fetch にブロックを渡しているコードで、
    「ブロック引数の数や中身」に依存しているもの。

    例: これまでは引数なし想定で書いていた場合

    ruby
    params.fetch(:page) do
      # 引数を取らない前提
      1
    end

    → このようなコードは、Ruby のブロック引数の仕様上そのままでも動きますが、
    「キーが引数として渡されるようになった」という意味で挙動が変わっています。
    (ただし引数を定義していないブロックは、渡された引数を単に無視するので、多くの場合は実害なし)

  2. Strong Parameters を Hash と同様に扱うライブラリ / アプリケーションコードで、
    Hash#fetch 相当の動作を期待していたが、うまく動いていなかったケースが「期待通り」になる。

    例:

    ruby
    def fetch_or_log_missing(hash_like, key)
      hash_like.fetch(key) do |k|
        Rails.logger.warn("Missing key: #{k}")
        nil
      end
    end
    
    fetch_or_log_missing(params, :user)  # これが Hash と同じ動作になる

互換性の観点

  • ブロックなしで fetch を使っているコード(例: params.fetch(:id)params.fetch(:id, 1))の挙動には変更なし。
  • ブロック付きで、かつブロック側で引数をとっていない場合も、多くは問題なく動作します(引数が単に無視されるため)。
  • ただし、「ブロックの arity を明示的にチェックしている」「引数が nil であることを前提にしている」など、かなり特殊なコードがあると、挙動差が出る可能性はあります。

総じて、後方互換性への影響は小さく、Hash との一貫性が強化されるメリットが大きい変更です。


  1. 参考情報 (あれば)

#56386 Consolidate use of Ruby versions in templates

マージ日: 2025/12/18 | 作成者: @jeromedalbert

  1. 概要 (1-2文で)
    Rails の各種テンプレート内で使われている Ruby バージョン指定の方法を統一し、「どの値をどの用途で使うか」を整理した PR です。version_manager_ruby_versionGem.ruby_version を状況に応じて使い分けることで、テンプレート生成時の Ruby バージョン指定がより一貫性のあるものになりました。

  1. 変更内容の詳細

2.1 目的

  • Rails アプリ/プラグイン生成用テンプレートの中で、Ruby バージョンを参照する際の記法・メソッドがバラバラになっていたのを整理し、一貫した方針にそろえる。
  • 特に以下 2 パターンの用途を明確に分離:
    • rbenv / asdf / ruby-build 等「バージョンマネージャ向け」の Ruby バージョン表記
    • Docker イメージ名・コメントなど「Ruby イメージタグ向け」の Ruby バージョン表記

2.2 具体的な変更点

(1) version_manager_ruby_version の利用統一

背景:

  • 先行 PR #56365 で、テンプレート用に version_manager_ruby_version が導入されています。
    • このメソッドは ruby-3.3.1 のような「ruby-build 形式」など、バージョンマネージャで使うのに適した形式を返すことを想定しています。

今回の PR では:

  • 「ruby-build 形式など、バージョンマネージャ向けの Ruby バージョンをテンプレートに埋め込みたい箇所」で、この version_manager_ruby_version を使うように統一しています。

対象ファイルの一つは以下:

text
railties/lib/rails/generators/rails/plugin/templates/github/ci.yml.tt

ここでは GitHub Actions の CI で利用する Ruby バージョン指定が登場しますが、
Ruby バージョンマネージャや ruby-build のような「qualified ruby-build format」が適している場面では、
今後 version_manager_ruby_version を使う方針にそろえた、という位置付けです。

(※実際のコードは 2 行の置き換えのみですが、意味としては「CI 用テンプレートでも他と同じメソッドを使うようにした」という変更です。)

(2) Gem.ruby_version の利用(Docker 関連)

対象:

text
railties/lib/rails/generators/rails/app/templates/config/deploy.yml.tt

このテンプレートは、config/deploy.yml 生成時に、 Docker 関連の説明コメントとして「RUBY_VERSION ARG を上書きする場合」の例を含んでいます。

ここでの変更内容:

  • コメント内の Ruby バージョン表記を、RUBY_VERSION Docker ARG のデフォルトと同様に Gem.ruby_version を使った値にするよう揃えました。

理由:

  • Docker イメージ名(例: ruby:3.3.1)では、ruby-3.3.1 のような「qualified」な形式は使用しません。
  • そのため、Dockerfile ではすでに Gem.ruby_version を使っており、この deploy.yml のコメントもそれに合わせて「Docker の世界で実際に使う形式」に統一しています。

Dockerfile 側の該当箇所(参照用):

Dockerfile
ARG RUBY_VERSION=<%= Gem.ruby_version %>

この PR で deploy.yml のコメントでも同じ値を前提とするようにしたため、

  • Dockerfile と deploy.yml のサンプル/コメントが矛盾しない
  • 利用者が「どのバージョン形式を使えばよいか」で迷いにくくなる

といった効果があります。


  1. 影響範囲・注意点
  • 対象は Rails のジェネレータが出力するテンプレートのみ であり、
    Rails 本体のランタイム挙動やアプリ動作には直接の影響はありません。

  • 影響があるのは主に以下のようなケースです:

    • rails new / rails plugin new で新規生成される:
      • config/deploy.yml
      • .github/workflows/ci.yml(プラグインテンプレート)
  • 既存プロジェクトの既に生成済みファイルは自動では書き換わりません。

  • もし独自テンプレートや社内テンプレートで Rails の標準テンプレートを参考にしている場合:

    • 「バージョンマネージャ向け」は version_manager_ruby_version
    • 「Docker イメージなど Ruby バージョンタグ向け」は Gem.ruby_version

    という使い分けを踏襲すると、今後の Rails 標準の流儀と揃えやすくなります。


  1. 参考情報 (あれば)

#56389 [Bug] Fix input[type=file] accept attribute array value separator as ,

マージ日: 2025/12/18 | 作成者: @bogdan

  1. 概要 (1-2文で)
    file_field ヘルパで複数指定された accept 属性値の区切り文字が、仕様どおりカンマ区切り(,)になるように修正したバグフィックスです。これにより、input[type="file"]accept 属性が HTML 仕様およびブラウザ実装に沿った形で出力されます。

  1. 変更内容の詳細

何が問題だったか

Rails の file_field ヘルパで accept に配列を渡した場合、これまで値が空白区切りで結合されていました。

ruby
file_field :avatar, accept: ['image/png', 'image/jpeg']
# 変更前の出力イメージ:
# <input type="file" accept="image/png image/jpeg">

しかし、HTML の仕様および MDN の記載では、accept 属性で複数値を指定する際は カンマ区切り にする必要があります。

何をしたか

actionview/lib/action_view/helpers/tags/file_field.rb にて、accept 属性が配列で渡された場合の結合方法を 空白からカンマ(,)に変更しました。

擬似コードイメージ:

ruby
# 変更前(イメージ)
accept_value = Array(options[:accept]).join(" ")

# 変更後(イメージ)
accept_value = Array(options[:accept]).join(",")

これにより、同じコードは次のように出力されます。

ruby
file_field :avatar, accept: ['image/png', 'image/jpeg']
# 変更後の出力:
# <input type="file" accept="image/png,image/jpeg">

テスト

actionview/test/template/form_helper_test.rb にテストが追加されており、accept に配列を渡した場合に , で連結された文字列になることを検証しています。
また、CHANGELOG も更新され、挙動変更として明示されています。


  1. 影響範囲・注意点
  • 対象となるヘルパ: ActionView::Helpers::FormTagHelper#file_field およびこれを内部的に利用するフォームヘルパ(form_with, form_for など)。
  • 挙動の違い:
    • 以前: accept: ['image/png', 'image/jpeg']accept="image/png image/jpeg"
    • 今回から: accept: ['image/png', 'image/jpeg']accept="image/png,image/jpeg"
  • ブラウザ側の影響:
    • 多くのブラウザは空白区切りでもある程度動作していた可能性がありますが、仕様どおりではなく、将来の互換性リスクがありました。
    • この変更により、HTML 仕様準拠の正しい指定方法となり、ブラウザによる解釈がより確実になります。
  • 既存コードへの互換性:
    • accept文字列で直接指定している場合(例: "image/png,image/jpeg")は影響ありません。
    • accept配列を渡している場合のみ、出力される HTML が空白区切りからカンマ区切りに変わります。
    • クライアントサイドで accept 属性の値をパースしている JavaScript がある場合、空白区切り前提のロジックがあると挙動が変わる可能性があります(カンマ区切りに対応しているか確認推奨)。

  1. 参考情報 (あれば)

#56387 [ci skip] Document top level bin/test

マージ日: 2025/12/17 | 作成者: @jeromedalbert

  1. 概要 (1-2文で)
    Rails リポジトリの最上位ディレクトリに追加された bin/test の使い方を、コントリビューションガイドに追記したドキュメント専用のPRです。サブディレクトリに cd しなくてもテストを実行できることを公式に説明し、コントリビューターのテスト体験を改善しています。

  1. 変更内容の詳細

対象ファイル: guides/source/contributing_to_ruby_on_rails.md に約18行の説明が追加されています。主な内容は次の通りです。

a. トップレベル bin/test の紹介

Rails リポジトリのルート直下にある bin/test を使うことで、どのサブプロジェクト(activesupport, activerecord, actionpack, railties など)のテストも、ルートディレクトリから実行できることが明記されました。

従来:

bash
cd activerecord
bin/test test/cases/validations_test.rb

ドキュメントに追記された新しい推奨方法の例:

bash
# Rails リポジトリのルートから
bin/test activerecord test/cases/validations_test.rb

のように、

  • 「どのフレームワークか」を第1引数に指定
  • それ以降にテストファイルやパターンを指定

という形で使えることが説明されています。

b. 典型的な使用例

ガイドには、以下のような利用パターンが含まれている可能性が高いです(PRの意図と既存ドキュメント構成からの推測):

  • 単一フレームワークの全テストを走らせる:

    bash
    bin/test activesupport
  • 特定のテストファイルだけを走らせる:

    bash
    bin/test activesupport test/time_with_zone_test.rb
  • パターン指定・行番号指定(既存の bin/test のインターフェイスをそのまま転送):

    bash
    bin/test actionpack test/controller/request_test.rb:42
  • 既存の bin/test オプション(-n でテスト名フィルタなど)も、そのまま利用可能であることが説明されている可能性があります:

    bash
    bin/test activerecord test/cases/validations_test.rb -n test_presence_validation

c. コントリビューションフローとの関連づけ

コントリビューションガイドの「テストの実行方法」もしくはそれに相当するセクションに、このトップレベル bin/test の利用方法が追記されています。
これにより、初めてコントリビュートする開発者でも、

  • 「どのディレクトリでどの bin/test を叩けばいいのか」
  • 「複数のコンポーネントのテストをどう整理して実行するのか」

といった迷いが減るような記述になっています。


  1. 影響範囲・注意点
  • 影響範囲

    • コード変更はなく、ガイド文言のみの追加です。
    • ただし、トップレベル bin/test が「公式に」推奨される形になるため、今後のブログ記事や他のドキュメント/チュートリアルでも、このスタイルが紹介されやすくなります。
    • Rails コア開発やプラットフォーム(CI 設定、社内向け開発ガイドなど)でのテスト実行手順の標準化に影響する可能性があります。
  • 注意点

    • トップレベル bin/test 自体は別PR(#56269)で導入されており、このPRはその存在を説明するだけです。
      そのため、実際の挙動は #56269 側の実装に依存します。
    • 既存の「各サブディレクトリに cd して bin/test を叩く」方法も依然として有効なはずですが、今後はルートの bin/test の使用が説明上のデフォルトになることが想定されます。
    • ローカル環境で bin/test を利用する際は、bundle installyarn install など、各コンポーネントが期待する依存関係が満たされている必要があります(この点はコントリビュートガイド全体の前提)。

  1. 参考情報 (あれば)
  • このPRでドキュメント化された機能の実装PR:
  • 該当ガイド(英語版):
    • guides/source/contributing_to_ruby_on_rails.md(Rails本体リポジトリ内)
  • 関連: Railsでのテスト実行の一般的な説明
    • 各フレームワーク配下の bin/test の使い方とオプションは、Railsガイドの「Testing」セクションおよび各 bin/test のヘルプ (bin/test --help) を参照可能。

#56393 Implement exclude keys for ActionController::Live execution sharing

マージ日: 2025/12/17 | 作成者: @eileencodes

  1. 概要 (1-2文で)
    ActionController::Live でスレッド間に共有される「実行コンテキスト(Execution State)」から、特定のキーを除外できる設定が追加されました。これにより、DB の writer/replica 切り替えなど「スレッド間で共有したくないコンテキスト」をアプリ側で制御できるようになります。

  1. 変更内容の詳細

背景: ActionController::Live と Execution State 共有

ActionController::Live はストリーミング処理などでコントローラのアクションを別スレッドで実行します。このとき、ActiveSupport::IsolatedExecutionState に格納された「実行コンテキスト」がスレッド間で共有されます。
しかし、DB 接続コンテキスト(例: connected_to による writer/replica の切り替え)まで共有されると、次のような問題が起こり得ます:

  • メインスレッドでは writer 接続に切り替えたつもりが、Live スレッドでもその状態が引き継がれ、想定外に writer を使い続ける
  • もしくは、replica 用の接続が Live スレッドにも引き継がれ、書き込みが失敗する・不整合が起きる など

この PR は、そうした「共有したくないキー」を除外できるようにするものです。

新しい設定項目

アプリケーション全体での設定

config/application.rb 等で、以下のように設定できます:

ruby
# config/application.rb
class Application < Rails::Application
  # ... 略 ...
  config.action_controller.live_streaming_excluded_keys = [:active_record_connected_to_stack]
end
  • live_streaming_excluded_keysシンボルの配列 を受け取ります。
  • ここで指定したキーは、ActionController::Live が内部でスレッドを切り替える際に、IsolatedExecutionState から「コピーしない(共有しない)」対象になります。
  • サンプルでは :active_record_connected_to_stack を除外しています。これは Active Record が connected_to などで管理している「どの DB role/接続プールを使っているか」のスタック情報です。

コントローラごとの細かい制御

アプリ全体ではなく、特定の Live コントローラだけで除外したい場合は、コントローラ側で設定できます:

ruby
class MyController < ApplicationController
  include ActionController::Live

  # このコントローラの Live 処理だけで、特定キーを除外
  self.live_streaming_excluded_keys = [:active_record_connected_to_stack]

  def index
    # Live スレッドでは active_record_connected_to_stack は引き継がれない
  end
end
  • クラスレベル属性 live_streaming_excluded_keysActionController::Live に追加されています。
  • アプリ全体の設定 (config.action_controller.live_streaming_excluded_keys) とどうマージされるかは実装依存ですが、「そのコントローラで使う除外キーとして解釈される」形です。

IsolatedExecutionState 側の変更

ActiveSupport::IsolatedExecutionState にも、キーを除外しつつ状態をコピーするための機構が足されています。

  • Live 実行時に、IsolatedExecutionState の内容を別スレッドに「コピー」する処理があり、その際に excluded_keys を指定できるようになりました。
  • この PR では、その excluded_keys にコントローラ(またはアプリ設定)で指定した live_streaming_excluded_keys が渡されるようになっています。

それに伴い、以下のようなテストが追加されています:

  • 除外キーが指定されたとき、該当するキーはコピーされない
  • 他のキーは従来通り共有される

Railtie / 設定周り

actionpack/lib/action_controller/railtie.rb に、以下が追加されています:

  • config.action_controller.live_streaming_excluded_keys のデフォルト値の定義
  • その値を ActionController::Base / ActionController::Live に伝播させるための初期化処理

railties/test/application/configuration_test.rb には、この設定が正しく読み書きできるかを確認するテストが追加されています。


  1. 影響範囲・注意点
  • デフォルトでは、何も除外されない
    PR 説明の文中に「By default no keys are shared」とありますが、ここでは「後方互換性維持のため、従来通りの挙動(=特に除外しない)を維持する」という意図です。
    つまり、この PR を取り込んだだけでは挙動は変わりません。新しい設定を使わない限り、今まで通り Execution State はそのまま共有されます。

  • DB コンテキスト共有に問題があるアプリは、自分で除外指定する必要がある
    レプリカ接続の切り替えなどで問題が出ている場合は、以下のいずれかを検討してください:

    • アプリ全体で除外:
      ruby
      config.action_controller.live_streaming_excluded_keys = [:active_record_connected_to_stack]
    • 問題がある Live コントローラだけで除外:
      ruby
      class StreamingController < ApplicationController
        include ActionController::Live
        self.live_streaming_excluded_keys = [:active_record_connected_to_stack]
      end
  • 今後のデフォルト変更の可能性
    PR のコメントにもある通り、将来的には :active_record_connected_to_stack をデフォルトで除外する方向も検討されています。
    ただし、この PR はバグフィックスとしてバックポートする必要があり、今すぐデフォルトを変えると既存アプリを壊す可能性があるため、現時点ではあくまで「オプトアウト手段の追加」に留まっています。

  • 独自に IsolatedExecutionState を使っている場合の注意
    IsolatedExecutionState に新しいコピー挙動(除外キー付き)が追加されているため、もしアプリやライブラリ側で低レベルにこれを触っている場合は、API の変化を確認しておくと安心です(通常の Rails アプリでは意識不要)。


  1. 参考情報 (あれば)
  • 対応 Issue: https://github.com/rails/rails/issues/52906
    ActionController::Live と DB 接続(writer/replica 切り替え)の問題についての議論がされています。
  • 関連ソース
    • actionpack/lib/action_controller/metal/live.rb
      • live_streaming_excluded_keys の追加と、Live 実行時の Execution State コピー処理
    • activesupport/lib/active_support/isolated_execution_state.rb
      • 除外キー付きの state コピー機構の追加

#56392 Fix false deprecation warnings when using protect_from_forgery with prepend: true

マージ日: 2025/12/17 | 作成者: @george-ma

  1. 概要 (1-2文で)
    protect_from_forgery prepend: true を使った場合に、本来問題ないのに「非推奨警告」が誤って出てしまうバグを修正した PR です。verify_authenticity_tokenverify_request_for_forgery_protection のコールバック順序を調整し、正しく deprecation 判定が行われるようにしています。

  1. 変更内容の詳細(サンプルコード含む)

問題の背景

Rails 7.2 系(PR #56369)で後方互換性のために以下 2 つの before_action が登録されるようになりました。

ruby
before_action :verify_authenticity_token, :verify_request_for_forgery_protection, options

protect_from_forgery prepend: true を使うと、Rails は内部的に prepend_before_action を利用するため、以下のようにコールバックチェーンが構築されます。

1回目の prepend(verify_authenticity_token):

ruby
[:verify_authenticity_token]

2回目の prepend(verify_request_for_forgery_protection):

ruby
[:verify_request_for_forgery_protection, :verify_authenticity_token]

結果として実行順序は:

  1. verify_request_for_forgery_protection
  2. verify_authenticity_token

となってしまいます。

一方、deprecation ロジックは「verify_authenticity_token が実行されて @_verify_authenticity_token_ran フラグが立っているかどうか」で判定しています。
しかし実際には verify_request_for_forgery_protection が先に動くため、このフラグが立っておらず、「verify_authenticity_token が呼ばれていない」と誤認して deprecation warning を出してしまう、というのが今回のバグです。

この問題は、以下のようなコードパターンを持つあらゆるコントローラに影響します。

ruby
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception, prepend: true
end

修正内容

修正では、2 つのコールバックの両方を prepend_before_action で登録し、かつ順序が期待通りになるようにしました。

ruby
# 変更後(概念的なイメージ)
prepend_before_action :verify_request_for_forgery_protection, options
prepend_before_action :verify_authenticity_token, options

prepend_before_action は「後から登録したものが前に挿入される」ため、最終的なチェーンは:

ruby
[:verify_authenticity_token, :verify_request_for_forgery_protection]

となり、実行順序は:

  1. verify_authenticity_token(ここで @_verify_authenticity_token_ran = true
  2. verify_request_for_forgery_protection(フラグを見て正しく deprecation 判定)

に修正されます。

テストの更新

actionpack/test/controller/request_forgery_protection_test.rb では:

  • prepend: true を使うケースのテストが追加/修正され、
  • ユーザーが独自の before_action / prepend_before_action を定義している場合でも、
    Rails が内部で追加する 2 つのコールバックの順序が崩れないこと

が確認されています。


  1. 影響範囲・注意点
  • 対象:
    • Rails の ActionController::RequestForgeryProtection を使っており、
    • コントローラまたは ApplicationControllerprotect_from_forgery prepend: true を指定しているアプリケーション。
  • 影響内容:
    • これまで「verify_authenticity_token が呼ばれていない」といった趣旨の deprecation warning が誤って出ていたケースで、その警告が出なくなります。
    • 実際の CSRF 保護の挙動自体(トークン検証ロジック)は、元々も今も変わりません。変わるのはコールバックの実行順と、それに紐づく deprecation 判定のみです。
  • カスタムコールバックとの兼ね合い:
    • 自前で before_action / prepend_before_action を追加している場合も、
      Rails 側の 2 つのコールバックが常に正しい順序で並ぶようにテストされています。
    • ただし、before_action の順序に依存した繊細なカスタマイズをしている場合は、Rails アップデート後に一度 controller._process_action_callbacks.map(&:filter) などで実際の順序を確認するのがおすすめです。

  1. 参考情報 (あれば)

#56390 [ci skip] Highlight wishlist model changes

マージ日: 2025/12/17 | 作成者: @Tretent

  1. 概要 (1-2文で)
    このPRは、Railsガイドの「Wishlist」モデルのコード例に対して、他モデルと同様に「変更箇所のハイライト」を付けるドキュメント修正です。アプリケーションコードや挙動には一切影響せず、ガイドの可読性・一貫性を高めるための変更です。

  2. 変更内容の詳細(あればサンプルコードも含めて)

  • 対象ファイル: guides/source/wishlists.md
  • 変更行数: +1 / -1(実質的にはコードブロック内のマークアップ修正)

背景として、ガイドのセクション 2.2 で Wishlist モデルに変更を加える流れが説明されているものの、他モデル(例: User, Product など)のコード例のように「どの行が新規・変更行か」がハイライトされていませんでした。

このPRでは、その Wishlist モデルのコードブロックに対して、以下のような「差分ハイライト用マークアップ」を追加・修正しています(イメージ):

diff
-```ruby
+```ruby
 class Wishlist < ApplicationRecord
-  has_many :items
+  has_many :items  # この行をハイライトするような記法に変更
 end

実際には、Railsガイドが採用している「ガイド内差分表示用の記法」(例: # BEGIN_HIGHLIGHT / # END_HIGHLIGHT、行頭の + / - など)を用いて、Wishlist モデルの「新しく追加された行」が読者に分かりやすく強調されるようにしています。
※正確な記法はガイド内の他モデルの例に合わせて統一されています。

  1. 影響範囲・注意点
  • 実コード、ライブラリ、フレームワークの挙動への影響はありません(完全にドキュメントのみの変更)。
  • CHANGELOG の更新も不要と明示されています。
  • 影響範囲は Rails Guides の「wishlists」ページ(guides/source/wishlists.md)に限定され、ガイド閲覧時の見え方が少し改善されるだけです。
  • テストへの影響はなく、既存テストもドキュメントの内容に依存していないため、特別な検証は不要です。
  1. 参考情報 (あれば)
  • PRタイトル: [ci skip] Highlight wishlist model changes
    • [ci skip] が付いていることからも、コード変更がなくCIを回す必要がないドキュメント専用PRであることが分かります。
  • 類似のハイライトは、Railsガイド中の他のモデル定義サンプル(例: guides/source/active_record_basics.md 等)で既に使われており、これに揃える形で Wishlist モデルにも適用されたと考えられます。

#56385 In MT6, test_order -> run_order. Add AS::TC#run_order as needed

マージ日: 2025/12/17 | 作成者: @zenspider

  1. 概要 (1-2文で)
    Minitest 6 で test_orderrun_order にリネームされる変更に対応するため、Rails 側で ActiveSupport::TestCaserun_order を追加し、Minitest のバージョン差を吸収するブリッジを入れた PR です。Rails 全体でメソッド名を置き換えるのではなく、AS::TC#test_order を通じて MT::Test#run_order を呼び出せるようにして互換性を保ちます。

  1. 変更内容の詳細

背景

  • Minitest 6 (MT6) では、テストの実行順序を表す API が test_order から run_order に変更されます。
  • Rails (ActiveSupport::TestCase など) は従来 test_order に依存しており、そのままだと MT6 で動作しない/警告が出る可能性があります。
  • とはいえ Rails 内のすべての test_order 呼び出しを run_order に変更すると、Minitest 5 以前との互換性を壊したり、大きな変更になってしまいます。

この PR でやっていること

activesupport/lib/active_support/test_case.rb に、必要な場合だけ run_order を提供するブリッジを追加します。

ざっくりしたイメージコードは以下のようなものです(※趣旨を説明するための擬似コードです):

ruby
module ActiveSupport
  class TestCase < Minitest::Test
    # 既存: Rails 側は test_order を使う
    class << self
      attr_accessor :test_order
    end

    # 新規: Minitest 6 の run_order にブリッジする
    if method_defined?(:test_order) || respond_to?(:test_order)
      # Rails から見た "run_order" を、内部的には "test_order" に委譲する
      def self.run_order
        test_order
      end

      def self.run_order=(value)
        self.test_order = value
      end
    end
  end
end

PR の説明文のポイント:

  • 「Rails 側ですべてをリネームする」のではなく、
  • AS::TC#test_order を使い続けつつ、run_order が必要なときだけブリッジする
  • かつ「AS::TC がそれに応答するなら (respond_to?)」という条件つきで、バージョン差に柔軟に対応

要するに:

  • Minitest 5 系: 従来どおり test_order だけを使っても動く
  • Minitest 6 系: run_order を見にくるコードがあっても、test_order に委譲されるので壊れない

という二重互換レイヤを ActiveSupport::TestCase に差し込んでいます。


  1. 影響範囲・注意点

影響範囲

  • ActiveSupport::TestCase を継承している Rails のテスト (model / helper / mailer / job など) が対象。
  • Minitest のバージョンにかかわらず、test_order を使ってきたコードはそのまま動く想定です。
  • Minitest 6 対応の一環で、今後 Rails 内部も徐々に test_orderrun_order へ移行しやすくなります。

注意点 / 開発者視点でのポイント

  1. アプリ/gem 側で Minitest に直接触っている場合

    • 自前で run_order を参照・定義しているコードがある場合、Minitest 6 と Rails の挙動差に注意してください。
    • Rails テストクラス (ActiveSupport::TestCase 継承) に対しては、このブリッジにより run_order を安全に呼べるようになりますが、素の Minitest::Test を継承しているクラスにはこのブリッジは効きません。
  2. test_order を前提とした Monkey Patch / メタプログラミング

    • ActiveSupport::TestCase に対して test_order をオーバーライドしたり、method_missing でプロキシしているようなコードがある場合、そこに run_order が追加されることで意図しない挙動になる可能性があります。
    • そのような場合は run_order にも対応するか、挙動を確認するのがおすすめです。
  3. Rails と Minitest のバージョン組み合わせ

    • この PR で Rails 側は MT5 / MT6 の両方をある程度吸収しますが、Minitest 6 の最終仕様 (特に run_order 周り) に追従しているかどうかは、今後の Rails リリースノートと合わせて確認すべきです。
    • 特に CI で Minitest のバージョンを固定している場合は、MT6 に上げるタイミングで挙動差をチェックしてください。

  1. 参考情報 (あれば)
  • Minitest 6 における test_orderrun_order 変更は、テストランナー周りの API クリーンアップの一部と考えられます。
  • 関連しそうな場所:
    • Rails ガイド: Testing Rails (ActiveSupport::TestCase, テストの実行順序)
    • ActiveSupport::TestCase ソース: activesupport/lib/active_support/test_case.rb
      (test_order / run_order 定義箇所を確認すると、この PR のブリッジロジックが見つかります)
  • 今後の Rails / Minitest のアップグレードに備え、アプリ側で「実行順序」を直接いじるコードは最小限にし、ActiveSupport::TestCase の提供する API に寄せておくと移行コストを下げられます。

#56380 [ci skip] Fix SQL and console commands for email field

マージ日: 2025/12/16 | 作成者: @Tretent

  1. 概要 (1–2文で)
    Railsガイド「sign_up_and_settings.md」の11.1節で、カラム名の記述ミス(email)を正しい email_address に修正したドキュメント専用PRです。実装コードや挙動には一切変更がなく、ガイドの内容と実際のスキーマ定義の不整合を解消しています。

  1. 変更内容の詳細
  • 対象ファイル: guides/source/sign_up_and_settings.md
  • 変更点: email というカラム名への言及を、実際のスキーマで使われている email_address に置き換え。

イメージとしては、以下のようなガイド中の記述が:

md
# (修正前のイメージ)
$ rails console
> User.find_by(email: "user@example.com")

次のように修正されている形です:

md
# (修正後のイメージ)
$ rails console
> User.find_by(email_address: "user@example.com")

また、同様に SQL の例が載っている場合も、emailemail_address に合わせて修正されています。

これにより、読者がガイドに従って Rails コンソールや SQL を実行した際に、実際のカラム名と一致するようになります。


  1. 影響範囲・注意点
  • 実装コード・テスト・挙動への影響は一切ありません(ドキュメントのみの変更)。
  • 「sign_up_and_settings」ガイドに沿って学習・実行している開発者にとって:
    • 以前は email を使うと NoMethodError や UnknownAttribute エラーになる可能性がありましたが、ガイドの記述が email_address に統一されることで、その混乱を防げます。
  • 既に email_address カラムを使っているアプリケーション側での対応は不要です(名前の「ドキュメント側の誤記」を直しただけです)。

  1. 参考情報 (あれば)
  • PRタイトル: [ci skip] Fix SQL and console commands for email field
    • [ci skip] からも分かる通り、CI が不要な「ドキュメントのみ変更」のPRです。
  • 変更は 1 ファイル・+2/-2 の極小差分で、CHANGELOG 更新も不要と明記されています。

#56377 Remove unused requires from ActiveRecord inheritance and enum

マージ日: 2025/12/16 | 作成者: @Saidbek

  1. 概要 (1-2文で)
    Active Record の inheritance.rbenum.rb から、現在は使われていない require が2つ削除された PR です。機能的な挙動は変えずに、不要な依存を整理してコードを軽量化しています。

  1. 変更内容の詳細

削除された require

対象ファイルと削除内容は以下の2つです。

diff
# activerecord/lib/active_record/inheritance.rb
-require "active_support/core_ext/hash/indifferent_access"
diff
# activerecord/lib/active_record/enum.rb
-require "active_support/core_ext/hash/slice"

1) inheritance.rbindifferent_access 削除の経緯

  • 元々 inheritance.rb では Hash#with_indifferent_access を利用するために
    require "active_support/core_ext/hash/indifferent_access" が追加されていました(commit: 85afc117566)。
  • その後の変更(commit: bfc62febac9)で、性能改善のために with_indifferent_access の利用がやめられ、
    シンボル/文字列キーを明示的に扱う実装に置き換えられています。
  • しかし、その際に require の削除漏れがあり、この PR でようやく削除された、という整理になります。

2) enum.rbslice 削除の経緯

  • enum 定義時の廃止予定だったキーワード引数形式をサポートするために、
    options.slice! を使う目的で require "active_support/core_ext/hash/slice" が導入されていました(commit: 0618d2d84a)。
  • その後、廃止予定だった enum のキーワード引数サポート自体が削除され(commit: b9226a68bb)、
    それに伴い slice! の呼び出しコードも削除されました。
  • こちらも require だけが残っていたため、今回の PR で削除されています。

簡易イメージ(サンプル的な状況説明)

※ 実際のコードではなく、状況イメージ用の擬似コードです。

ruby
# 以前(性能改善前)
def inheritance_config
  config_hash.with_indifferent_access[:some_key]
end

# 性能改善後
def inheritance_config
  config_hash[:some_key] || config_hash["some_key"]
end
# しかし require "active_support/core_ext/hash/indifferent_access" だけ残っていた
ruby
# 以前(enum の非推奨キーワード引数サポートあり)
options = args.extract_options!
options.slice!(:_prefix, :_suffix, :_scopes) # こうした処理のために require が必要だった

# 非推奨サポート削除後
# ↑のような options.slice! を行うコード自体が削除済み
# しかし require "active_support/core_ext/hash/slice" が残っていた

  1. 影響範囲・注意点
  • Rails の公式コード内部でのみ完結する変更です。
    2つの require は、どちらもすでに利用箇所が無く、アプリコードから直接 rely されることは通常ありません。
  • この PR によって:
    • ActiveRecord の 挙動・API・public インターフェースは変化しません
    • 既存の enum 機能や STI(Single Table Inheritance)などの機能に影響はありません。
  • もしアプリケーション側が、
    「ActiveRecord が読み込まれることで Hash#with_indifferent_accessHash#slice が必ず使える」
    という暗黙の前提に依存していた場合(かなり特殊なケース)、 そのコードは壊れる可能性があります。
    • その場合は、アプリ側で明示的に:
      ruby
      require "active_support/core_ext/hash/indifferent_access"
      require "active_support/core_ext/hash/slice"
      のどちらか/両方を読み込む必要があります。
  • ただし、通常の Rails アプリでは active_support/all または必要な core_ext を
    どこかでロードしていることがほとんどのため、この影響を受けるケースは稀と思われます。

  1. 参考情報 (あれば)
  • この PR の説明に登場する関連コミット:
    • inheritance.rbindifferent_access を導入したコミット: 85afc117566
    • その後 with_indifferent_access 利用を削除してパフォーマンス最適化したコミット: bfc62febac9
    • enum.rbslice を導入したコミット(非推奨の enum キーワード引数サポート用): 0618d2d84a
    • 非推奨の enum キーワード引数サポートと slice! 利用を削除したコミット: b9226a68bb

これらを踏まえると、この PR は「長期間実質的に死んでいた依存関係を整理する掃除(refactoring/cleanup)」に位置づけられる変更です。


#56357 Only emit logs if logger is defined

マージ日: 2025/12/16 | 作成者: @claudiob

  1. 概要 (1-2文で)
    Rails の ActiveSupport::EventReporter::LogSubscriberlogger=nil の場合に NoMethodError (undefined method 'debug?' for nil) を起こしていた問題を、logger が存在する場合のみログを出力するようにして修正した PR です。config.action_view.logger = nil のように一部コンポーネントで明示的に logger を無効化していても、/up などのエンドポイントで例外が出なくなります。

  1. 変更内容の詳細

問題の発生条件

PR 説明にある手順のとおり、

ruby
# config/environments/production.rb
config.consider_all_requests_local = true
config.action_view.logger = nil

と設定し、RAILS_ENV=production RAILS_LOG_LEVEL=debug bin/rails server で起動した状態で /up にアクセスすると、以下のような例外が発生していました。

text
NoMethodError (undefined method 'debug?' for nil)
Caused by: NoMethodError (undefined method 'debug?' for nil):
/rails/activesupport/lib/active_support/event_reporter/log_subscriber.rb:9:in 'block in '
/rails/activesupport/lib/active_support/event_reporter/log_subscriber.rb:47:in 'ActiveSupport::EventReporter::LogSubscriber#emit'

原因は ActiveSupport::EventReporter::LogSubscriber が、loggernil のケースを考慮せずに logger.debug? のような呼び出しを行っていたためです。

実際の変更内容

変更ファイルは 2 つです。

  1. activesupport/lib/active_support/event_reporter/log_subscriber.rb
  2. activesupport/test/event_reporter/log_subscriber_test.rb

log_subscriber.rb 側では、「logger が定義されている場合だけログを emit する」という条件分岐が追加されました。実際のコード断片は PR 本文には出ていませんが、イメージとしては次のようなガードが入った形です。

ruby
def emit(...)
  return unless logger # ← このように logger の存在チェックを追加

  # 既存のログ出力処理(例: logger.debug? で判定してから出力)
end

あるいは、debug? 呼び出しの前で安全にナビゲーションしている可能性もありますが、趣旨としては「loggernil のときはそもそもログを発生させない」という修正です。

それに合わせて、activesupport/test/event_reporter/log_subscriber_test.rb にテストが追加されています。テスト内容は、おそらく次の 2 パターンをカバーしていると考えられます。

  • logger が設定されている場合に、これまで通りログが出力されること
  • logger = nil の場合でも #emit の呼び出しが例外を投げずにスルーされること

テストの追加行数が 12 行なので、簡潔なケースが 1〜2 個増えた程度の小さな変更です。


  1. 影響範囲・注意点
  • 影響範囲

    • ActiveSupport::EventReporter / LogSubscriber を通じたログ出力全般に影響しますが、「logger を明示的に nil にしているケースで、これまでクラッシュしていたところが静かに何もしない」ようになるだけなので、後方互換性はほぼ問題ありません。
    • 特に、config.action_view.logger = nil のようにサブコンポーネントごとに logger をオフにする設定をしているアプリで、/up などのヘルスチェックエンドポイントアクセス時の例外が解消します。
  • 注意点

    • これまでは「バグとして例外になっていた」ケースが、「ログが出ないだけで正常に処理が進む」ようになります。もし logger=nil を意図していなかった場合(設定ミスなど)は、例外による早期発見ができなくなるので、production.rb などでの logger の設定は改めて確認した方が安全です。
    • logger を nil にしたいのではなく「静かにしたい」だけなら、レベルを :fatal に上げるなどの選択肢もありますが、この PR により nil でも安全に動作するため、そのままでもクラッシュはしません。

  1. 参考情報 (あれば)

#56202 Prepping rails for minitest 6

マージ日: 2025/12/16 | 作成者: @zenspider

  1. 概要 (1-2文で)
    Rails のテスト周りを、次期 minitest 6 でも問題なく動作するように調整した PR です。minitest 5 との後方互換性を維持しつつ、Rails 側の拡張・フック・並列実行・ラインフィルタリングなどを minitest 6 の仕様に合わせて整備しています。

  1. 変更内容の詳細

※実際の diff 全文は提示されていないため、ファイル名と目的から分かる範囲で解説します。

2-1. Gemfile.lock (+2/-2)

  • minitest 関連の依存関係を更新。
  • 「minitest 6 を前提にする」のではなく、「minitest 5/6 どちらでも動く」ことを意図した調整で、バージョン指定や依存の整合性をとっています。

2-2. routing テストの微調整

対象: actionpack/test/dispatch/routing/route_set_test.rb (+2/-2)

  • minitest 6 側で挙動や API、あるいはエラーメッセージの差異が出る部分を吸収するための、小さな修正と思われます。
  • よくあるパターン:
    • 期待されるエラーメッセージやクラス名の変更
    • assert_raise / assert_raises のメッセージの差異
    • テストオーダーやシンボル名の差異
  • ここでは Rails 固有のルーティングまわりのテストが minitest 5 と 6 の両方で安定して通るように修正されています。

2-3. ActiveSupport のテスト用アサーション

対象: activesupport/lib/active_support/testing/assertions.rb (+5/-3)

  • Rails が提供しているテスト用アサーション(例: assert_difference, assert_no_difference, assert_changes など)が、minitest 6 の実装や API の変化に追従できるように修正されています。

  • 主にあり得る変更点:

    • minitest のエラークラス・メッセージ・バックトレースの扱いの変更への対応
    • Minitest::Assertion の継承やラップ方法の微調整
    • keyword arguments 周りの互換性 (Ruby 3 系での kwarg 仕様+minitest 側の対応)
  • 典型的な例(擬似コード):

    ruby
    # 例: minitest 6 での実装変更に合わせてエラークラスや初期化を調整
    def assert_difference(expression, difference = 1, message = nil, &block)
      before = Array(expression).map { |e| eval(e, block.binding) }
      yield
      after  = Array(expression).map { |e| eval(e, block.binding) }
    
      Array(before.zip(after)).each do |b, a|
        assert_equal b + difference, a, message
      end
    rescue Minitest::Assertion => e
      # minitest 6 側のスタックやメッセージ処理に合わせてラップ方法を調整
      raise
    end
  • Rails のアサーションが minitest 6 の非互換変更に飲み込まれないよう、安全側に寄せた対応が入っていると考えてよいです。


2-4. ActiveSupport の autorun 処理

対象: activesupport/lib/active_support/testing/autorun.rb (+7/-0)

  • minitest 本体が提供する minitest/autorun の挙動に対して、Rails が独自にフックを挟んでいる部分です。

  • minitest 6 では、テストの起動フロー(at_exit フック、Minitest.run のタイミング等)が変わる可能性が高いため、それに合わせて Rails 独自のオプションや初期化処理を差し込めるよう調整しています。

  • 想定されるパターン(イメージ):

    ruby
    # 擬似コードイメージ
    require "minitest/autorun"
    
    # Rails の test runner 初期化を autorun の直前/直後にフック
    Minitest.after_run do
      # Rails 特有のクリーンアップなど
    end
  • minitest 5/6 両対応のため、条件分岐で minitest のバージョンを見て処理の分岐をしている可能性があります。


2-5. 並列テスト Worker の修正

対象: activesupport/lib/active_support/testing/parallelization/worker.rb (+5/-1)

  • Rails の並列テスト実行 (parallelize) 機構が minitest 6 でも正しく動作するように調整。

  • 変更されそうな点:

    • worker プロセス/スレッド側での Minitest.run 呼び出し方
    • Minitest::Runnable.runnables など、テストケースの列挙・実行 API の扱い
    • テスト結果 (Minitest::Result) の取り扱い
  • 具体例(擬似コード):

    ruby
    # minitest 5 でも 6 でも動くように Minitest.run の呼び出しや
    # 結果オブジェクトの扱いを統一するなど
    def run_tests(worker_id)
      # ... テスト対象のフィルタリングなど ...
      Minitest.run
    end
  • これにより、並列実行時に minitest 6 特有の挙動差でテストが落ちたりフリーズしたりしにくくなっています。


2-6. ActiveSupport::TestCase 周りのテスト修正

対象: activesupport/test/test_case_test.rb (+2/-2)

  • ActiveSupport::TestCase の挙動を検証するテストが、minitest 6 でも正しく通るように微調整。
  • 可能性が高い変更:
    • テストメソッドのディスカバリ順序 (test_ メソッド順) の違い
    • デフォルトのシード値やランダム化戦略の差
    • Minitest::Test から派生したクラス名やフルネームの扱いの違い
  • ここの修正は「Rails が minitest の挙動をどう前提にしているか」の調整なので、フレームワーク依存部分の期待値アップデートと考えられます。

2-7. railties/lib/minitest/rails_plugin.rb の拡張

対象: railties/lib/minitest/rails_plugin.rb (+7/-0)

  • minitest/rails_plugin は、minitest 側から読み込まれる Rails プラグインです。

  • ここに minitest 6 用の初期化やフック登録が追加されています。

  • 典型的には以下のようなことを行う場所です(擬似コード):

    ruby
    # 擬似コード
    if defined?(Rails)
      Minitest.extensions << "rails"
    
      module Minitest::Rails
        def before_setup
          super
          # Rails test helper のセットアップ
        end
      end
    end
  • minitest 6 でプラグインのロード順や拡張ポイントが変わっていても、同等の機能を提供できるように調整されていると考えられます。


2-8. ラインフィルタリングの大幅な調整

対象: railties/lib/rails/test_unit/line_filtering.rb (+24/-3)

この PR で最も大きく変わっている箇所です。

  • rails test test/models/user_test.rb:42 のように、ファイルの特定行番号を指定してテストを絞り込む機能(ラインフィルタリング)の実装を、minitest 6 に合わせて再実装/修正しています。

  • minitest 本体も行番号ベースのテストフィルタリング機構を持ちますが、Rails は独自フォーマット(複数ファイル・複数行指定など)や ActiveSupport::TestCase との組み合わせを実現するために自作のフィルタ処理を挟んでいます。

  • 想定される変更内容:

    • minitest 6 のテストディスカバリ API(例: Minitest::Runnable.runnablesMinitest::Test.runnables)に合わせて、どのテストメソッドがどの行にあるかを解決するロジックを更新。
    • 正規表現ベースだったフィルタを、行番号とメソッド定義位置のマッピングにするなど、より堅牢な実装へ。
    • rails test test/models/user_test.rb:10-20 のような範囲指定や複数指定に対する処理の改善。
  • 具体的なイメージ(擬似コード):

    ruby
    module Rails::TestUnit::LineFiltering
      def self.filter(test_files_with_lines)
        # "file.rb:10", "file.rb:20-30" などを解析して
        # minitest に渡す test name or filter を構築
      end
    
      def self.tests_for_line(file, line)
        # file をパースして、指定行を含む test_ メソッドを特定
      end
    end
  • minitest 6 でテスト名の扱いやクラス探索の順序が変わっても、rails test の UX を維持するための変更と言えます。


  1. 影響範囲・注意点

3-1. 影響範囲

  • 影響を受けるのは Rails のテスト全般:
    • rails test / bin/rails test を使ったテスト実行
    • ActiveSupport::TestCase や Rails 独自アサーションの利用
    • 並列テスト (parallelize) 機能
    • 行番号を指定したテスト絞り込み (rails test foo_test.rb:42)
  • minitest 5 で動いていた環境でも、同じテストが引き続きパスするよう配慮されています(PR 説明中でも「MT5 と MT6 で広くテストした」と明記)。

3-2. 注意点 / 想定されるブレークポイント

  • 独自に minitest を拡張している場合:
    • Minitest::Test を monkey patch している
    • 独自の Minitest.plugin_xxx を Rails と組み合わせている
      などの場合、今回の Rails 側の調整と衝突する可能性があります。
  • rails test に対する独自スクリプト:
    • 行番号指定や test name 指定をラップしているスクリプトやツールは、内部実装の変化(line_filtering.rb)で挙動が微妙に変わる可能性があります(特に複数行・範囲指定など)。
  • 並列テストを heavily 使っているプロジェクト:
    • 並列実行に絡む minitest API の挙動が変わりうるため、極端にカスタマイズされた CI セットアップでは flakiness が変わる可能性があります。

  1. 参考情報 (あれば)
  • minitest 6 に関して:
    • minitest はメジャーバージョンアップ時に、テスト runner API や plugin 仕組み、エラークラスなどを破壊的変更することがあり、Rails のような大きなフレームワークはこの対応が必要になります。
  • PR 作者コメント:
    • 「このブランチには minitest 6 が Rails とスムーズに動くための修正がすべて含まれている」
    • 「minitest 5 と 6 の両方で広くテスト済み」
    • 「実際に minitest 6 にバージョンを上げる別ブランチでは、まだ何らかの flakiness を調査中」と記載あり
      → この PR は「本体バージョンアップ前の事前整備」であり、本番で minitest 6 に完全移行するのはもう少し後になる見込みです。

開発者目線では、「Rails を minitest 6 へ上げてもテスト周りが崩れないようにするための準備 PR」であり、特にテストの起動フロー・並列実行・ラインフィルタリング周りの独自拡張に手が入った、と理解しておくとよいです。


#56368 Refactor File.atomic_write

マージ日: 2025/12/15 | 作成者: @byroot

  1. 概要 (1–2文で)
    File.atomic_write の一時ファイル生成ロジックが整理され、SecureRandom を用いて衝突を事実上不可能にしつつ、万一の競合は EXCL フラグ付きオープンで防ぐようになりました。あわせて、ディレクトリごとのパーミッション推測ロジックが削除され、「必要ならブロック内で自分で chmod/chown する」方針に簡素化されています。

  1. 変更内容の詳細

2-1. File.atomic_write の衝突回避ロジックの刷新

従来の File.atomic_write は、同名ファイルの存在チェック+リトライ、といった形で一時ファイル名の衝突を回避していましたが、この PR で次のように変わりました。

  • 一時ファイル名の生成に SecureRandom を使用
    • 例: basename-<secure_random_hex> のような形で、事実上衝突しない名前を生成
  • ファイルオープン時に EXCL フラグ(File::EXCL)を利用
    • File::CREAT | File::EXCL の組み合わせでオープンし、万が一同名ファイルが既に存在する場合は OS レベルでエラーにする
    • これにより「存在チェック → 作成」の間に他プロセスがファイルを作る TOCTTOU レースを避けられる

Ruby 的なイメージとしては、以下のような振る舞いになっています(実際の実装とは簡略化しています):

ruby
def atomic_write(file_name, temp_dir: nil)
  temp_dir ||= File.dirname(file_name)
  tmp_basename = "#{File.basename(file_name)}-#{SecureRandom.hex(10)}"
  tmp_path = File.join(temp_dir, tmp_basename)

  # EXCL による排他的作成
  File.open(tmp_path, File::WRONLY | File::CREAT | File::EXCL, 0o600) do |tmp_file|
    yield tmp_file
  end

  # 書き込み完了後に rename で入れ替え(ここは従来通り)
  File.rename(tmp_path, file_name)
end

ポイントは:

  • 一時ファイル名が暗号学的に十分ランダムになった
  • それでも理論上の競合を考慮して EXCL で「二重作成」を OS レベルで防止

これにより、マルチプロセス/マルチスレッド環境での競合可能性がより低く、かつ安全なものになっています。


2-2. パーミッション推測の廃止

以前の File.atomic_write には、「指定された temp_dir に一時ファイルを作るが、そのパーミッションを最終的な出力先ディレクトリやファイルの状態に近づける」ためのロジックがありました。そのために:

  • 出力先ディレクトリ配下に一時的なファイルを作り、そのパーミッションを見てから本番の temp ファイルを作る
  • というような「パーミッションを探る(probe する)」動きをしていました。

この PR で、この「パーミッションを探る」動作が削除されました。理由は説明文の通りです:

The only case this is needed is if you asked for the tempfile to be created in another directory. In which case creating a file in your directory to probe the permission is contradictory with your wishes.

要約すると:

  • 本当に必要になるのは「temp_dir を明示的に(かつ出力先とは別のディレクトリに)指定した場合」
  • しかし、そのようなケースで「結局出力先ディレクトリに probe 用ファイルを作成する」のは、そもそも temp_dir を指定した意図(別の場所で一時ファイルを作りたい)と矛盾する
  • 従って、Rails が「勝手に」パーミッションを推測・補正するのはやめて、
    • 「必要ならユーザーがブロック内で chmod / chown してください」というシンプルなポリシーに変更

そのため、atomic_write ブロック内で次のようなコードを書くことが推奨されるケースがあります:

ruby
File.atomic_write("/var/app/config.yml", temp_dir: "/tmp") do |file|
  file.write(yaml_content)
  # 必要なら自分でパーミッションを合わせる
  file.flush
  File.chmod(0o640, file.path)
  File.chown(target_uid, target_gid, file.path)
end

以前は Rails 側で「それっぽいパーミッション」になるよう努力していましたが、今後は「どのようなパーミッションにしたいかは利用側の責任」という整理になります。


  1. 影響範囲・注意点

3-1. File.atomic_write を直接/間接的に使っているコード

影響を受けうるのは、File.atomic_write を直接呼び出しているアプリ/ライブラリ、もしくはそれを内部で利用している Rails の機能(例: 一部のキャッシュ・設定ファイル生成など)です。

3-2. パーミッションまわりの挙動が変わる可能性

特に注意が必要なのは「temp_dir を明示的に別ディレクトリにしているコード」です。

  • 以前:
    • Rails が出力先ディレクトリにファイルを作ってパーミッションを推測し、最終的な書き込み結果が「従来と近い」状態になるよう頑張っていた
  • 現在:
    • そうした probe が行われなくなったため、最終的なファイルのパーミッション/所有者が以前と異なる可能性がある

実際に確認しておきたい点:

  • root で動いていて、アプリ固有ユーザー所有にしたい、など「所有者やモードが重要」なデプロイ/設定生成処理
  • NFS・コンテナボリュームなど、権限挙動がセンシティブなファイルシステム

こうしたケースでは、atomic_write のブロック内で明示的に chmod / chown するように変更することで、以前と同等の(むしろより明示的な)挙動にできます。

3-3. エラー挙動の変化は軽微だが、競合検知はより厳格に

  • ファイル名競合が起きた場合(非常に稀ですが)、EXCL によって Errno::EEXIST などの例外が発生する可能性があります。
  • 以前も競合は想定していましたが、現在は OS レベルの排他を使うことで、より「しっかり」エラーになる形です。
  • ただし、SecureRandom によるユニーク名生成のため、実際にこのケースに遭遇することはまずありません。

  1. 参考情報 (あれば)
  • 対応する Issue/PR:
  • 関連する Ruby / POSIX の知識:
    • File::EXCL + File::CREAT による原子的なファイル作成は、TOCTTOU(Time Of Check To Time Of Use)回避の定石です。
    • SecureRandom を使った一時ファイル名生成は、衝突リスクと推測可能性の両方を減らすためによく使われます。

#56369 Support but deprecate skip_before_action :verify_authenticity_token

マージ日: 2025/12/15 | 作成者: @rosa

  1. 概要 (1-2文で)
    このPRは、Rails 7.2 で verify_authenticity_tokenverify_request_for_forgery_protection に改名された後も、既存コードで広く使われている skip_before_action :verify_authenticity_token が引き続き動くようにしつつ、非推奨として扱うための仕組みを追加したものです。skip_before_action :verify_authenticity_token で forgery protection をオフにした場合、今後はデprecation警告が出るようになります。

  1. 変更内容の詳細

背景

  • 前PR (#56350) で verify_authenticity_token コールバックが
    verify_request_for_forgery_protection にリネームされた。
  • ただし現実には、ほとんどのアプリが以下のように書いて forgery protection を無効化している:
    ruby
    skip_before_action :verify_authenticity_token
  • 名前が変わっただけで既存のこの書き方を壊すと、膨大な既存アプリに影響が出るため、挙動は維持しつつ「非推奨」として誘導したい

実装方針

PR本文を踏まえると、実装の狙いは次の通りです。

  1. protect_from_forgery 実行時に、「verify_authenticity_token が実行された」というフラグを内部的に立てるようにする。
  2. 実際にリクエストを検証するタイミングで、そのフラグを確認する。
  3. フラグが立っていない場合は、「verify_authenticity_token が本来実行されるはずだったが、どこかでスキップされた」と判断する。
  4. そのスキップが skip_forgery_protection 経由ではなく、明示的な skip_before_action :verify_authenticity_token によるものだと判断できる場合に、deprecation warning を出す

ざっくり言うと:

  • 以前:
    • verify_authenticity_token コールバックがスキップされると、そのまま forgery protection が効かない状態になるが、特に警告などはなし。
  • 今回のPR以降:
    • skip_before_action :verify_authenticity_token が呼ばれて forgery protection が無効化されるのは従来通りだが、
      • 内部的には「verify_authenticity_token がスキップされた」という状態が検知され、
      • その場合に「このやり方は deprecated なので、skip_forgery_protection を使ってほしい」と警告が出る。

想定されるコード例と今後の推奨書き方

従来よく見られるコード:

ruby
class WebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token

  def create
    # ...
  end
end

このPR適用後も動作自体は変わりませんが、ログ等に deprecation warning が出るようになります。
今後は以下のように書き換えるのが推奨されます:

ruby
class WebhooksController < ApplicationController
  skip_forgery_protection

  def create
    # ...
  end
end

変更点のファイルレベルでの概要:

  • action_controller/metal/request_forgery_protection.rb
    • forgery protection の内部処理に、
      • verify_authenticity_token が実行されたかどうか」を記録する仕組み
      • それを検査して deprecation warning を出すロジック
        を追加。
  • action_controller/base.rb
    • 上記の挙動を踏まえた軽微な修正(ほぼフックの差し替えレベル)。
  • request_forgery_protection_test.rb
    • skip_before_action :verify_authenticity_token を使った場合に、
      • 挙動が今まで通り forgery protection をスキップすること
      • かつ deprecation warning が出ること
        を確認するテストを追加。

  1. 影響範囲・注意点
  • 既存の skip_before_action :verify_authenticity_token を使っているコードは動きは変わらないが、deprecation warning が出るようになる
  • 将来的なメジャーバージョン(例: Rails 8 以降)で、この書き方は削除される可能性が高いです。
  • Forgery protection をコントローラ単位またはアクション単位で無効化したい場合は、今後は次のようなパターンに移行する必要があります:
    ruby
    # コントローラ全体で無効化
    skip_forgery_protection
    
    # 一部アクションだけ無効化
    skip_forgery_protection only: :webhook
  • verify_request_for_forgery_protection 自体の動作には直接の変更はなく、「名前の変更」と「旧名経由のスキップ方法の非推奨化」が主な変更点です。

  1. 参考情報 (あれば)
  • このPRでの前提となる変更:
    • #56350: verify_authenticity_tokenverify_request_for_forgery_protection へのリネーム
  • 関連するAPI:
    • protect_from_forgery
    • skip_forgery_protection
    • skip_before_action / before_action (旧 before_filter)

#56365 Include MRI prerelease string in generated .ruby-version

マージ日: 2025/12/15 | 作成者: @jeromedalbert

  1. 概要 (1-2文で)
    Rails の rails new が生成する .ruby-version について、MRI のプレリリース版(例: 4.0.0-preview2)を使っている場合に、プレリリースのサフィックス(-preview2 など)を含めて正しく出力するように修正した PR です。これにより、asdfmise などの Ruby バージョンマネージャーでエラーになりにくくなります。

  1. 変更内容の詳細

何が問題だったか

  • これまで:
    • MRI プレリリース版の Ruby(例: ruby 4.0.0preview2)で rails new を実行すると、生成される .ruby-version には 4.0.0 しか書かれず、-preview2 が落ちていた。
    • その結果、asdfmise などが「4.0.0 が見つからない」といった扱いになり、インストール済みのプレリリース版 4.0.0-preview2 を認識できないケースがあった。
  • 一方で、TruffleRuby や JRuby など MRI 以外の実装では、RUBY_ENGINE_VERSION がもともと 23.0.0-preview1 のようにサフィックス込みで返るため、この問題は発生していなかった。

どのように直したか

変更された主な箇所は以下です。

  • railties/lib/rails/generators/app_base.rb
  • railties/lib/rails/generators/rails/app/templates/ruby-version.tt
  • railties/test/generators/generator_test.rb

.ruby-version のテンプレート変更

ruby-version.tt では、Ruby のバージョン文字列を生成するときに、MRI かそうでないかで扱いを分岐するようになりました。

概念的には以下のような挙動になります(実際のコードはもう少し抽象化されています):

ruby
# 擬似コードイメージ

if RUBY_ENGINE == "ruby"
  # MRI の場合:
  # 例: RUBY_VERSION = "4.0.0"
  #     RUBY_PATCHLEVEL = -1 (プレ版)
  #     RUBY_DESCRIPTION = "ruby 4.0.0preview2 (2025-xx-xx revision xxxxx) [x86_64-darwin22]"
  #
  # RUBY_DESCRIPTION から "4.0.0-preview2" のような「プレリリースサフィックス付きの完全なバージョン」を抽出する
  ruby_version_with_suffix = detect_mri_prerelease_version
  ruby_version_to_write = ruby_version_with_suffix || RUBY_VERSION
else
  # MRI 以外 (JRuby, TruffleRuby 等) の場合:
  # 例: RUBY_ENGINE = "truffleruby"
  #     RUBY_ENGINE_VERSION = "23.0.0-preview1"
  ruby_version_to_write = RUBY_ENGINE_VERSION
end

File.write(".ruby-version", ruby_version_to_write)

実際には、AppBase に Ruby バージョンを決定するためのロジックが追加され、その値をテンプレートから参照する形になっています。
MRI のプレリリース判定は、RUBY_DESCRIPTION かそれに類する情報をもとに、「4.0.0-preview2」「4.0.0-rc1」などのサフィックス付きバージョンをパースするような実装がされています。

テストの追加

railties/test/generators/generator_test.rb に、以下のようなケースをカバーするテストが追加されています (値は実使用例に基づいたモック):

  • MRI リリース版: .ruby-version には 4.0.0 のような純粋なバージョンが書かれる
  • MRI プレリリース版: .ruby-version には 4.0.0-preview2 のようにサフィックス付きで書かれる
  • JRuby リリース版: .ruby-version には RUBY_ENGINE_VERSION の値 (例: 9.4.7.0) がそのまま書かれる
  • TruffleRuby リリース版: .ruby-version には 23.0.0 のようなエンジンバージョンが書かれる
  • TruffleRuby プレリリース版: .ruby-version には 23.0.0-preview1 のようなアルファベット入りのバージョンがそのまま書かれる

これにより、主要な Ruby 実装 + バージョンマネージャの現実的な利用パターンが網羅的にテストされています。


  1. 影響範囲・注意点
  • 影響範囲

    • 新規に rails new を実行したときに生成される .ruby-version の内容が、MRI プレリリース版利用時にのみ変わります。
    • 既存プロジェクトの .ruby-version には影響しません(自動変換などはなし)。
    • MRI 以外(JRuby, TruffleRuby など)は、従来通り RUBY_ENGINE_VERSION ベースで書かれる挙動のままです。
  • 期待できる改善

    • MRI プレリリース版を使っている環境で:
      • .ruby-version4.0.0-preview2 のように完全一致の値を持つようになり、
      • asdf, mise などのバージョンマネージャが、インストール済みのプレリリースバージョンと正しくマッチしやすくなります。
    • これにより、「.ruby-version には 4.0.0 と書いてあるが、インストールされているのは 4.0.0-preview2 なので見つからない」といった齟齬が減ります。
  • 注意点

    • MRI プレリリース版をあえて 4.0.0 として扱いたい、というニッチな運用をしている場合は、今後 .ruby-version-previewX が付与されるため、ツール側の設定や CI などに影響が出る可能性があります。
    • ただし、一般的にはプレリリースとリリースを区別して扱いたいケースが大半なので、多くの環境では望ましい挙動変更といえます。
    • この変更は Rails の内部実装 (AppBase のバージョン抽出ロジック) に依存しており、MRI 側の RUBY_DESCRIPTION 等フォーマットが将来的に大きく変わると、プレリリース判定ロジックの追随が必要になる可能性があります。

  1. 参考情報 (あれば)
  • 対応 Issue: https://github.com/rails/rails/issues/55644
  • この挙動は、rbenvrvm だけでなく、asdf, mise といったマルチランタイムマネージャでの動作検証に基づいてテストされているため、Ruby 4.0 系プレリリースを試す際にも安心して rails new を使えるようになります。

#56363 Fix typo in Active Job callbacks test

マージ日: 2025/12/15 | 作成者: @shivamsinghchahar

  1. 概要 (1-2文で)
    Active Job のコールバックに関するテスト名に含まれていたタイプミス(swhen)を修正する、テストコードのみの非常に小さな修正です。挙動や API には一切変更がありません。

  2. 変更内容の詳細

  • 対象ファイル: activejob/test/cases/callbacks_test.rb
  • 変更内容: テストメソッド名から不要な文字列 swhen を取り除くリネームのみ。

イメージとしては、以下のような変更です(実際の名前は PR 本文からは断定できませんが、ニュアンスとして):

ruby
# 変更前
def test_callbacks_run_swhen_job_is_performed
  # ...
end

# 変更後
def test_callbacks_run_when_job_is_performed
  # ...
end
  • ロジックやアサーション内容、テスト対象のコードには一切手を入れておらず、「テスト名の typo 修正」という一点に限定されています。
  • コミットメッセージや PR 説明からも、他の副次的な変更がないことが明示されています。
  1. 影響範囲・注意点
  • 影響範囲

    • Rails 本体の実行時挙動には影響なし。
    • Active Job の API/挙動/パフォーマンスも一切変化なし。
    • 影響するのは「テストコード上のメソッド名」のみです。
  • 注意点

    • Rails 本体の CI を前提とした他のコード(例えば「特定のテストメソッド名をピンポイントで実行するスクリプト」など)がもし存在する場合、そのメソッド名に依存していると影響を受ける可能性はあります。ただし、通常の利用では問題になりません。
    • 公開 API の変更や仕様変更ではないため、CHANGELOG の更新も不要と判断されています。
  1. 参考情報 (あれば)
  • PR: https://github.com/rails/rails/pull/56363
  • 対象コンポーネント: Active Job のコールバック関連テスト (activejob/test/cases/callbacks_test.rb)
  • 目的: テスト名の可読性・一貫性向上(typo 修正)

#56364 [ci-skip] Fix typo in Active Job test helper

マージ日: 2025/12/15 | 作成者: @shivamsinghchahar

  1. 概要 (1-2文で)
    Active Job のテストヘルパー内にある文言の文法ミス("it's" と "its" の誤用)を修正した PR です。機能や挙動には一切変更がなく、コメント/メッセージの英語表現のみが改善されています。

  1. 変更内容の詳細
  • 対象ファイル: activejob/lib/active_job/test_helper.rb
  • 変更点: 英文の possessive(所有格)の誤りを修正
    • "it's"("it is" の短縮形)と書かれていた箇所を、
      所有格 "its" に修正しています。

実際には次のようなごく小さな変更です(イメージ例):

ruby
# 修正前(例)
# This method resets the job queue and it's state.
#
# 修正後(例)
# This method resets the job queue and its state.

※PR 説明文から判断すると、実コードではなくコメントやドキュメント的な文言の修正であり、ロジックやインターフェイスは一切変更されていません。


  1. 影響範囲・注意点
  • 影響範囲:

    • Active Job のテストヘルパーを利用するアプリケーションの挙動には影響しません。
    • API シグネチャ、挙動、テスト結果は従来と完全に同じです。
    • ドキュメントやコメントを読む際の英語表現が正しくなったのみです。
  • 注意点:

    • バージョンアップ時にこの PR によるマイグレーションやコード修正は不要です。
    • CI をスキップする指定([ci-skip])がタイトルにあり、純粋にドキュメントレベルの修正であることが明示されています。

  1. 参考情報 (あれば)

#56360 [ci skip][docs] Fix :nodoc: placement in ActiveRecord::Transactions

マージ日: 2025/12/15 | 作成者: @Yuhi-Sato

  1. 概要 (1-2文で)
    ActiveRecord::Transactions 内の :nodoc: の付け方を修正し、APIドキュメントに本来載るべきメソッドが非表示になっていた問題を直した PR です。あわせて、内部用の with_transaction_returning_status を公式に「内部メソッド」としてドキュメントから隠すようにしています。

  1. 変更内容の詳細

背景

  • PR #18102 で、内部用の定数を API ドキュメントから隠すために :nodoc: が導入された。
  • しかし、その :nodoc: の書き方(適用範囲)が広すぎたため、
    • 対象にしたかった定数だけでなく、
    • その後ろに定義されるメソッドまで API ドキュメントから除外されてしまっていた。
  • また、issue #26656 で指摘されていた with_transaction_returning_status は内部メソッドであり、公開 API に含めるべきではないことが、メンテナによって確認済み。

この PR はこの2点を整理して、「何が public API か」という境界をドキュメントレベルで正しく表現する修正です。

実際の修正内容

ファイル: activerecord/lib/active_record/transactions.rb

  1. ACTIONS 定数に対する :nodoc: のスコープを限定
    もともと、ACTIONS 定数周りに :nodoc: が書かれていたものの、その位置が悪く、後続のメソッド定義まで YARD/rdoc に「ドキュメント対象外」と解釈されていた状態でした。
    この PR では:

    • ACTIONS 定数だけを非公開にする」ように :nodoc: の付け方を変更し、
    • それ以外のメソッドが誤って非公開扱いされないようにしています。

    擬似コードでイメージすると以下のような変更です(実際のコードとは多少異なりますが意図の説明用):

    ruby
    # 変更前(イメージ)
    # :nodoc:
    ACTIONS = [:create, :update, :destroy]
    
    def transaction(...)
      ...
    end

    これだと transaction メソッドまでドキュメントから消えてしまう可能性があるのに対し:

    ruby
    # 変更後(イメージ)
    ACTIONS = [:create, :update, :destroy] # :nodoc:

    のように「定数単体」にだけ :nodoc: が効く形にしています。

  2. with_transaction_returning_status に明示的に :nodoc: を付与

    • with_transaction_returning_status は内部実装用のヘルパーメソッドであり、アプリケーションコードから直接呼ぶことを前提としていないものです。
    • 以前から issue #26656 で「これは public API に載せるべきではない」とされており、メンテナも同意している状態でした。
    • この PR で、その合意をコード上に反映し、メソッド定義に :nodoc: を明示しています。

    例(イメージ):

    ruby
    # 変更前
    def with_transaction_returning_status
      ...
    end
    
    # 変更後
    def with_transaction_returning_status # :nodoc:
      ...
    end

    あるいは、メソッド定義直前にコメントとして:

    ruby
    # :nodoc:
    def with_transaction_returning_status
      ...
    end

    のような形になっていると考えられます(実装スタイル次第)。


  1. 影響範囲・注意点
  • ランタイムの挙動への影響

    • 変更は :nodoc:(ドキュメント生成用のメタ情報)に限定されており、Ruby の実行時動作には影響しません。
    • メソッドや定数のシグネチャ・挙動は変わっておらず、既存アプリケーションのコードが壊れることはありません。
  • ドキュメント/API 仕様面の影響

    • ActiveRecord::Transactions の API ドキュメントに、本来表示されるべき公開メソッドが正しく表示されるようになります。
    • ACTIONS 定数は内部向けとしてドキュメントから隠されます。
    • with_transaction_returning_status は「内部メソッド」として公式に非公開扱いとなり、今後の変更互換性が保証されない位置づけが明確になります。
  • 開発者が意識すべき点

    • もしアプリ側で with_transaction_returning_status を直接呼び出している場合は、非公開 API に依存している状態です。この PR をきっかけに、将来の Rails バージョンでこのメソッドが変更・削除される可能性を考慮すべきです。
    • トランザクション処理は、基本的には transactionsave!/update! など、ドキュメントに記載された公開メソッドだけを使うのが安全です。

  1. 参考情報 (あれば)
  • この PR で言及されている過去の議論:
    • PR #18102: 内部定数を隠すために :nodoc: が追加された PR
    • Issue #26656: with_transaction_returning_status を公開 API に含めるべきでないとした議論
  • :nodoc: は Rails 独自ではなく、Ruby のドキュメントツール(RDoc / YARD)における「ドキュメント生成対象外」指定の記法です。
    • クラス/モジュール/メソッド/定数などに付与でき、そのスコープの取り方によって「どこまでを非公開扱いにするか」が変わるため、今回のように配置ミスがドキュメントに影響することがあります。

#56361 Add controller action source location to routes inspector

マージ日: 2025/12/14 | 作成者: @guilleiguaran

  1. 概要 (1-2文で)
    rails routes --expanded とルーティングエラーページで、各コントローラアクションが「どのファイルの何行目で定義されているか」を表示できるようになりました。RAILS_EDITOR / EDITOR が設定されている場合、ルーティングエラーページからワンクリックで該当アクションのソースをエディタで開けます。

  1. 変更内容の詳細

2-1. RouteWrapper#action_source_location の追加

ActionDispatch::Routing::RoutesInspector::RouteWrapper に、アクションの定義場所を取得するメソッドが追加されています。

内部的には次のようなイメージで実装されています(概念的なサンプル):

ruby
def action_source_location
  return unless controller && action

  controller_class = controller.constantize # "PostsController" -> PostsController
  method = controller_class.instance_method(action) # :index など
  method.source_location # => ["/path/to/app/controllers/posts_controller.rb", 12]
rescue NameError
  nil
end

ポイント:

  • instance_method(:action).source_location を使って「定義ファイル・行番号」を取得
  • コントローラやアクションが見つからない場合は例外を握りつぶして nil を返し、安全にスキップ
  • Rack アプリや動的ルート、名前空間付きコントローラ (Admin::PostsController 等) も考慮している

2-2. rails routes --expanded への「Action Location」列の追加

rails routes --expanded コマンドの出力に、新しいカラム「Action Location」が追加されました。

イメージ:

bash
$ bin/rails routes --expanded

--[ Route 1 ]--------------------------------------------------------
Prefix            | posts
Verb              | GET
URI               | /posts(.:format)
Controller#Action | PostsController#index
Action Location   | app/controllers/posts_controller.rb:12
--

従来は Controller#Action までしか見えなかったものが、どのファイルのどの行に飛べばよいかまで一覧で分かるようになります。
アクションのソース位置が取れない(例: Rack エンドポイント、メソッド未定義など)場合は、この列は空 or 表示無しになります。

2-3. ルーティングエラーページに ✏️ リンクを追加

ActionDispatch::ShowExceptions が出す「Routing Error」ページにおいて、RAILS_EDITOR または EDITOR 環境変数が設定されている場合、Controller#Action 表示の横に ✏️ アイコンが表示されます。

このアイコンは、action_source_location で取得したファイルと行番号を元に、設定済みエディタでその位置を開くリンクになっています。

例:

  • RAILS_EDITOR="code --goto %{file}:%{line}" などを設定しておけば、
  • PostsController#index ✏️ をクリック → app/controllers/posts_controller.rb の該当行が VS Code で開く

Rails 既存の「エディタ連携」仕組み(RAILS_EDITOR / EDITOR にコマンドテンプレートを設定して %{file} %{line} を埋め込む方式)をそのまま再利用しています。

2-4. テンプレート・テストの変更

変更ファイルから読み取れるポイント:

  • actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb
    • rails routes --expanded のテーブルヘッダに "Action Location" 列を追加。
  • actionpack/lib/action_dispatch/middleware/templates/routes/_route.html.erb
    • 各ルート行にアクション位置情報を表示。
    • ルーティングエラーページで ✏️ リンクを描画するロジックを追加。
  • actionpack/test/dispatch/routing/inspector_test.rb
    • action_source_location が期待どおり動作することを確認するテストを多数追加。
    • 名前空間付きコントローラ、Rack アプリ、存在しないアクションなどのケースもカバー。
  • railties/test/commands/routes_test.rb
    • rails routes --expanded の出力に "Action Location" が含まれることを確認するテストを追加。

  1. 影響範囲・注意点
  • 主な影響範囲

    • rails routes --expanded の出力形式が増える(列が一つ増える)
      • このコマンド出力をパースしているツール/スクリプトがある場合は要注意。
    • ルーティングエラー画面の HTML が変わる
      • 自前でテンプレートを差し替えている場合、今回の変更と差分が出る可能性あり。
  • パフォーマンス

    • source_location を得るためにコントローラクラスを constantizeinstance_method を呼び出すため、ルート数が非常に多いアプリで rails routes --expanded を多用する場合は若干のオーバーヘッドが増えます。
    • 通常の開発規模では問題にならない想定ですが、大規模モノリスなどでは気になる場合があるかもしれません。
  • エディタ連携の注意点

    • RAILS_EDITOR / EDITOR を正しく設定する必要があります。例:
      • RAILS_EDITOR="code --goto %{file}:%{line}"
      • RAILS_EDITOR="rubymine %{file}:%{line}"
    • テンプレート中で %{file} %{line} が展開される想定なので、それを使わないと正しい位置に飛べません。
    • サーバがコンテナ内で動いていて、ローカルのエディタとパスが一致しない場合は、そのままでは開けない点に注意。
  • 互換性

    • コントローラやアクションが存在しない/autoload 前などでも例外を出さないようにしてあり、「既存挙動が壊れる」類の変更は極力避けられています。
    • Rack アプリや Proc-based ルートなど Controller#Action ではないものは、アクション位置が表示されないだけで、従来どおり利用できます。

  1. 参考情報 (あれば)
  • 関連ファイル:
    • actionpack/lib/action_dispatch/routing/inspector.rb
    • actionpack/lib/action_dispatch/middleware/templates/routes/
  • エディタ連携の一般的な設定例(Rails ガイドでも紹介されている形式):
    • VS Code:
      export RAILS_EDITOR="code --goto %{file}:%{line}"
    • RubyMine:
      export RAILS_EDITOR="rubymine %{file}:%{line}"
  • この PR により、「どこでそのアクションが定義されているか」を Rails が教えてくれるようになるため、ルーティング周りのデバッグ・既存コードの追跡がかなり楽になります。

#56359 Active Support: remove stale deprecation assertions from TimeWithZone YAML tests

マージ日: 2025/12/13 | 作成者: @afurm

  1. 概要 (1-2文で)
    ActiveSupport::TimeWithZone の YAML シリアライズに関するテストから、既に無効となった非推奨警告(assert_not_deprecated)のアサーションと TODO コメントが削除され、テストがシンプルに YAML 出力そのものを検証する形に整理されています。挙動変更はなく、テストコードのクリーンアップのみです。

  1. 変更内容の詳細(あればサンプルコードも含めて)
  • 対象ファイル:

    • activesupport/test/core_ext/time_with_zone_test.rb
  • 主な変更点:

    • test_to_yaml / test_ruby_to_yaml で使用されていた assert_not_deprecated ブロックが削除されました。
    • 併せて、「Rails 7.1 でこのアサーションを削除する」という趣旨の TODO コメントも削除されました。
    • これにより、テストは「この処理が非推奨警告を出さないこと」を保証するのではなく、「YAML 出力が期待どおりであること」のみに集中する形になっています。
  • 変更前のイメージ(擬似コード):

    ruby
    # TODO: Remove assertion in Rails 7.1
    assert_not_deprecated do
      assert_equal expected_yaml, time_with_zone.to_yaml
    end
  • 変更後のイメージ:

    ruby
    assert_equal expected_yaml, time_with_zone.to_yaml

    同様に、Rubyオブジェクト→YAML へのシリアライズ (test_ruby_to_yaml) でも assert_not_deprecated が取り除かれています。

  • 背景:

    • もともと TimeWithZone の YAML シリアライズは、あるバージョンまで非推奨扱いのパスだった/あるいは挙動変更が控えており、将来的に削除・変更されることを前提に「非推奨警告が出ていないこと」を明示的にテストしていました。
    • Rails 7.1 以降では、その非推奨経路が解消され、もはや assert_not_deprecated で囲む意味がなくなったため、TODO 消化として削除されています。
    • 実行ログとして bundle exec ruby activesupport/test/core_ext/time_with_zone_test.rb が通ることが確認されています。

  1. 影響範囲・注意点
  • ランタイム挙動:

    • アプリケーションコードには一切変更がなく、ActiveSupport::TimeWithZone の YAML シリアライズ挙動自体は従来どおりです。
    • 非推奨警告の有無もすでに現状どおりで、このPRで新たに non-deprecated になったわけではありません。「既に出ていない警告に対するテスト用のラッパ削除」という位置付けです。
  • テスト・開発者への影響:

    • YAML 形式の期待値比較がそのまま残っているため、将来 YAML 出力フォーマットが変われば従来どおりテストが落ちて検知できます。
    • 逆に「非推奨警告が出ないこと」を明示的にテストする仕組みはなくなりますが、そもそも該当ケースは非推奨扱いではなくなっており、テスト意図としても不要という判断です。
    • CI などに対しても影響はなく、テストのノイズが減るのみです。
  • Rails バージョンとの関係:

    • TODO コメントに記載されていた「Rails 7.1 で削除予定」のものを、現行バージョン(7.1 以降)にあわせて実際に削除しているだけのため、バージョンアップ時のブレイクチェンジではありません。

  1. 参考情報 (あれば)
  • PR 本文で言及されているテストコマンド:
    • bundle exec ruby activesupport/test/core_ext/time_with_zone_test.rb
  • 関連コンポーネント:
    • ActiveSupport::TimeWithZone
    • YAML シリアライズ (to_yaml / Rubyオブジェクトからの YAML 生成)
  • 類似のクリーンアップ PR を読むと、「どの非推奨がいつ解消されたか」「どのテストが歴史的経緯で残っているか」を把握しやすくなります。

#55928 Batch reset sequence

マージ日: 2025/12/13 | 作成者: @gmcgibbon

  1. 概要 (1-2文で)
    PostgreSQL アダプタに、複数テーブルのシーケンスを一括でリセットできる reset_column_sequences! が追加され、特にフィクスチャ読み込み時などに多数テーブルがある環境でのパフォーマンス改善を狙った変更です。あわせて抽象アダプタ/各DBアダプタの reset 関連メソッドの整理が行われています。

  1. 変更内容の詳細

2-1. 背景

Rails のフィクスチャは、データ投入後に主キー用シーケンスをテーブルごとにリセットしますが、

  • テーブル数が多い
  • PostgreSQL を利用
    といった環境(例: Shopify)では、これが「テーブル単位での逐次クエリ実行」となり、数秒かかるほどのオーバーヘッドになっていました。

PostgreSQL では、

  • シーケンス名やカラム情報は schema cache 経由で事前にわかることが多い
  • デフォルトの命名規則(#{table}_#{column}_seq など)が使われるケースも多い
    ため、「シーケンス情報を DB に毎回問い合わせる」コストを減らしたい、というのがモチベーションです。

2-2. reset_column_sequences! の追加(PostgreSQL アダプタ)

activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb に、PostgreSQL 専用の新メソッド reset_column_sequences! が追加されました。

役割:

  • 「テーブル名 + 主キー列名 + シーケンス名 + 最大値」を引数でまとめて受け取り、一括で ALTER SEQUENCE ... RESTART WITH ... を行うバッチメソッド
  • 既存の「1テーブルずつシーケンスをリセットする」処理の基礎となるプリミティブな API として提供

ポイント:

  • 「どのテーブルにどの主キー(とシーケンス)が紐づくか」「今の最大値はいくつか」といった情報は、呼び出し側が決めて渡す想定で、このメソッド自身は情報取得ロジックを持ちません。
  • これにより、
    • fixture 処理側で schema cache を活用してまとめて情報を準備
    • reset_column_sequences! で一気にリセット
      というフローが可能になります。

疑似的な利用イメージは以下のような形になります(Rails 内部実装を簡略化したサンプル):

ruby
# schema_cache や AR モデル定義などから、まとめて情報を用意する想定
sequences_to_reset = [
  {
    table_name: "users",
    pk: "id",
    sequence_name: "users_id_seq",
    max_value: 123  # users.id の最大値
  },
  {
    table_name: "orders",
    pk: "id",
    sequence_name: "orders_id_seq",
    max_value: 999
  }
]

# PostgreSQL アダプタのバッチリセットメソッドを呼ぶ
ActiveRecord::Base.connection.reset_column_sequences!(sequences_to_reset)

内部では何が変わるか:

  • 従来:
    • 各テーブルごとに
      • 主キー列・シーケンス名取得のための問い合わせ
      • SELECT MAX(pk)
      • ALTER SEQUENCE ... RESTART WITH ...
  • 変更後 (将来的に fixture 側を最適化した場合):
    • schema cache を使ってテーブル/PK/シーケンスを一覧で取得
    • MAX(pk) もまとめてある程度効率よく計算
    • reset_column_sequences! に一覧を渡してまとめて実行

現段階では「アダプタ側にバッチ API を用意した段階」であり、fixtures 側の実装をこの API を使って最適化するのは今後の作業として想定されています。

2-3. 抽象アダプタおよび各 DB アダプタの整理

以下のファイルで、reset_pk_sequence! 周りのメソッド定義が整理されています。

  • abstract/database_statements.rb (+19/-17)
  • mysql2/database_statements.rb (+17/-17)
  • sqlite3/database_statements.rb (+17/-17)
  • trilogy/database_statements.rb (+18/-18)

主な意図は:

  • 抽象層 (AbstractAdapter) に置くべきインターフェイスと、各 DB ごとの実装をはっきり分離する
  • Postgres 専用の「バッチリセット」機能を追加しても、他 DB アダプタへの影響を最小化する
  • インターフェイスのシグネチャの揺れを減らし、将来的に fixtures 側の実装をシンプルにできるようにする

コード量としては行数は増減していますが、MySQL / SQLite / Trilogy については、仕様追加というより「メソッド定義位置の移動・共通化・整理」が中心です。

2-4. fixtures 周りの軽微な変更

  • activerecord/lib/active_record/fixtures.rb (+2/-2)

現時点では大規模なリファクタではなく、

  • 既存 reset ロジックとの整合性を保つための微調整
  • 新しいアダプタ側インターフェイスに合うような呼び出しの調整
    程度の変更に留まっています。

2-5. テスト追加

  • activerecord/test/cases/adapters/postgresql/schema_test.rb (+17/-1)

PostgreSQL アダプタに追加された reset_column_sequences! などに対するテストが追加されています。
これにより、複数テーブルを対象としたリセット処理が想定通り動くことを担保しています。


  1. 影響範囲・注意点
  • 対象 DB:
    • 実質的な新機能は PostgreSQL アダプタのみ。
    • MySQL / SQLite / Trilogy アダプタはインターフェイス整理が主で、機能面の挙動はほぼ従来どおり。
  • 公開範囲:
    • reset_column_sequences! は nodoc(非公開 API 扱い)で、公式にサポートされるパブリック API ではありません。
    • ただし、Rails 内部や gem(特にフレームワーク拡張系)がこれを呼ぶ可能性はあります。
  • 既存アプリへの影響:
    • 公開 API に大きな破壊的変更はないため、通常のアプリケーションコードへの影響はほぼありません。
    • ただし、独自にアダプタの reset_pk_sequence! や fixtures 周りを monkey patch している場合は、インターフェイスの微妙な変化に注意が必要です。
  • パフォーマンス改善のタイミング:
    • この PR 時点では「バッチリセット API が追加された」段階であり、fixtures 実装の最適化はまだ将来の変更として計画されています。
    • そのため、すぐに体感できる速度向上は限定的な可能性がありますが、今後の PR によって大規模プロジェクトでの fixture ロード時間短縮が見込まれます。

  1. 参考情報 (あれば)
  • PR 作成者のコメント:
    • Shopify のような大規模環境で fixture のシーケンスリセットが数秒かかっている現状への対処が主目的。
    • schema cache やデフォルトのシーケンス名を利用することで、情報取得を最小化したい。
    • まずは PostgreSQL アダプタに汎用的なバッチ API を追加し、後続で fixtures 実装をそれに載せ替える方針。
  • チェックリスト:
    • テストは追加済み。
    • CHANGELOG は現時点では更新されていない(nodoc な内部 API であり、挙動変更も限定的なため)。

#56356 Fix protect_from_forgery to use the default value from config

マージ日: 2025/12/13 | 作成者: @chaadow

  1. 概要 (1-2文で)
    config.action_controller.forgery_protection_verification_strategy で設定したデフォルト値が、コントローラ側の protect_from_forgery に正しく反映されず、常に :header_only がフォールバックとして使われていた問題を修正するPRです。これにより、config で指定した検証戦略(例: :header_or_legacy_token)が期待通りデフォルトとして利用されるようになります。

  1. 変更内容の詳細

何が問題だったか

  • アプリ側で以下のように設定しても:

    ruby
    # config/application.rb など
    config.action_controller.forgery_protection_verification_strategy = :header_or_legacy_token
  • 実際には、ActionController::Base などで protect_from_forgery が呼ばれるときに、この設定値が使われず、header_only がフォールバックとして使われていました。

  • その結果、「設定した戦略がデフォルトとして適用される」という期待が満たされていませんでした。

修正内容(概要)

  • ActionController::RequestForgeryProtection#protect_from_forgery のデフォルトオプションの決定ロジックを修正し、
    「config に値があればそれを使う」「なければ :header_only」という形に統一しました。
  • テストを追加・修正し、
    • 設定値を変えたときに実際に protect_from_forgery がその値を採用すること
    • 設定がない場合にだけ header_only へフォールバックすること
      を確認しています。

※ 実際の差分は1行レベルの変更で、protect_from_forgery のオプション解決時に参照する値を config.action_controller.forgery_protection_verification_strategy に揃えた、という内容です。

ざっくりしたイメージコード

(実際のコードとは多少簡略化したイメージです)

ruby
def protect_from_forgery(options = {})
  options[:with] ||= Rails.configuration.action_controller.forgery_protection_verification_strategy || :header_only
  # ...
end

このように「config にあるならそれを使う」が保証されるようになりました。


  1. 影響範囲・注意点
  • 影響を受けるケース

    • config.action_controller.forgery_protection_verification_strategy:header_only 以外に設定しているアプリケーション。
    • 特に、PR 説明にもある :header_or_legacy_token を使っている場合は、これまで実は :header_only が使われていたため、今回の修正で挙動が変わります。
  • 挙動の変化

    • 以前:
      • 設定値に関係なく、protect_from_forgery が暗黙に :header_only を採用してしまうケースがあった。
    • 修正後:
      • config.action_controller.forgery_protection_verification_strategy に指定した値が、protect_from_forgery のデフォルト戦略として正しく利用される。
      • 設定が未指定の場合にのみ、header_only がフォールバックとして使われる。
  • 注意点

    • これまで「設定しているつもりだったが、実際には header_only で動いていた」アプリが、今回の修正で本来意図していた戦略(例: :header_or_legacy_token)に切り替わるため、CSRF トークン検証の挙動が変わる可能性があります。
    • :header_or_legacy_token など非デフォルト戦略を利用している場合は、
      • API クライアント
      • 旧来のフォーム(form_authenticity_token を hidden field で送っているもの)
        の双方で CSRF が期待通り検証されるか、確認しておくと安全です。

  1. 参考情報 (あれば)

#56346 Update with_lock docs to mention yielded transaction

マージ日: 2025/12/12 | 作成者: @Saidbek

  1. 概要 (1-2文で)
    ActiveRecord::Base#with_lock が「現在のトランザクションオブジェクトをブロックに渡す」ようになった (#56334) ことを、APIドキュメントとガイドに反映した PR です。これにより、利用者が with_lock 内でトランザクションのコールバックなどを明示的に登録できることがドキュメント上も分かるようになりました。

  1. 変更内容の詳細

a. with_lock のドキュメント更新(inline docs)

activerecord/lib/active_record/locking/pessimistic.rb 内の with_lock のコメントが、以下のような点で更新されています:

  • ブロック引数として「現在のトランザクション」が渡されることを明記
  • そのトランザクションオブジェクトに対して、after_commit などのコールバック登録ができることを示唆

(実際のコードはほぼコメントのみの変更で挙動は #56334 で既に変わっており、この PR はそれを説明とサンプルに反映するものです)

b. ガイド (Active Record Querying ガイド) の更新

guides/source/active_record_querying.mdwith_lock の解説部分が更新され、サンプルコードが「トランザクションをブロック引数で受け取る」形になっています。おおよそ以下のような内容が追加・変更されています(イメージ):

ruby
book = Book.first

book.with_lock do |transaction|
  # 通常の排他ロック付き更新
  book.update!(stock: book.stock - 1)

  # トランザクションオブジェクトに対してコールバックを登録できる
  transaction.after_commit do
    Notifications.book_purchased(book)
  end
end

ポイント:

  • これまでの with_lock do ... end の例に「|transaction| を受け取る」パターンを追加
  • 読者に「with_lock のブロックは、単にロック下での処理を書く場所」というだけでなく、「そのロックを担うトランザクションオブジェクト自体を直接扱える場所」であることを示しています。

  1. 影響範囲・注意点
  • 本 PR自体は ドキュメント変更のみ で、挙動の変更は含まれていません(挙動はすでに #56334 で変更済み)。
  • Rails 7.2 以降(または #56334 が入ったバージョン)では、with_lock のブロック引数にトランザクションオブジェクトが渡される前提でコードが書けます。
    • 例: with_lock do |tx| tx.after_commit { ... } end
  • 既存コードでブロック引数を取っていない場合は、そのまま動作します。with_lock do ... end という書き方も引き続き有効です。
  • トランザクションオブジェクトに依存した書き方をすると、その API(例: after_commit, after_rollback など)の互換性や将来変更に注意が必要です。

  1. 参考情報 (あれば)
  • この PR の背景となる挙動変更:
    • PR #56334: with_lock が現在のトランザクションを yield するようにした変更
  • 関連ドキュメント:
    • Rails Guides – Active Record Querying – Locking (with_lock セクション)
    • ActiveRecord::Base#with_lock の API ドキュメント (Edge / 該当バージョンの RDoc)

#56355 Active Support notifications for CSRF warnings

マージ日: 2025/12/12 | 作成者: @jeremy

  1. 概要 (1-2文で)
    このPRは、CSRF関連の警告・ブロック処理を「直接ログ出力」から Active Support Notifications を用いた「イベント駆動のログ・フック」に変更し、CSRFイベントを購読・拡張できるようにしたものです。新しい通知イベントが追加され、StructuredEventSubscriber によってログ用の構造化イベントへ変換されるようになりました。

  1. 変更内容の詳細

2-1. 新しく追加された通知イベント

ActionController から以下の ActiveSupport::Notifications イベントが発火されるようになりました:

  • csrf_token_fallback.action_controller
    • 想定通りの CSRF トークンが得られず、フォールバック手段に頼った場合のイベント(例: クッキー不整合などでトークン生成/検証が怪しい時)。
  • csrf_request_blocked.action_controller
    • CSRF 検証に失敗し、リクエストがブロックされたときのイベント。
  • csrf_javascript_blocked.action_controller
    • JavaScript 経由の CSRF(例: UJS / fetch など)をブロックしたときのイベント。

これまでは RequestForgeryProtection モジュール内で直接 Rails.logger.warn / info 等へ出していた内容が、上記のような Notification イベントとして発火されるように置き換わっています。

通知の使い方(購読例)

ruby
ActiveSupport::Notifications.subscribe("csrf_request_blocked.action_controller") do |name, start, finish, id, payload|
  # name: "csrf_request_blocked.action_controller"
  # payload: ハッシュ形式のメタ情報(controller, action, request_id など)
  Rails.logger.warn("[CSRF BLOCKED] #{payload[:method]} #{payload[:path]} by #{payload[:controller]}##{payload[:action]}")
  
  # 例: 独自の監査ログ・メトリクス連携・Slack通知など
end

CSRFの発生状況を、ログに限らずメトリクス送信・アラート・監査ログなど任意の処理に接続しやすくなっています。


2-2. StructuredEventSubscriber との連携

action_controller/structured_event_subscriber.rb に CSRF イベント対応が追加されました。

  • StructuredEventSubscriber が上記の Notification イベントを受け取り、
    • action_controller.* 名前空間の「構造化イベント」に変換
    • LogSubscriberaction_controller/log_subscriber.rb)が扱いやすいフォーマットに整形
  • これにより、既存の「アクション実行ログ」などと同じフォーマット/ストリームで CSRF に関するログが扱えるようになります。

LogSubscriber 側では、新たに CSRF 向けのハンドラメソッドが追加されています(例示イメージ):

ruby
module ActionController
  class LogSubscriber < ActiveSupport::LogSubscriber
    def csrf_request_blocked(event)
      payload = event.payload
      info do
        "Blocked CSRF request: #{payload[:method]} #{payload[:path]} "\
        "(origin: #{payload[:origin]})"
      end
    end

    def csrf_token_fallback(event)
      debug do
        "CSRF token fallback used for #{payload[:controller]}##{payload[:action]}"
      end
    end

    # ...
  end
end

※メッセージ内容や payload の正確なキー名は実装依存ですが、このような形で「イベント名ごとのログメソッド」が追加されています。


2-3. RequestForgeryProtection 内部の変更

action_controller/metal/request_forgery_protection.rb の内部で、次のような変更が行われています:

  • 旧: logger.warn "CSRF token ..." のようにその場で直接ログ出力
  • 新: ActiveSupport::Notifications.instrument("csrf_request_blocked.action_controller", payload) { ... } のようにイベントを発火

これにより、CSRF 関連の処理は

  1. ActionController 側で「何が起きたか」のイベントを発火
  2. StructuredEventSubscriber がそれを捕捉し、構造化イベント/ログフォーマットに変換
  3. LogSubscriber やカスタム Subscriber が必要なログ・処理を行う

という流れになります。

テスト (request_forgery_protection_test.rb) も、直接ログメッセージを期待する形から、通知イベント発火/StructuredEventSubscriber 経由のログを期待する形に更新されています。


  1. 影響範囲・注意点

  2. CSRF関連ログに直接依存している場合の注意

    • 以前の「生ログメッセージ」にパース依存していた場合(例: 正規表現で CSRF ログ行だけ抜き出すなど)、メッセージの文言変更や構造化ログ化の影響を受ける可能性があります。
    • 今後はなるべく ActiveSupport::Notifications.subscribe によるイベント購読に切り替えると安全です。
  3. カスタムミドルウェア・監査基盤との連携がしやすくなる

    • CSRF ブロックを検知してメトリクス送信・アラート発報・IP ブロック・ユーザー通知などを行うロジックを「Rails の標準機能にフックする形」で実装しやすくなりました。
    • 例えば csrf_request_blocked.action_controller にフックして、WAF や SIEM へのイベント送信を行うことができます。
  4. パフォーマンス上の影響はごく小さい想定

    • ActiveSupport::Notifications はもともと Rails 内部で幅広く利用されている仕組みであり、CSRF 関連の処理もその一部に乗った形です。標準的な利用では特別なパフォーマンス劣化はほぼありません。
    • ただし、購読側で重い処理(同期I/O, 外部API呼び出しなど)を行うとリクエストレイテンシに影響が出る可能性があるため、非同期処理(ActiveJob など)と組み合わせることが推奨されます。
  5. ログフォーマットの変化を監視・運用している場合

    • 既存のロガー設定、ログ集約/解析ツール(Datadog, Splunk, OpenSearch, Cloud Logging など)のフィルタやパーサが「CSRF ログ」を前提としている場合は、動作確認が必要です。
    • 新しく action_controller.* 系の構造化イベントとして出力されるため、フィールドベースのパースがやりやすくなる一方、旧来の生テキスト前提のルールは調整が必要になるかもしれません。

  1. 参考情報 (あれば)

この変更により、CSRF に関する挙動をより柔軟・構造化された形で監視・拡張できるようになっているため、監査ログやセキュリティ基盤との統合を検討している場合に特に有用です。


#56350 Use a modern approach for cross-site request forgery protection

マージ日: 2025/12/12 | 作成者: @rosa

  1. 概要 (1-2文で)
    Rails の CSRF 保護を「Sec-Fetch-Site ヘッダを使ったモダン方式」がデフォルトとなるように刷新し、従来の authenticity token 方式はレガシー環境向けのフォールバックとして扱う変更です。あわせて、正当なクロスサイトアクセスを許可する trusted_origins オプションなどが追加され、将来的に CSRF トークンを廃止していく足がかりになっています。

  1. 変更内容の詳細

2.1 新しい CSRF 検証アプローチ

protect_from_forgeryusing オプションが追加され、CSRF 検証方式を選択できるようになりました。

  • using: :header_only
    • Sec-Fetch-Site ヘッダのみを使って CSRF 判定を行う。
    • Rails 8.2 のデフォルトになる想定。
  • using: :header_or_legacy_token
    • 原則 Sec-Fetch-Site で判定し、判定できない場合に限って従来の authenticity token チェックにフォールバックする。
    • レガシーブラウザや proxy の都合で Sec-Fetch-Site が使えないアプリ向けの移行パス。

使用例:

ruby
class ApplicationController < ActionController::Base
  # 例: 当面はトークンも使いつつ移行したい場合
  protect_from_forgery using: :header_or_legacy_token, with: :exception
end

header_or_legacy_token を使っていて、実際にトークンへのフォールバックが発生した場合にはログに警告が出るようになっており、どのリクエストがまだモダン方式に移行できていないかを把握できます。

2.2 Sec-Fetch-Site による判定のポリシー

この PR で導入される新方式では、Sec-Fetch-Site ヘッダを非常に保守的に解釈します。非 GET/HEAD の「ブラウザ由来」のリクエストを想定した挙動です。

  • 許可される値
    • same-origin
    • same-site
  • 原則拒否となる値
    • cross-site
  • ヘッダがない/none の場合
    • 原則拒否
    • using: :header_or_legacy_token の場合のみ、authenticity token による従来チェックにフォールバックする。

開発者視点の要点:

  • モダンブラウザからの通常のフォーム送信や XHR/Fetch であれば same-origin / same-site となり、問題なく通ります。
  • レガシーブラウザ、あるいは中間 proxy / FW が Sec-Fetch-Site を削っている環境では、このヘッダに頼りすぎると CSRF に対して無防備になるため、ヘッダ欠如は「危険」とみなして拒否 or トークンフォールバックします。
  • Origin チェックは従来通り残っており、Sec-Fetch-Site もしくは authenticity token チェックと組み合わせて使われます(Origin ポリシー自体は変更なし)。

2.3 正当なクロスサイトを許可する trusted_origins

OAuth コールバックや、埋め込みウィジェットなど、「意図した cross-site POST」を許可するための設定が追加されました。

ruby
class ApplicationController < ActionController::Base
  protect_from_forgery using: :header_only,
                      trusted_origins: %w[ https://accounts.google.com ]
end
  • Sec-Fetch-Site: cross-site であっても、trusted_origins に含まれる Origin からであれば許可されます。
  • これにより、昔は「CSRF トークンを特別扱いでスキップ」していたようなパス(例: OAuth コールバック)を、よりヘッダベースなセキュリティモデルで扱えるようになります。

2.4 設定・ガイド・テストの更新

  • actionpack/CHANGELOG.md
    • 上記の新機能・デフォルト変更についてのエントリが追加。
  • guides/source/security.md / configuring.md / action_controller_advanced_topics.md
    • CSRF 保護の説明がアップデートされ、新しい using オプションや trusted_origins についての記述が追加。
  • config/initializers/new_framework_defaults_8_2.rb.tt
    • Rails 8.2 で新デフォルト(header_only)へ移行するための初期化オプションが追加。アプリ作成時・アップグレード時にここを編集して挙動を切り替えられる。
  • テスト (request_forgery_protection_test.rb)
    • 新しいパス(header_only, header_or_legacy_token, trusted_origins)を網羅する大量のテストが追加されており、本機能がかなり慎重にカバーされていることがわかります。

  1. 影響範囲・注意点

(1) Rails 8.2 以降のデフォルト変更

  • デフォルトが using: :header_only になるため、以下のようなケースで非互換が出る可能性があります:
    • レガシーブラウザ(Sec-Fetch-Site 非対応)からの非 GET/HEAD リクエスト
    • 中間 proxy / FW が Sec-Fetch-* 系ヘッダを削除している構成
    • 正当な Cross-Site POST(OAuth, サードパーティ連携など)を trusted_origins で明示していない場合

対処方針:

  • 不安がある場合は、まずは以下のように「フォールバックあり」の設定で運用開始するのが現実的です:

    ruby
    protect_from_forgery using: :header_or_legacy_token, with: :exception
  • ログに出る「トークンへフォールバックしたリクエスト」を監視し、

    • どのパスが古いクライアント / 変な proxy 経由なのか
    • どのリクエストが正当な cross-site アクセスなのか
      を洗い出して、順次:
    • クライアントをモダンブラウザ・適切な proxy 設定に変更
    • trusted_origins を追加 していくと、最終的に header_only へ切り替えやすくなります。

(2) API クライアントや非ブラウザクライアント

  • 本 PR は基本的に「ブラウザ由来の CSRF リスクがあるリクエスト」を対象としているため、
    • API モード (ActionController::API)
    • そもそも CSRF 保護を無効にしている JSON API
      などには直接の影響は少ないはずです。
  • ただし、「ブラウザからの HTML + JSON を混在させているアプリ」で、CSRF 保護対象の JSON エンドポイントがある場合は、Sec-Fetch-Site が前提になることで挙動が変わる可能性があります。

(3) サブドメインをまたぐ運用

  • 現時点の実装では same-site も許可するため、
    • app.example.com から admin.example.com へのリクエストなど、サブドメイン間は「同一サイト」として扱われ、CSRF 的には許可されます。
  • PR で提起されているオープンクエスチョンとして、「same-site を許さず same-origin のみを許可できるようにした方が良いか」があります。
    • 将来的にここが設定可能になると、サブドメイン間攻撃を避けるためにより厳格なポリシーを選べるようになる見込みです。
    • 現時点ではそこまでの設定は入っていないため、サブドメイン分離でセキュリティ境界を引いているアプリは、Origin チェック・クッキー分離などと組み合わせて慎重に設計する必要があります。

  1. 参考情報

PR 内で言及されている参考資料と、理解に役立つポイントです。

この PR は、「CSRF トークンに強く依存する旧来スタイル」から、「ブラウザ標準の Fetch Metadata に基づく CSRF 防御」への移行を Rails レベルで促進するものなので、中長期的に Rails アプリの CSRF 戦略を見直すきっかけになる変更と言えます。


#56354 [ci skip][docs] PARALLEL_WORKERS threshold bypass behavior

マージ日: 2025/12/12 | 作成者: @justinko

  1. 概要 (1-2文で)
    Rails のテスト並列実行機能で使われる PARALLEL_WORKERS 環境変数について、「しきい値(threshold)よりテスト件数が少ない場合でも、PARALLEL_WORKERS を明示指定すれば並列実行を強制できる」という挙動を、ガイドと API ドキュメントに明記した PR です。コード上の変更はごく小さく、主にドキュメントの明文化が目的です。

  1. 変更内容の詳細

背景となる機能

Rails のテスト並列実行は通常、以下のように設定します。

ruby
# test/test_helper.rb など
class ActiveSupport::TestCase
  parallelize(workers: :number_of_processors)
end

Rails 7.1 以降では、テスト総数が少ない場合に「オーバーヘッドの方が大きくなる」ことを避けるため、**一定件数未満のテストでは自動的に並列化をスキップする「しきい値」**が導入されています。

この PR(とリンク先の #56352)で整理されたのは、このしきい値を「環境変数 PARALLEL_WORKERS の明示指定でバイパスできる」という挙動です。


コード変更(ActiveSupport::TestCase

activesupport/lib/active_support/test_case.rb の変更は +3 / -1 行と小さいですが、おおよそ次のような意図を持っています(疑似コードイメージ):

ruby
def self.parallelize(workers: :number_of_processors, with:)
  # ここに「しきい値判定 + PARALLEL_WORKERS によるバイパス」ロジックが存在
  # 例示的には:
  if ENV["PARALLEL_WORKERS"].present?
    # ユーザー指定を尊重して threshold を無視して並列化する
    workers = ENV["PARALLEL_WORKERS"].to_i
  else
    # threshold に基づいて並列化するかどうかを決める
    # (テスト数が threshold 未満ならシリアルに、以上なら parallel)
  end
end

今回の PR 自体はこの挙動をわずかに調整・明文化した程度で、**「PARALLEL_WORKERS がセットされていれば threshold を無視する」**というルールが明確になったと考えてよいです。


Guides の変更(guides/source/testing.md

ガイドのテスト章に、以下のような趣旨の記述が追加されています(要約):

  • Rails には「テスト数が少ない場合は並列実行しない」ためのしきい値がある
  • しかし PARALLEL_WORKERS 環境変数を指定した場合は このしきい値をバイパスして、指定したワーカー数で並列実行される
  • つまり:
    • PARALLEL_WORKERS 未指定 → threshold ロジックが有効
    • PARALLEL_WORKERS 指定あり → threshold を無視して強制並列化

具体的な利用イメージ:

bash
# 通常: テスト数が threshold 未満ならシリアル実行される可能性がある
bin/rails test

# しきい値を無視して常に 4 ワーカーで並列実行したい
PARALLEL_WORKERS=4 bin/rails test

# CI 上で CPU コア数に合わせて並列数を明示的に制御
PARALLEL_WORKERS=$(nproc) bin/rails test

API ドキュメントの変更

ActiveSupport::TestCase.parallelize の API ドキュメントに、環境変数との関係が追記されています(要約):

  • parallelize(workers: ...)ENV["PARALLEL_WORKERS"] によってオーバーライドされる
  • テストの並列化にはしきい値があり、通常はテスト数が少ないと並列化を行わないが、 PARALLEL_WORKERS を指定したときは このしきい値をスキップして並列化 される

  1. 影響範囲・注意点
  • すでに PARALLEL_WORKERS を使っている環境

    • これまで通り「指定したワーカー数で必ず並列実行」されることが、仕様として明文化された形です。
    • テスト件数が少なくても、CI などで PARALLEL_WORKERS を設定していれば並列実行されるので、環境によってはオーバーヘッドが増える可能性があります。
  • PARALLEL_WORKERS を使っていない環境

    • 挙動は従来と同じく、テスト数や threshold に応じて自動判定されます。
    • 「小さな test suite で並列化したい」場合は、PARALLEL_WORKERS を指定すれば threshold を無効化できると理解しておくとよいです。
  • 設定戦略の指針

    • ローカル開発:
      • テスト数が少ないプロジェクトでは PARALLEL_WORKERS をあえて指定せず、threshold の自動判定に任せると速度面で有利なことがあります。
    • CI:
      • 実行時間のブレを減らしたい場合には、PARALLEL_WORKERS を固定値(または CPU コア数)に明示設定しておくと、threshold に左右されず安定した挙動になります。

  1. 参考情報 (あれば)

#56353 Make delegate and delegate_missing_to work in BasicObject subclasses

マージ日: 2025/12/12 | 作成者: @rafaelfranca

  1. 概要 (1-2文で)
    BasicObject を継承したクラスでも delegate / delegate_missing_to が正しく動くように、例外の発生元や例外クラス参照を ::Kernel / ルート名前空間経由に修正した PR です。これにより、BasicObject サブクラスで delegate_missing_to を使った際に SystemStackError(無限再帰)が発生していた問題が解消されます。

  1. 変更内容の詳細

何が問題だったか

BasicObject には raise メソッドや NoMethodError などの定数がありません。
一方、delegate / delegate_missing_to が生成するメソッド内では例外を投げたり、例外クラスを参照したりします。

例えば(あくまでイメージ・簡略化):

ruby
class MyObject < BasicObject
  extend ActiveSupport::Concern
  extend ActiveSupport::Delegation

  delegate_missing_to :@target
end

このとき、method_missing 経由で DelegationError などを投げようとして、

  • raise が存在しない(BasicObject にはない)
  • NoMethodErrorDelegationError などの定数解決が正しく行えない

結果として、method_missing 内でさらに method_missing が呼ばれるような無限再帰が発生し、SystemStackError になる、という挙動になっていました。

PR での対処

この PR では、activesupport/lib/active_support/delegation.rb 内で生成されるコードを次のように修正しています(実際のコードはもっと複雑ですが、ポイントだけ抜粋イメージ):

1. 例外発生時は ::Kernel.raise を使うように修正

修正前(イメージ):

ruby
raise DelegationError, "Message"

修正後(イメージ):

ruby
::Kernel.raise ::DelegationError, "Message"

もしくは NoMethodError などを投げる箇所も同様に:

ruby
::Kernel.raise ::NoMethodError, "undefined method ..."

これにより、

  • BasicObject には raise がなくても、::Kernel.raise を呼ぶので例外を正しく発生できる
  • DelegationError / NoMethodError などの定数も :: でルート名前空間から解決され、BasicObject に依存しない

という状態になります。

2. 例外クラスをすべてルート名前空間で参照

NoMethodError, DelegationError などの例外クラス参照を、

ruby
::NoMethodError
::DelegationError

のように、先頭に :: をつけてルート名前空間に固定しました。

BasicObject サブクラスでは Kernel などを include していないことも多く、そのクラスのコンテキストで定数解決させると失敗する可能性があるため、それを避けています。

3. テストの追加

activesupport/test/core_ext/module_test.rb にテストが追加されています。
ここでは、BasicObject を継承したクラスで delegate / delegate_missing_to を利用し、期待どおり動作することを確認するテストが加えられています。

イメージ的には:

ruby
class BasicProxy < BasicObject
  extend ::ActiveSupport::Delegation

  def initialize(target)
    @target = target
  end

  delegate_missing_to :@target
end

obj = BasicProxy.new("string")
obj.upcase # => "STRING" が返り、例外も無限再帰も起きない

のような使い方を想定したテストです。


  1. 影響範囲・注意点
  • 影響を受ける場面

    • BasicObject を継承したクラスで delegate / delegate_missing_to を使っている(もしくは、これから使いたい)場合に直接恩恵があります。
    • これまでは delegate_missing_to を使うと SystemStackError になるケースがあったのが、正しく DelegationError / NoMethodError を発生させられるようになります。
  • 既存コードへの互換性

    • 通常の Object / ActiveRecord::Base / ApplicationRecord などを使ったクラスに対しては、例外の発生元が raise から ::Kernel.raise に変わるだけで挙動は同じです。
    • 例外クラスも従来どおり NoMethodError などが使われているだけで、名前空間指定が明示的になっただけなので、互換性上の問題はほぼありません。
  • メタプログラミングやパッチを当てている場合の注意

    • もしアプリ側で ActiveSupport::Delegation の生成するコードにパッチを当てていたり、raise 呼び出しをフックしていたりするような高度なメタプログラミングをしている場合、::Kernel.raise 呼び出しになることで挙動が変わる可能性があります。
    • そのようなコードを書いている場合は、この PR 以降のバージョンで挙動を確認した方が安全です。

  1. 参考情報 (あれば)
  • PR 本体: https://github.com/rails/rails/pull/56353
  • 関連する Rails 機能:
    • Module#delegate / Module#delegate_missing_to(ActiveSupport::Delegation)
  • BasicObjectKernel の関係:
    • BasicObject は Ruby オブジェクト階層の最下層で、Kernel を include していません。そのため、puts / raise などは素のままでは使えず、::Kernel.raise のように明示する必要があります。本 PR はこの Ruby の性質を踏まえた修正になっています。

#56333 Do not clean directories directly under the application root with Rails::BacktraceCleaner

マージ日: 2025/12/12 | 作成者: @alpaca-tc

  1. 概要 (1-2文で)
    Rails の Rails::BacktraceCleaner が、アプリケーションルート直下のディレクトリ(※vendor を除く)にあるファイルのスタックトレースを「クリーン」しないように変更されました。これにより、./script./packs など独自ディレクトリに置いたスクリプトやユーティリティの呼び出し元が backtrace にきちんと表示されるようになります。

  1. 変更内容の詳細

背景・課題

  • 多くのチームが、一時的・補助的なスクリプトを ./script, ./scripts, ./packs などアプリケーションルート直下の任意ディレクトリに置き、bin/rails runner ./script/foo.rb のように実行している。
  • デバッグ時に BACKTRACE=1 を付けてクエリの発行元を追いたいが、Rails.backtrace_cleaner は「フレームを減らして見やすくする」ために、アプリケーションコードとみなしていないパスをフィルタリングしてしまう。
  • その結果、本当に知りたい ./script/xxx.rb などユーザ定義ディレクトリのフレームが backtrace から消え、代わりに gem 内部 (activerecord/lib/… など) だけが見える状態になっていた。

問題例

ruby
# ./script/verbose_logs.rb
User.where(id: 1).load

現在の main ブランチでは:

bash
$ bundle exec rails runner ./script/verbose_logs.rb
  User Load (1.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 1

BACKTRACE=1 を付けても:

bash
$ BACKTRACE=1 bundle exec rails runner ./script/verbose_logs.rb
  User Load (1.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 1
 activerecord (7.2.3) lib/active_record/log_subscriber.rb:122:in 'ActiveRecord::LogSubscriber#log_query_source'
  • verbose_logs.rb 自体の行番号など、本当に見たい呼び出し元フレームが表示されない。

ActiveSupport::BacktraceCleaner#remove_filters! を使えばフィルタを全削除できるが、そうすると逆に Rails が標準で提供している便利なノイズ除去機能も失われてしまう、というトレードオフがあった。


今回の変更内容

Rails::BacktraceCleaner の初期化ロジックを変更し、以下のポリシーにしました。

  • アプリケーションルート直下にあるディレクトリは、原則として backtrace のクリーン対象から外す
    • 例: ./script, ./scripts, ./packs, ./lib_custom など
  • ただし vendor ディレクトリは例外扱い とし、従来通りクリーン対象のままにする
    • vendor 以下はサードパーティコードが多く、backtrace ノイズになりやすいため

具体的には、Rails::BacktraceCleaner が「どのパスをアプリケーションコードとみなすか」「どのパスをクリーン対象とするか」を決めている箇所に、アプリケーションルート直下ディレクトリを含めるようなロジックが追加されています(差分は +4/-2 行と小さく、既存の挙動を崩さない形での拡張)。

変更後の挙動例

同じスクリプト:

ruby
# ./script/verbose_logs.rb
User.where(id: 1).load

PR 適用後は:

bash
$ bundle exec rails runner ./script/verbose_logs.rb
  User Load (1.1ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 1
 script/verbose_logs.rb:1:in '<top (required)>'
  • script/verbose_logs.rb のファイル名・行番号が backtrace に表示されるようになっている。
  • gem 内部 (activerecord など) のフレームは、従来どおり適度にクリーンされる。

  1. 影響範囲・注意点

影響を受けるケース

  • 以下のような 「アプリケーションルート直下のカスタムディレクトリ」にコードを置いているプロジェクト:
    • ./script, ./scripts, ./packs, ./tools, ./tasks など
  • それらを bin/rails runner で実行したり、アプリケーションから require して使っている場合に、ログ・エラー・クエリソースの backtrace に当該ファイルがより多く表示されるようになります。

とくにメリットが大きいのは:

  • データパッチや一時的スクリプトを migration ではなく ./script に置いているチーム
  • モジュラーモノリス構成で ./packs にドメイン別パッケージを切っているチーム

注意点

  • vendor 直下のコードは、従来どおり backtrace クリーン対象 です。
    • vendor 以下を自前コードとして積極的に使っていて「vendor も backtrace に常に出したい」というニーズがある場合は、引き続き Rails.backtrace_cleaner のカスタマイズが必要です。
  • 表示されるフレーム数が若干増える可能性がありますが、増えるのは基本的に「自分たちが書いたコード」のみなので、デバッグ上の恩恵のほうが大きいはずです。
  • ActiveSupport::BacktraceCleaner の API 自体は変わっていないため、既存で backtrace_cleaner を独自にカスタマイズしている場合も、衝突はほぼ起きにくい変更です。

  1. 参考情報 (あれば)
  • 対象 PR: https://github.com/rails/rails/pull/56333
  • PR 内コメント: Rails::BacktraceCleaner の「アプリケーションコードとみなすパス」定義を拡張するという方針に基づいた修正。
  • 関連クラス:
    • Rails::BacktraceCleaner (railties/lib/rails/backtrace_cleaner.rb)
    • ActiveSupport::BacktraceCleaner (ベースとなるクリーンクラス)

#56349 Fix request load leak when building middleware stack

マージ日: 2025/12/12 | 作成者: @gmcgibbon

  1. 概要 (1-2文で)
    このPRは、Rails 起動時にミドルウェアスタックを構築する際、不要に ActionDispatch::Request をロードしてしまう「リクエスト関連のロードリーク」を解消するものです。ActionDispatch::Request を eager load せず、autoload + load hook を活用することで、ブート時の不要な処理を避けています。

  1. 変更内容の詳細

主なポイントは「ActionDispatch::Request を明示的に require しないようにした」ことです。

変更1: Flash ミドルウェアでのリクエストパッチ方法の変更

action_dispatch/middleware/flash.rb で、フラッシュ用の機能を ActionDispatch::Request にパッチする際に、これまでは ActionDispatch::Request を直接ロードする形になっていましたが、それをやめて「autoload が ActionDispatch::Request を読み込んだタイミングでパッチする」ように修正しています。

イメージとしては、以下のような方針転換です(実際のコードは簡略化例):

ruby
# 以前のイメージ(例)
require "action_dispatch/request"

ActionDispatch::Request.include Flash::RequestMethods

# 変更後のイメージ(例)
ActiveSupport.on_load(:action_dispatch_request) do
  include Flash::RequestMethods
end

これにより、アプリ起動時(ミドルウェアスタック構築時)に ActionDispatch::Request 自体は読み込まれず、実際に ActionDispatch::Request が必要になった時点でロードフックが動作し、拡張が加わるようになります。

変更2: セッション抽象ストアでの require 削除

action_dispatch/middleware/session/abstract_store.rb から ActionDispatch::Requestrequire が1行削除されています。

ruby
# 削除されたイメージ
require "action_dispatch/request"

ActionDispatch::Request は Rails 内で autoload されるクラスであり、ここで明示的に require する必要がないため、ブート時の不要なロードを避ける目的で削除されています。

CHANGELOG の更新

actionpack/CHANGELOG.md に、この挙動変更(ブート時の不要な request ロードをやめた)が記載され、利用者が挙動変化・最適化を把握できるようにされています。


  1. 影響範囲・注意点
  • 影響範囲

    • ActionDispatch::Flash ミドルウェア
    • ActionDispatch::Session::AbstractStore を継承するセッションストア実装
    • ActionDispatch::Request に対して ActiveSupport.on_load(:action_dispatch_request) フックを使って拡張しているコード
  • パフォーマンス・リソース観点

    • Rails アプリの起動時(特にミドルウェアスタック構築時)に ActionDispatch::Request がロードされなくなるため、
      • 起動時間がわずかに短縮される可能性
      • action_dispatch_request の load hook に重い処理を仕込んでいるアプリでは、その処理が「初期化時」ではなく「実際にリクエストが必要になったタイミング」まで遅延される
        が期待されます。
  • 互換性・注意点

    • 公開APIとしては ActionDispatch::Request 自体や Flash/Session の挙動は変わらない前提です。
    • ただし、ActionDispatch::Request がブート時点で必ずロードされていることを前提にしていた独自コード(例えば「初期化処理の中で ActionDispatch::Request を reopen してパッチする」など)がある場合は、動作タイミングが変わる可能性があります。
      • そういったコードは、以下のように ActiveSupport.on_load(:action_dispatch_request) を使う形に書き換えるのが推奨です:

        ruby
        # config/initializers/action_dispatch_request_patch.rb
        ActiveSupport.on_load(:action_dispatch_request) do
          # ここで Request を拡張する
          ActionDispatch::Request.include MyRequestExtensions
        end

  1. 参考情報 (あれば)
  • 元になった議論: https://github.com/rails/rails/pull/56201
    ここで「ミドルウェア構築時の request ロード」が話題になり、その副作用として load hook が起動時に発火してしまう問題が発見されています。
  • ActiveSupport.on_load のパターンは、Rails コアでも既に広く使われている慣習であり、今回の変更もそれに沿って「autoload にフックする」方向へ揃えたものと言えます。

#56347 Fix guides/bug_report_template_test not running with rake

マージ日: 2025/12/11 | 作成者: @zzak

  1. 概要 (1-2文で)
    Rails の guides 用テスト (guides/bug_report_template_test) が bin/test では動くのに rake test では実行されていなかった問題を修正した PR です。あわせて、guides のテスト用 Rake タスク定義を、フレームワーク本体のテストタスクと同じスタイルに整理しています。

  1. 変更内容の詳細

問題の背景

  • bin/test で実行した場合はテストが動くのに、rake test ではテストが 0 件になる状態だった。
  • 原因は autorun がロードされていない ためで、rake test 側では Minitest の自動実行が働かず、テストが実行されていなかった。

guides/test/test_helper.rb の変更

test_helper に autorun 読み込みを追加していると考えられます:

ruby
# guides/test/test_helper.rb

require "minitest/autorun"  # ← このような行が追加されている

これにより、rake test 経由でテストをロードした場合でも、Minitest が自動的に実行されるようになります。

guides/Rakefile の変更

guides 用の Rake タスク定義を、railties/Rakefile など他のコンポーネントと同じパターンにそろえています。おおよそ以下のような方向の変更です:

  • Rake::TestTask の使い方を、本体側と同じスタイルに修正
  • テストファイルのパターン指定や load path 設定を整理
  • 余計な独自処理をなくし、他コンポーネントと一貫性を持たせる

例として、他コンポーネントのテストタスクに近い形:

ruby
# guides/Rakefile(イメージ)

require "rake/testtask"

Rake::TestTask.new(:test) do |t|
  t.libs << "test"
  t.test_files = FileList["test/**/*_test.rb"]
end

このような形に寄せることで、rake test の挙動が Rails 本体とそろい、予期せぬ「テスト 0 件」状態を避けられます。


  1. 影響範囲・注意点
  • 影響範囲は Rails guides のテスト (特に bug_report_template_test) に限定されます。アプリケーションの通常のテスト (bin/rails test, rake test) への影響はほぼありません。
  • guides のテストを rake test で回している開発者・CI 環境では、これまで 0 件だったテストが 正しく実行されるようになる ため、失敗テストが顕在化する可能性があります。
  • minitest/autorun の二重ロードなどによる副作用は一般的には問題になりませんが、独自に Minitest 実行フローをいじっている場合は念のため挙動を確認するとよいです。
  • guides 下の Rake タスク定義をカスタマイズしている場合は、今回の変更との差分を見て、必要に応じて自分のプロジェクト側もタスク定義を整理する価値があります。

  1. 参考情報 (あれば)

#56252 [ci skip] Improve docs about system test generation

マージ日: 2025/12/11 | 作成者: @callmesangio

  1. 概要 (1-2文で)
    このPRは、Railsのシステムテスト関連ドキュメント(Testing GuideおよびAPIドキュメント)の内容を、現在の挙動に合わせて改善・修正したものです。特に「system test は自動生成されない」「application_system_test_case.rb はデフォルトでは作られない」という点を明確にしています。

  1. 変更内容の詳細

テストガイド (guides/source/testing.md) の修正

(1) 「Test Setup」セクションの ls -F test 出力を実際の構成に合わせて更新

  • rails new 直後などで ls -F test を実行したときに表示される内容が、現行のRailsのディレクトリ構成と一致するようにコードブロックを修正しています。
  • 具体的には「system test 関連のファイルやディレクトリ」が、デフォルトでは存在しない状態を反映した出力になったと考えられます。

例(イメージ・実際のPRでは具体的な中身が更新されている):

bash
$ ls -F test
controllers/
models/
test_helper.rb

のように、system/application_system_test_case.rb が最初からは存在しないことを示す方向に修正。

(2) 「Test Directories」セクションの文章を再構成

  • 「system test 関連の内容(ディレクトリ・基底クラスなど)は、自動で用意されるのではなく“明示的に生成する必要がある”」ことを強調するように文言が書き換えられました。
  • これにより、以下のような誤解を避けます:
    • 新規アプリ作成時に test/systemapplication_system_test_case.rb が勝手にできているはず、という期待
    • scaffold で system test が勝手に生成される、という期待

(3) 「Generating System Tests」セクションのレンダリング不具合修正

  • ガイド中の「Generating System Tests」セクションで、Markdownの崩れやコードブロックの表示不具合があったため、それを修正。
  • これにより、rails g system_test ... などのコマンド例や出力例が正しく整形されて表示されます。

(4) system test 生成コマンドの出力例を更新

  • rails g system_test などを実行したときに生成されるファイル一覧のコードブロックを、現行バージョンの挙動に合わせて更新しています。
  • 例として、初回のシステムテスト生成時に application_system_test_case.rbtest/system ディレクトリが同時に生成されることなどが、正確な形で示されるようになります。

例(イメージ):

bash
$ bin/rails generate system_test articles
      invoke  test_unit
      create  test/system/articles_test.rb
      create  test/application_system_test_case.rb  # 初回生成で作られる

Testing Guide / APIドキュメント全体の整合性修正

(5) application_system_test_case.rb が「デフォルトで生成されない」ように記述を修正

  • 以前のドキュメントには、以下のような前提が含まれていたと考えられます:
    • 新規アプリ (rails new) で最初から test/application_system_test_case.rb がある
    • scaffold 生成で application_system_test_case.rb も用意される
  • 実際には、Railsの最近のバージョンでは:
    • 新規アプリ作成時には application_system_test_case.rb は生成されない
    • 最初に rails g system_test ... などを実行したときに初めて生成される
  • この実態に合わせて、ガイドとactionpack側APIドキュメント(ActionDispatch::SystemTestCase 周辺のコメント)を修正しています。

actionpack/lib/action_dispatch/system_test_case.rb の変更

  • このファイルは ActionDispatch::SystemTestCase の定義およびコメントを含むクラスです。
  • 今回の変更は +3 / -3 行と小さく、おそらく以下のようなコメント・ドキュメント面の修正です:
    • application_system_test_case.rb は新規アプリ/スキャフォールドで自動生成される」といった古い記述の削除・修正
    • もしくは、「アプリケーション側で ApplicationSystemTestCase を定義する際の推奨パス/ファイル名」の説明更新

コード自体のロジック変更ではなく、説明文の整合性合わせと見てよいです。


  1. 影響範囲・注意点
  • ランタイム挙動への影響はほぼ無し

    • 変更はガイドとコメント中心で、実際のテスト実行やシステムテストのAPIには変更がありません。
    • 既存アプリの挙動が変わることはありません。
  • ドキュメントを参照してセットアップしている開発者への影響

    • これまで古いドキュメントを信じて「application_system_test_case.rb がない、バグ?」と思っていた開発者が、今回の修正で正しい手順(system test の明示的生成)を理解しやすくなります。
    • CI設定や新規開発時に、system test を使うつもりなら:
      • bin/rails generate system_test NAME
      • あるいは何らかのgeneratorを通じて、一度 system test を生成する
        といったステップが必要であることをドキュメントで確認しやすくなります。
  • 既存の社内ドキュメントや教材を持っている場合

    • rails new 直後に application_system_test_case.rb がある前提」で書かれている資料は、Rails本体の最新ドキュメントと食い違う可能性があります。
    • 教材・Wiki・テンプレートを持っているチームは、今回のPR内容に沿って表現を修正すると混乱が減ります。

  1. 参考情報 (あれば)
  • フォローアップ対象となったPR:

    • #55743
    • #56272
      これらで導入/変更されたsystem test関連仕様に対して、説明の抜けや古い記述を補正する目的のPRです。
  • 実際にsystem testを有効化する際の実務的ポイント:

    • 新規プロジェクトでsystem testを使いたい場合、まずは:
      bash
      bin/rails generate system_test sample
      などを一度実行して、test/application_system_test_case.rbtest/system を生成してから他のsystem testを追加していくとスムーズです。

#55294 [RF-DOCS][ci skip] Update Threading and Code Execution Guide

マージ日: 2025/12/11 | 作成者: @OughtPuts

  1. 概要 (1-2文で)
    このPRは Rails ガイド「Threading and Code Execution (スレッドとコード実行)」を大幅にアップデートし、非同期 Active Record や Isolated Execution State、CurrentAttributes など、現行 Rails のスレッド関連機能を整理・追記したドキュメント改善です。ガイドの表現・構成も見直され、より読みやすく実践的な内容になっています。

  1. 変更内容の詳細

※コードの挙動を変える PR ではなく、ガイドの内容・構成を更新するものです。

2-1. ガイド全体の書き換え・構成変更

  • 文言を平易にしつつ、スレッドや並行実行に慣れていない Rails 開発者でも理解しやすいように説明をリライト。
  • セクション構成の見直しにより、
    • 「どこまでが Rails が面倒を見る部分か」
    • 「アプリ側が注意すべきスレッド安全性や状態管理」 が順序立てて理解しやすくなるように再構成。
  • 以前付いていた「work in progress (作業中)」ラベルを削除し、正式なガイドとして位置づけ。

2-2. 非同期 Active Record に関する情報の追加

Rails 7 以降で強化されている「非同期なデータベース操作」について、スレッド&実行モデルの観点から説明が追加されています。

典型的には以下のようなポイントが整理されていると考えられます:

  • ActiveRecord::Base やクエリメソッドの非同期 API(例: load_async)がどのようなスレッドで動くか
  • その非同期処理が Rails のエグゼキューションコンテキスト (Current attributes, I18n, timezone など) を引き継ぐのかどうか
  • 非同期クエリを使うときに「スレッド安全性」や「接続プール」にどのような影響があるか

イメージしやすいサンプル:

ruby
# コントローラ内など
def index
  # 非同期にロード
  @users = User.where(active: true).load_async

  # ここでは他の処理を先に進める
  do_some_heavy_logic

  # ビューで @users を参照したタイミングで結果が利用可能になる
end

ガイドでは、こうした非同期処理が裏側でどのようなスレッドを使うか、アプリ開発者は何に気を付けるべきか、が説明されているはずです。

2-3. ラップされたアプリケーションコードの例の追加

「コードがどのように『ラップされて』実行されるか」の具体例が増えています。
これは、Rails が各種ミドルウェアやエグゼキューションラッパーを通してアプリケーションコードを実行する構造を解説するものです。

典型的には以下のような点がサンプルコード付きで整理されます:

  • Rails.application.executor.wrap などで実行コンテキストを管理する仕組み
  • スレッドプール上でコードを実行する際に「必ず Executor で wrap すべき」という推奨パターン

サンプルイメージ:

ruby
Rails.application.executor.wrap do
  # このブロックの中は:
  # - CurrentAttributes
  # - I18n.locale
  # - Time.zone
  # などが正しく設定・リセットされる
  UserMailer.welcome(user).deliver_later
end

あるいは、独自スレッドを立てる場合のパターン:

ruby
Thread.new do
  Rails.application.executor.wrap do
    # 非同期・バックグラウンドで安全に実行
    SomeService.call
  end
end

こうした例を通して、「Executor でラップしないと、CurrentAttributes のリークやゾーン/ロケールの取り違えが起こり得る」という実務的な注意点が解説されていると思われます。

2-4. Isolated Execution State に関する情報の追加

新しく「Isolated Execution State」に関する節が追加されています。
これは、マルチスレッド環境で「リクエストごと」「ジョブごと」の状態を安全に隔離するための仕組みを説明するものです。

ポイント:

  • 各スレッドごとに「現在のユーザー」「タイムゾーン」「CurrentAttributes などの request/job 単位の状態」を持つための抽象化
  • スレッド間で共有されてはならない状態を、Thread-local ではなく「より高レベルな API」経由で扱えるようにする設計意図
  • Executor の before/after hooks や Active Support の内部構造との関連

これにより、「グローバル変数やクラス変数ではなく、Isolated Execution State を前提に設計された API (CurrentAttributes など) を使うべき」という設計指針が明確になります。

2-5. CurrentAttributes セクションの追加

ActiveSupport::CurrentAttributes に関する独立した節が新たに設けられています。

おそらく以下のような内容が整理されています:

  • CurrentAttributes は「リクエスト単位」「ジョブ単位」での状態を表現するための公式な仕組みであり、スレッドセーフに動作する
  • グローバル変数やクラスインスタンス変数の代わりに Current オブジェクトを使うことで、並行実行に耐えられる設計になる
  • Executor/Isolated Execution State との統合により、各スレッド・各実行単位で値が正しくセット・クリアされる

サンプルイメージ:

ruby
# app/models/current.rb
class Current < ActiveSupport::CurrentAttributes
  attribute :user, :request_id
end

# コントローラなど
class ApplicationController < ActionController::Base
  before_action do
    Current.user = current_user
    Current.request_id = request.uuid
  end
end

# どこからでも
Current.user       # => 現在のリクエストの user
Current.request_id # => 現在のリクエストの ID

ガイドでは、「Current をどう定義し、どのようにセット/リセットされるか」「非同期処理やバックグラウンドジョブに引き継ぐパターン・引き継がれないケース」などが説明されていると考えられます。

2-6. documents.yaml からのラベル削除

  • guides/source/documents.yaml から "work in progress" ラベルに相当する記述が削除され、このガイドが正式な完成度のドキュメントとして扱われるようになりました。
  • これにより、利用者は「未完成の情報かもしれない」という懸念なしに参照しやすくなります。

  1. 影響範囲・注意点
  • 実行コードには一切変更なし
    変更はすべて guides/source 配下であり、Rails 本体の挙動・API・パフォーマンスには影響しません。

  • 正しい使い方がより明示される

    • 非同期 Active Record、独自スレッド、バックグラウンド処理などで「Executor で wrap する」「CurrentAttributes を使う」といった推奨パターンがドキュメントとして明確になるため、今後の新規コードでは「暗黙の Thread-local 依存」や「グローバル状態のリーク」を避けやすくなります。
    • 既存アプリで独自にスレッドを使っている場合、このガイドを読むと「やるべき初期化やクリーンアップ」がわかり、リファクタリングの指針になります。
  • ドキュメントとコードベースの整合性

    • ガイドは最新の Rails 実装 (特に Executor / Isolated Execution State / CurrentAttributes / 非同期 Active Record) を前提に書かれていると考えられるため、古い Rails バージョンでは一部 API が存在しない/挙動が異なる可能性があります。
    • 自分のアプリの Rails バージョンとガイドの対象バージョン (edge / stable) を確認して読む必要があります。

  1. 参考情報 (あれば)

この PR 自体はドキュメント変更のみですが、非同期処理・マルチスレッドを使うアプリでは一度読み通しておくと設計・デバッグに役立つ内容です。


#56345 Add SVG renderer

マージ日: 2025/12/11 | 作成者: @thiagoyoussef

  1. 概要 (1-2文で)
    Rails に format.svg { render svg: ... } という形で利用できる SVG レンダラーが追加され、コントローラの respond_to で SVG を第一級のレスポンス形式として扱えるようになりました。これに伴い、MIME type 定義とテスト、CHANGELOG が更新されています。

  1. 変更内容の詳細

2-1. SVG レンダラーの追加

actionpack/lib/action_controller/metal/renderers.rb に SVG 用のレンダラーが追加されています。
使い方は PR のサンプルのとおりです。

ruby
class Page
  def to_svg
    qr_code # SVG の文字列を返す想定
  end
end

class PagesController < ActionController::Base
  def show
    @page = Page.find(params[:id])
    respond_to do |format|
      format.html
      format.svg { render svg: @page }
    end
  end
end

ポイント:

  • render svg: @page と書くと、@page.to_svg の結果がレスポンスボディになる設計(to_jsonto_xml と同じパターン)。
  • respond_to ブロック内で format.svg を使えるようになり、Accept: image/svg+xml を受け取った場合のコンテンツネゴシエーション対象になる。

内部的には以下のようなことが行われていると考えられます(疑似コード):

ruby
ActionController::Renderers.add :svg do |object, options|
  data = object.respond_to?(:to_svg) ? object.to_svg : object.to_s

  self.content_type ||= Mime[:svg]
  self.response_body = data
end

※ 実際の実装は PR の diff ですが、既存の :json / :xml レンダラーと同様のパターンです。

2-2. MIME type の追加・調整

actionpack/lib/action_dispatch/http/mime_types.rb で SVG の MIME type が定義されています。
既存の Mime::Type 一覧に SVG (image/svg+xml) が含まれ、format.svg で利用可能になります。

変更は 1 行のみですが、以下のいずれか(または同等)が追加/修正されています。

ruby
Mime::Type.register "image/svg+xml", :svg

これにより:

  • respond_to :html, :svgsvg を指定できる
  • Mime[:svg] / Mime::SVG 相当を利用できる
  • format.svg ブロックが Accept: image/svg+xml と連動する

2-3. テスト追加

actionpack/test/controller/renderers_test.rb に SVG レンダラー向けのテストが追加されています。

内容としては:

  • render svg: "..."Content-Type: image/svg+xml になること
  • オブジェクトを渡した場合に to_svg が呼び出され、その戻り値がレスポンスボディになること

など、JSON/XML レンダラーと同レベルの確認が行われていると考えられます。

2-4. CHANGELOG 更新

actionpack/CHANGELOG.md に以下のような項目が追記されています。

  • SVG レンダラーが追加されたこと
  • 対応バージョンのセクションに「render svg: が使える」旨の記述

これにより、利用者がリリースノートからこの機能を把握できるようになっています。


  1. 影響範囲・注意点

影響範囲

  • ActionController::Base を継承するコントローラで、標準機能として SVG レスポンスを扱えるようになります。
  • respond_to/respond_with でのフォーマット選択に :svg が追加されることにより、Accept ヘッダが image/svg+xml のクライアントへの応答が変わる可能性があります(既にカスタムで :svg 定義をしていた場合は特に注意)。

注意点・利用時のポイント

  • 返却するオブジェクトに to_svg を実装しておくと、render svg: @object で自然に使えます。to_svg が無い場合は to_s にフォールバックする実装が想定されます。
  • セキュリティ:
    • SVG は XML ベースでスクリプトや外部参照を含み得るため、ユーザー入力をそのまま出力する場合は XSS / 外部リソース読み込みに注意が必要です。
    • 事前にサニタイズする、静的テンプレート+安全なデータ差し込みに限定するなどの対策が望まれます。
  • 既にアプリ側で独自に Mime::Type.register "image/svg+xml", :svg や独自レンダラーを定義している場合、この変更と衝突する可能性があります。その場合は:
    • アプリ側の独自定義を削除して Rails 標準機能に寄せる
    • どうしても挙動を変えたい場合は、ActionController::Renderers.add :svg を再定義する

  1. 参考情報 (あれば)

#56329 [ci skip][docs] Fix RDoc markup for DATE_FORMATS constant references

マージ日: 2025/12/11 | 作成者: @Yuhi-Sato

  1. 概要 (1-2文で)
    このPRは、DATE_FORMATS 定数への言及に対して RDoc 用のマークアップ(+DATE_FORMATS+)を付けることで、日付・日時関連のドキュメントコメントを整備・統一したものです。コードの挙動には一切変更はなく、ドキュメント生成時の表示改善のみを目的としています。

  1. 変更内容の詳細

対象は ActiveSupport の日付・時刻拡張のドキュメントコメントで、以下の3ファイルです。

  • activesupport/lib/active_support/core_ext/date/conversions.rb
  • activesupport/lib/active_support/core_ext/date_time/conversions.rb
  • activesupport/lib/active_support/core_ext/time/conversions.rb

それぞれのファイルで、RDocコメント内の「DATE_FORMATS 定数」への参照を、RDocの定数マークアップに修正しています。

例(イメージ、実際の対象コメントを簡略化):

修正前:

ruby
# You can add your own formats to the DATE_FORMATS hash.

修正後:

ruby
# You can add your own formats to the +DATE_FORMATS+ hash.

RDoc では +定数名+ という形で記述することで、HTML生成時に等幅フォント&コードとして扱われ、他の場所ですでに使われていた表記(+DATE_FORMATS+)と揃うようになります。
このPRは、Date, DateTime, Time の各 conversions 拡張で同様のコメント表現を統一するものです。


  1. 影響範囲・注意点
  • 影響範囲

    • ドキュメントコメントのみの変更であり、Rubyコード(挙動・API・署名)には一切影響しません。
    • 生成される RDoc/HTML ドキュメント上での見た目・スタイルが変わる(DATE_FORMATS がコードとして強調される)だけです。
    • 既存アプリケーションやテストへの影響はありません。
  • 注意点

    • DATE_FORMATS 自体の仕様や利用方法(例: Time::DATE_FORMATS[:short] を追加するなど)は何も変わっていません。ドキュメントの見栄えと一貫性の改善のみです。
    • Rails内の他の箇所(ActiveRecord::Integration, ActiveSupport::TimeWithZone など)ですでに使われていた表現に合わせた統一なので、新しい書き方を導入したわけではありません。

  1. 参考情報 (あれば)
  • RDocのインラインコード/定数マークアップ:
    • +CONSTANT+, +method_name+ のように +...+ で囲むとインラインのコード扱いになります。
  • Rails側の既存スタイル:
    • ActiveRecord::Integration, ActiveSupport::TimeWithZone など、日付・時刻関連の他の箇所ではすでに +DATE_FORMATS+ の表記が使われており、今回のPRでそのスタイルに合わせて整えています。

#56344 Fix Inflections.instance_or_fallback to properly find :en fallback

マージ日: 2025/12/11 | 作成者: @Saidbek

  1. 概要 (1-2文で)
    Rails 8.1 以降で、カスタムロケール使用時に pluralize ヘルパーが英語 (:en) へのフォールバックを正しく参照できなくなっていた不具合を修正する PR です。ActiveSupport::Inflector::Inflections.instance_or_fallback がフォールバックチェーン内の :en を正しく扱うように変更されています。

  1. 変更内容の詳細(あればサンプルコードも含めて)

問題の背景

  • Rails 8.1 でのパフォーマンス最適化(コミット 2ab34cd)により、英語の Inflections は
    • 以前: @__instance__
    • 以後: @__en_instance__ に保存されるようになりました。
  • しかし、Inflections.instance_or_fallback(locale) はフォールバックチェーンを辿る際に @__instance__ しか見ていなかったため、
    • 例: フォールバックチェーンが [:custom, :en] のとき
    • :en を探しに行っても @__en_instance__ を見に行かない
    • 結果として、「customen → …」というフォールバックにおける英語の規則が見つからない
  • これにより、pluralize ヘルパーがカスタムロケールで英語規則へフォールバックすべき場面でも、正しく動作しないケースが発生していました(Issue #56339)。

修正内容

activesupport/lib/active_support/inflector/inflections.rb に 1 行の修正が入り、instance_or_fallback がフォールバック処理中に :en を見つけた場合、@__en_instance__ を参照するように変更されています。

擬似コードイメージ(実際のコードを読みやすく意訳):

ruby
def self.instance_or_fallback(locale)
  # フォールバックチェーンを辿る
  I18n.fallbacks[locale].each do |fallback_locale|
    if fallback_locale == :en
      # ここで @__en_instance__ を見るように修正
      return @__en_instance__ if defined?(@__en_instance__)
    else
      # それ以外のロケールは従来通りインスタンスを参照
      return @__instance__ if defined?(@__instance__) && locale == fallback_locale
    end
  end
end

(正確な構文は若干異なりますが、要点は「:en だけ特別扱いして @__en_instance__ を参照する」点です)

テストの追加

activesupport/test/inflector_test.rb に 10 行のテストが追加され、次のようなケースが検証されています。

  • カスタムロケール(例: :custom)に対して、I18n のフォールバックチェーンが [:custom, :en] となるように設定
  • pluralize(あるいは Inflector のメソッド)を呼び出したときに、custom 側にルールがなくても、en の Inflections が正しく使われることを確認

イメージ:

ruby
I18n.available_locales = [:custom, :en]
I18n.fallbacks.map(custom: [:custom, :en])

assert_equal "posts", "post".pluralize(locale: :custom)
# custom で定義がなくても、en の規則で "posts" になることを確認

  1. 影響範囲・注意点
  • 影響を受けるのは以下のようなケースです:
    • Rails 8.1 以降
    • カスタムロケール(例: :ja, :custom など)を使っている
    • そのロケールのフォールバックに :en が含まれている(例: [:ja, :en]
    • カスタムロケール側には該当する Inflections 規則がないが、:en にはある
  • この修正により、
    • 以前のバージョン(8.1 導入前)と同じように、フォールバックとしての英語 Inflections が再び機能する
    • 既存の英語ロケールの挙動は変わらない
    • パフォーマンス最適化(@__en_instance__ を分ける)自体は維持されたまま、参照だけが正しくなった形
  • 既に 8.1 の挙動に合わせて「英語に頼らないようカスタム Inflections を追加した」プロジェクトがあっても、そのルールは引き続き優先的に使われるため、後方互換性上の問題は小さいと考えられます。

  1. 参考情報 (あれば)
  • 対応 Issue: Fixes #56339
    pluralize がカスタムロケールで英語フォールバックを無視する不具合)
  • 影響箇所:
    • ActiveSupport::Inflector::Inflections.instance_or_fallback
    • pluralize ヘルパーや ActiveSupport::Inflector をロケール付きで利用するコード全般
  • 関連コミット: 2ab34cd
    英語 Inflections を @__en_instance__ に分離するパフォーマンス最適化が元原因。

#56335 Analyze attachments before validation

マージ日: 2025/12/10 | 作成者: @jeremy

  1. 概要 (1–2文で)
    Active Storage の添付ファイル(画像・動画等)のメタデータ(幅・高さ・再生時間など)を「モデルのバリデーション前」に取得できるようになり、avatar.metadata[:width] などを使ったサーバーサイドのバリデーションが素直に書けるようになりました。あわせて、メタデータ解析のタイミングを :immediately / :later / :lazily から選べる新オプションが導入されています。

  1. 変更内容の詳細

2-1. バリデーション前にメタデータが利用可能に

これまで Active Storage の metadata は、アップロード後に非同期で解析されたり、最初のアクセス時に解析されたりとタイミングが不定で、「バリデーション時点ではまだ width/height が入っていない」問題がありました。

このPRでは、(ローカルIO経由のアップロードに対して)バリデーション実行前にメタデータ解析を行うように変更され、次のようなコードが安全に書けます:

ruby
class User < ApplicationRecord
  has_one_attached :avatar

  validate :validate_avatar_dimensions, if: -> { avatar.attached? }

  def validate_avatar_dimensions
    if avatar.metadata[:width] < 200 || avatar.metadata[:height] < 200
      errors.add(:avatar, "must be at least 200x200")
    end
  end
end

validate が走る時点で avatar.metadata に幅・高さなどが入っていることを前提にできるようになりました(後述の例外: 直接アップロード時)。

2-2. メタデータ解析タイミングの設定オプション analyze:

添付関連の関連定義 (has_one_attached / has_many_attached) で、解析タイミングを指定できるオプションが追加されています。

ruby
has_one_attached :document, analyze: :later
has_many_attached :files, analyze: :lazily

利用可能な値:

  • analyze: :immediately
    • 8.2 のデフォルト
    • ローカルIO経由のアップロードの場合、保存前(=バリデーション前)にメタデータ解析を行う。
    • これによりメタデータに基づいたバリデーションが同期的に実行可能。
  • analyze: :later
    • ファイル保存後に解析する。
    • ローカルIOの場合もアップロードの直後に非同期(または別フェーズ)で解析。
    • バリデーション時点ではメタデータが利用できない可能性がある。
  • analyze: :lazily
    • 自動解析を行わない。
    • attachment.analyze を明示的に呼ぶまで解析されない。
    • 完全にオンデマンドで解析タイミングをコントロールしたいとき向け。

2-3. 全体デフォルト設定の追加

アプリケーションレベルでデフォルトを指定する設定が追加されています:

ruby
# config/application.rb など
config.active_storage.analyze = :later  # :immediately / :later / :lazily から選択

また、new_framework_defaults_8_2.rb にも関連の初期値が追記されており、Rails 8.2 でのデフォルトは :immediately になるよう準備されています。

2-4. Direct Upload(直接アップロード)時の挙動

Direct Upload(ブラウザからクラウドストレージへ直接アップロードし、Railsサーバーはメタデータ取得のためのローカルファイルを持たないケース)では:

  • サーバー側にファイル実体がないため、アップロード直後にはメタデータ解析ができない
  • このため、analyze: :immediately が指定されていても、Direct Upload 経由のファイルでは内実として :later にフォールバックし、アップロード完了後にバックグラウンドジョブで解析される。
  • 結果として、
    • バリデーション時点でメタデータはまだ無い
    • メタデータに基づく制限(解像度など)をサーバー側で保証したい場合は、クライアント側での事前バリデーションや、解析完了後の別フェーズでのチェックが必要

PRの説明にもある通り、このケースでは「メタデータを使うようなバリデーションはクライアント側で行ってほしい」というスタンスです。

2-5. 内部実装まわりの変更ポイント(概要)

※主な影響箇所のみ簡潔に:

  • ActiveStorage::Attachment / ActiveStorage::Attached::Changes::CreateOne などで、レコード作成時のメタデータ解析フローを変更。
  • ActiveStorage::Attached::Modelanalyze オプションを受け取り、関連リフレクション経由で添付ごとの設定として保持。
  • engine および configuration クラス (Rails::Application::Configuration) に config.active_storage.analyze を追加。
  • ガイド (active_storage_overview.md, configuring.md) に新オプションと推奨の使い方を追記。
  • テスト一式の追加・更新により、新挙動とオプション組み合わせの検証が行われている。

  1. 影響範囲・注意点

3-1. Rails 8.2 へのアップグレード時の挙動変化

  • デフォルトが :immediately になるため、既存アプリで Active Storage を使っている場合:
    • 保存処理のたびにメタデータ解析が「バリデーション前に」同期的に走るようになります。
    • ファイルサイズが大きい、または解析コストの高いファイル(動画など)を扱う場合、保存レスポンスが遅くなる可能性があります。
    • パフォーマンスやUXを優先したいなら、グローバルまたは添付ごとに analyze: :later:lazily を検討すべきです。

3-2. バリデーションロジックの書き方

  • analyze: :immediately を前提としたメタデータ依存バリデーションは、基本的にローカルIOアップロードでは安全に機能します。
  • Direct Upload を利用している場合:
    • avatar.metadata[:width] などは、バリデーション時点で nil または未設定となる可能性が高いです。
    • クライアント側で画像サイズのチェックを行うか、
    • またはメタデータが揃った後に別の非同期ジョブで検証・再処理する設計にする必要があります。
  • マルチアップロード (has_many_attached) で :immediately を使うと、添付数が多いほど保存時の解析コストが積み上がるので注意してください。

3-3. マイグレーション・互換性

  • 既存の has_one_attached / has_many_attached 宣言はそのままでも動作しますが、Rails 8.2 での挙動変化(解析タイミングの同期化)を意識する必要があります。
  • 解析をこれまで通り「後で」やりたい場合、以下のいずれかを明示的に設定してください:
    • 全体設定: config.active_storage.analyze = :later
    • 個別設定: has_one_attached :avatar, analyze: :later
  • メタデータ未設定状態を前提としていた既存コードがある場合(「初回アクセス時に解析されるはず」という前提)も挙動が変わるため、依存関係がないか確認した方が安全です。

  1. 参考情報 (あれば)
  • 変更の中心となるクラス/ファイル:
    • activestorage/app/models/active_storage/attachment.rb
    • activestorage/lib/active_storage/attached/model.rb
    • activestorage/lib/active_storage/attached/changes/create_one.rb
    • railties/lib/rails/application/configuration.rb
    • config/initializers/new_framework_defaults_8_2.rb テンプレート
  • ドキュメント:
    • guides/source/active_storage_overview.md
      • メタデータ解析と analyze オプションの使い方が追記。
    • guides/source/configuring.md
      • config.active_storage.analyze についての説明が追加。
  • 運用設計の目安:
    • サーバーサイドのメタデータバリデーションを重視 → :immediately
    • レスポンス速度・バックグラウンド処理優先 → :later
    • 完全に手動制御したい、独自ジョブで解析したい → :lazily

#56017 Add Rails::CodeStatistics#register_extension to register file extensions for rails stats

マージ日: 2025/12/10 | 作成者: @taketo1113

  1. 概要 (1-2文で)
    rails stats が対象とするファイル拡張子を、安全に追加できる Rails::CodeStatistics.register_extension が新設されました。これにより、将来のデフォルト拡張子の変更と競合せずに、独自や gem 由来の拡張子を統計対象に含められます。

  1. 変更内容の詳細

背景整理

  • 以前の PR (#55734) で、rails stats がどの拡張子を対象にするかを Rails::CodeStatistics.pattern で上書きできるようになっていました。

  • しかし pattern を「丸ごと上書き」する方式だと、Rails 本体側で将来デフォルトの PATTERN に拡張子(例: .txt)が追加されても、その変更を取り込めません。

    ruby
    # 例: デフォルト PATTERN に .txt が後から追加されたとする
    # PATTERN = /^(?!\.).*?\.(rb|js|ts|css|scss|coffee|rake|erb|txt)$/
    
    # 既存アプリがこう上書きしていた場合
    Rails::CodeStatistics.pattern = /^(?!\.).*?\.(rb|js|ts|css|scss|coffee|rake|erb|slim)$/
    
    # → .txt が統計対象から漏れる
  • サードパーティー gem(例: slim-rails, haml-rails など)が自分の拡張子を追加したい場合にも、既存の pattern を再定義してしまうため「追加」ではなく「上書き」になってしまう問題がありました。

新しく追加された機能

Rails::CodeStatistics に拡張子を登録するためのメソッドが追加されました。

ruby
Rails::CodeStatistics.register_extension("slim")

ポイント:

  • 「デフォルト PATTERN + 自分の拡張子」という形で、既定の挙動を保ったまま拡張子を増やせる
  • 内部的には、register_extension で登録された拡張子を pattern に反映する形になっており、Rails 側がデフォルトの PATTERN を将来変更しても、それに追随できます。
  • 既存の Rails::CodeStatistics.pattern の直接上書きは 引き続きサポートされる(互換性維持)。

テスト・実装の方向性

  • railties/lib/rails/code_statistics.rbregister_extension 実装を追加。
  • railties/test/code_statistics_test.rb に、登録した拡張子が rails stats 対象になることを確認するテストが追加。
  • railties/CHANGELOG.md に、この新機能のエントリが追記されています。

※PR 本文から読み取れる範囲では、register_extension は「文字列の拡張子名(例: "slim")」を受け取り、pattern に組み込む仕組みになっています。


  1. 影響範囲・注意点
  • アプリ側:

    • これまで Rails::CodeStatistics.pattern を丸ごと上書きして独自拡張子を追加していた場合は、今後は register_extension を使う方が安全です。

      ruby
      # 旧: PATTERN の直接上書き
      Rails::CodeStatistics.pattern = /^(?!\.).*?\.(rb|js|ts|css|scss|coffee|rake|erb|slim)$/
      
      # 新: デフォルトを維持したまま追加
      Rails::CodeStatistics.register_extension("slim")
    • 既に pattern を上書きしているプロジェクトは、そのままでも動きますが、将来の Rails 更新で新しいデフォルト拡張子が増えた際に取りこぼしたくなければ、register_extension ベースに移行した方がよいです。

  • gem 開発者側:

    • slim-rails やテンプレートエンジン系 gem は、railtie の初期化時などに:

      ruby
      Rails::CodeStatistics.register_extension("slim")

      を呼べば、他の gem やアプリの設定を壊さずに自分の拡張子を rails stats に加えられます。

    • 以前のように Rails::CodeStatistics.pattern = を gem 側で定義してしまうと、アプリ側・他の gem 側の上書きと競合するので、今後は register_extension 一択が望ましいです。

  • 互換性:

    • pattern の直接上書きは残っているため、Rails 8.1.0 で既に導入されているコードも壊れません。
    • 将来的に pattern を非推奨にする可能性は示唆されていますが、現時点ではそのような変更は行われていません。

  1. 参考情報 (あれば)
  • 本 PR:
    • Add Rails::CodeStatistics#register_extension to register file extensions for rails stats (#56017)
  • 関連 PR:

#56323 Ensure generated yml files do not have extra blank lines

マージ日: 2025/12/10 | 作成者: @jeromedalbert

  1. 概要 (1-2文で)
    Rails 8.1.1 で生成される deploy.ymldatabase.yml に紛れ込んでいた不要な空行を削除し、今後同様の空行が紛れ込まないようにテストを追加した PR です。見た目の調整・品質向上が目的で、挙動や仕様の変更はありません。

  1. 変更内容の詳細

対象ファイル

  • railties/lib/rails/generators/rails/app/templates/config/deploy.yml.tt
  • railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.tt
  • railties/test/generators/app_generator_test.rb

テンプレートから余計な空行を削除

deploy.yml テンプレートと sqlite3database.yml テンプレート (*.yml.tt) の末尾または途中にあった「意味のない空行」が 1 行ずつ削除されています。

イメージとしては次のような差分です(あくまで構造イメージ):

yaml
# 修正前 (例)
default: &default
  adapter: sqlite3
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

# ここに空行があった

development:
  <<: *default
  database: storage/development.sqlite3
yaml
# 修正後 (例)
default: &default
  adapter: sqlite3
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

development:
  <<: *default
  database: storage/development.sqlite3

このような「コード上も見た目上も意味のない空行」をなくし、生成される YAML がすっきりした形になるようにしています。

空行を検出するテストの追加

railties/test/generators/app_generator_test.rb に 11 行のテストコードが追加されています。

内容としては、Rails アプリ生成テストの一部として:

  • rails new 相当のジェネレータでアプリを生成
  • 生成された config/deploy.ymlconfig/database.yml を読み込み
  • 不要な連続空行や末尾の空行が含まれていないか確認

といった検証を行うことで、テンプレート修正時にうっかり空行を挿入してしまう問題を自動テストで検出できるようにしています。


  1. 影響範囲・注意点
  • 影響範囲

    • Rails アプリ生成時 (rails new) に出力される config/deploy.ymlconfig/database.yml のフォーマットのみが変わります。
    • 設定値やキー構造、アプリケーションの挙動には一切影響しません。
    • 既存アプリには影響せず、新規生成またはジェネレータを通じて生成されるファイルにのみ影響があります。
  • 注意点

    • YAML としてのパース結果は変わらないため、デプロイツールや DB 接続には影響ありません。
    • ただし、CI などで「生成ファイルとテンプレートの厳密比較(空白や空行も含めた差分チェック)」をしている場合には、deploy.yml / database.yml の生成結果が 1 行分短くなるなどの違いが出る可能性があります。
    • PR の性質上、CHANGELOG は更新されていません(ユーザーに見える挙動変更ではなく軽微なフォーマット修正のため)。

  1. 参考情報 (あれば)

この PR が対処している背景となる PR:

これらの変更の副作用でテンプレート中に余分な空行が入り込み、本 PR と追加されたテストで再発防止を図っています。


#56327 Active Storage: eager analysis and local file processing for immediate variants

マージ日: 2025/12/10 | 作成者: @jeremy

  1. 概要 (1-2文で)
    Active Storage で process: :immediately を指定したバリアント付き添付ファイルについて、バリデーション時にメタデータ解析(analysis)を即時実行し、かつアップロード直後のローカルファイルを直接使って解析・変換を行うようにした PR です。これにより、画像サイズなどのメタデータを使ったカスタムバリデーションがやりやすくなり、無駄な再ダウンロードも減ります。

  1. 変更内容の詳細

2-1. 「process: :immediately」なバリアントの挙動変更

これまで:

  • Active Storage の添付時に variant を定義していても、
    • 画像の analysis(width/height などのメタデータ取得)
    • variant の生成(リサイズなど)
  • は基本的に遅延実行 (on-demand) で、初回アクセス時に行われることが多かった。

今回の変更:

  • process: :immediately オプションを指定したバリアントを持つ添付については、
    • レコードのバリデーション段階で blob の analysis を実行
    • その結果得られたメタデータを、同じバリデーション中のカスタム検証で利用可能
  • 例: モデル側で画像サイズをチェックしたいケース
ruby
class User < ApplicationRecord
  has_one_attached :avatar do |attachable|
    attachable.variant :thumb, resize_to_limit: [100, 100], process: :immediately
  end

  validate :avatar_must_be_landscape

  private

  def avatar_must_be_landscape
    return unless avatar.attached?
    avatar.analyze unless avatar.analyzed? # ただし PR 後は immediately の場合自動で解析される

    if avatar.metadata["width"] <= avatar.metadata["height"]
      errors.add(:avatar, "は横長画像である必要があります")
    end
  end
end

この PR により、process: :immediately な variant が存在する場合、validate 実行時までに Active Storage 側が blob を解析するようになるため、ユーザーが明示的に analyze を叩かなくてもメタデータを前提にしたバリデーションを書きやすくなります。

ポイント:

  • 「immediately な variant を持つ attachment」が対象で、既存 Blob を attach しただけでは対象にならない(後述)。
  • 解析が eager になるのは、「uploadable io(アップロードされた IO オブジェクト)を attach したとき」に限定。

2-2. ローカルファイルを使った解析・変換(再ダウンロードの削減)

これまで:

  • Active Storage は blob を analyze したり variant を生成したりする際、通常はストレージ(S3 など)から一度ダウンロードして処理していた。
  • アップロード直後にバリデーション → variant を生成、というフローでも、「一旦アップロード → 解析・変換のためにまたダウンロード」という無駄があり得た。

今回の変更:

  • アップロード時の IO(uploadable io)が手元にある間は、それをそのまま解析・変換に利用するようにした。
  • 対象:
    • blob の analysis
    • variant の処理(画像変換など)
  • 対象外:
    • すでに存在する Blob を attach しただけのケース(この場合は従来どおりストレージから取得)

イメージコード(概念的な流れ):

ruby
user.avatar.attach(io: params[:file], filename: "avatar.jpg")

# ここで process: :immediately な variant のための analysis が走る
# 解析・変換時には params[:file] (tempfile 等) を直接使う
# → すぐストレージからダウンロードし直さない

Active Storage 内部変更のポイント:

  • ActiveStorage::Attachment / ActiveStorage::Blob に「uploadable io」を渡して処理するフローが追加。
  • ActiveStorage::Variant / VariantWithRecord でも、可能な場合はローカルの uploadable io を用いて処理するように分岐。
  • attached/changes/create_one.rb など、添付変更オブジェクト周りに、「アップロード時の IO を保持しつつ attach → 即解析・処理」するためのロジックが追加・拡張されている。

2-3. Blob/Attachment/Variant 周りの主な API レベルの変化

PR 全体としては内部実装の変更が主ですが、挙動として開発者が感じる点は次のとおりです。

  1. ActiveStorage::Attachment:

    • attach 処理時に、アップロード IO を保持して analysis/variant 処理に渡すための変更。
    • process: :immediately な variant を検知して、バリデーション中に analysis を走らせるロジックが追加。
  2. ActiveStorage::Blob / Analyzable:

    • analyze 実行時に、「もし uploadable io が指定されていれば、それを使って解析」する経路が追加。
    • そうでない場合は従来どおりサービス(S3 など)からダウンロード。
  3. ActiveStorage::Variant / VariantWithRecord:

    • 変換時に「uploadable io を活用する」経路を追加。
    • バリアント生成時に余計なダウンロードが発生しないようにする。
  4. テスト:

    • Attachment のテストに 100 行以上追加されており、
      • バリデーション時に analysis が行われること、
      • uploadable io を使って処理していること、
    • などが確認されている。
    • 代表表現コントローラテストや integration テストも微調整され、今回の挙動を前提にしたテストになっている。

  1. 影響範囲・注意点

3-1. 想定されるメリット

  • カスタムバリデーションが書きやすくなる:
    • process: :immediately な variant を定義しておけば、画像の width/height といった metadata をバリデーション時に安全に使える。
  • パフォーマンス向上/無駄な通信削減:
    • アップロード直後の処理(analysis・variant生成)でストレージからの再ダウンロードが不要になり、特に S3 などリモートストレージのときに効く。
  • レイテンシ削減:
    • ユーザーが初めて画像を表示するタイミングで重い処理を行わず、事前に済ませたい場合に process: :immediately が機能しやすくなる。

3-2. 既存コードへの影響と注意点

  1. process: :immediately を使っている or 使おうとしているプロジェクト:

    • この PR により、「即時処理」としての意味がより明確になり、バリデーション段階での解析が行われます。
    • すでに image metadata を使ったバリデーションをしている場合、
      • これまで「初回アクセスまで metadata が nil だった」ケースで、今後は metadata が入っている可能性が増えるため、動作が「改善される」方向の変更。
    • ただし、バリデーション中に画像解析が走るということは、
      • バリデーションのレスポンスタイムが若干伸びる可能性があります(画像サイズ・ネットワーク環境にもよる)。
      • 特に大量の添付を一度に処理するフォームでは注意。
  2. 既存 Blob を attach するケース:

ruby
# 既に存在する blob を別モデルに付け直すようなケース
post.image.attach(existing_blob)
  • この場合は、「uploadable io」が存在しないため、従来どおりストレージからダウンロードして analysis/variant 処理を行います。
  • この PR の「ローカルファイル利用による最適化」は適用されない点に注意。
  1. ストレージ負荷・コスト観点:
  • この PR による最適化は「アップロード直後」の処理に限られるため、
    • ユーザーが後から別の画面で初回アクセスする場合など、従来どおりダウンロードが発生するケースも当然残ります。
  • とはいえ、「アップロード直後に即座に処理する」設計のアプリ(たとえば投稿完了時に全バリアントを生成しておく)では、ストレージからの無駄な読み出しが削減され、トラフィックとコストに効きます。
  1. バリデーション・コールバック順序への影響:
  • analysis は「バリデーション中」に走るため、
    • before_validation などのコールバック内で attachment にアクセスしたときの挙動は、以前と若干タイミングが変わる可能性があります。
    • ただし基本的には「より早く metadata が使えるようになった」だけなので、副作用は限定的と思われます。

  1. 参考情報 (あれば)
  • 元 Issue: #51951
    • process: :immediately な variant に関する要望/議論が背景にあると考えられます。
  • この PR (#56327) が入ることで、
    • process: :immediately = 「リクエスト中(少なくともバリデーション中)に解析・処理までやり切る」
      という意味合いがよりはっきりするため、今後の Active Storage 設計時にその前提でパフォーマンスチューニングや UX 設計がしやすくなります。
  • 実際に使う場合の典型的なパターン:
    • サムネイルや特定のバリアントを必ず生成しておきたい
    • 画像の寸法・アスペクト比・ファイルサイズなどでバリデーションしたい
    • アップロード直後に変換を済ませておき、後続のリクエストを軽くしたい

このような要件があるプロジェクトでは、has_one_attached / has_many_attached のブロック内で process: :immediately を積極的に活用する価値があります。


#56331 Backport #56275 to 7-2-stable

マージ日: 2025/12/9 | 作成者: @yahonda

  1. 概要 (1-2文で)
    Rails 7.2 系ブランチ(7-2-stable)に対して、本来取り込むべきだった PR #56275 をバックポートし、Bundler 4.0.0 でテストが落ちる問題に対応する修正です。
    railties のジェネレーター用テストヘルパーを調整し、7-2-stable でも CI が安定して通るようにしています。

  1. 変更内容の詳細
  • 対象ファイル: railties/test/generators/generators_test_helper.rb
  • 実質的には、#56275 の内容を 7-2-stable に移植する際に、既存の PR #53647 とのコンフリクトを解消した上での反映です。
  • 差分は +5 / -1 行と小さく、「ジェネレーター関連のテストヘルパーが、Bundler 4.0.0 環境でも正しく動くようにする」ための調整です。

元 PR (#56275) は、7.2 では当初「問題が再現しない」と判断されていたためバックポートされていませんでしたが、
Bundler 4.0.0 がリリースされて以降、7-2-stable の CI(Buildkite)でも同様のテスト失敗が再現するようになったため、
その修正を 7-2-stable にも反映した、という位置付けです。

コード上は、以下のような類の変更が入っていると考えられます(あくまで PR 説明から推測される例):

  • Bundler / Gemfile 周りの前提をゆるめる・条件分岐を追加する
  • テストのセットアップ時に Bundler のバージョン差異を吸収するようなヘルパーメソッドの追加・調整
  • ジェネレーター実行時のパスや環境変数の扱いを安定化する

いずれも、Rails ランタイムの挙動ではなく、「テスト環境のセットアップ方法」をいじる種類の変更です。


  1. 影響範囲・注意点
  • 影響範囲:

    • railties の「ジェネレーターのテスト」を実行する際の挙動に限定されます。
    • アプリケーション本体のランタイムコードや、実際のジェネレーターの生成結果の挙動への直接的な変更はありません。
    • 主な影響は Rails 自身の CI・開発者が Rails を checkout してテストスイートを走らせるケースです。
  • 注意点:

    • Bundler 4.0.0 環境で 7-2-stable ブランチのテストを回していた場合、今回の修正によりテストが通るようになるはずです。
    • バックポート時に #53647 とのコンフリクトを手作業で解消しているため、テスト結果の確認が重要になりますが、この PR 自体は CI を通過してマージされている前提です。
    • アプリケーション側で特別な対応が必要になるような破壊的変更は含まれていません。

  1. 参考情報 (あれば)

#56336 Split dbs_test.rb to whittle away at CI bottlenecks

マージ日: 2025/12/9 | 作成者: @jeremy

  1. 概要 (1-2文で)
    railties/test/application/rake/dbs_test.rb が CI の大きなボトルネックになっていたため、テストの見積り時間を現実に合わせて更新しつつ、テスト構成を整理・分割して並列化しやすくした PR です。重複していた ERB YAML テストを統合したうえで、DB 関連のテストを4つのファイルに分割し、CI のタイムアウトと全体時間の削減を狙っています。

  1. 変更内容の詳細

パフォーマンス問題と背景

  • 問題となっていたファイル: railties/test/application/rake/dbs_test.rb
  • CI の挙動:
    • テスト時間の見積もりが 82秒 と登録されていたが、実際は 20分以上 実行されるケースがある
    • テスト構造:
      • 49個の isolated test があり、それぞれが
        • プロセス fork
        • フルな Rails アプリ構築
      • 1ファイル内にまとまっているため、CI の「ファイル単位」の並列化が効かず、1ワーカーで全て処理していた

このため、CI がこの1ファイルに引きずられて全体が遅くなり、かつタイムアウトしやすい状態になっていました。


この PR で行っていること

1. テスト時間見積もりの修正

  • 変更ファイル: railties/Rakefile
  • 目的:
    • CI がテストスイートをワーカーに分散する際に使う「推定時間」が実態より大幅に小さく、負荷分散がうまくいっていなかった
  • 対応:
    • dbs_test.rb 系テストの推定時間を、実際の 20分超に見合うように「大きめ」に更新
    • 結果として、「このテストファイルは重い」と CI に認識させ、より均等にワーカーに配分されるようにする

(具体的な数値は PR 本文には書かれていませんが、「Bumps up inaccurate duration estimates」とある通り、明確に増やしています。)


2. ERB YAML テストの統合

  • 元々の問題:
    • YAML を ERB 経由で読み込むようなテストが 7ケース あり、すべてほぼ同じ内容のテストだった
    • しかし各テストが「isolated」扱いになっているため、7回とも別プロセス + フルアプリ構築 になっていた
  • 対応:
    • この7つの「同種テスト」を1つのテストケースに集約
    • 「1回の fork で7パターンを検証する」形に変更
  • 効果:
    • 不要な 6回分の fork / アプリ構築を削減
    • CI の実行時間・リソース消費を直接削減

3. テストファイルの分割(4ファイル構成へ)

もともと railties/test/application/rake/dbs_test.rb にすべて詰め込まれていたテストを、役割ごとに分割しています。

新しく作成されたファイル:

  1. railties/test/application/rake/dbs_postgresql_test.rb (+65行)

    • PostgreSQL 向けの rake タスク/DB 動作のテストを分離
    • 想定されるテスト内容(PR説明ベースの推測):
      • db:create, db:drop, db:structure:dump, db:structure:load の PostgreSQL 固有挙動
      • マルチ DB / schema search path などの Postgres 特有の処理確認
  2. railties/test/application/rake/dbs_schema_test.rb (+330行)

    • スキーマ関連の rake タスクを集中管理
    • 例:
      • db:schema:dump, db:schema:load, db:schema:cache:clear など
      • schema.rb / structure.sql の切り替え挙動
      • 複数データベース/複数スキーマ構成時の schema dump / load 動作
  3. railties/test/application/rake/dbs_setup_test.rb (+301行)

    • DB セットアップ全般(db:setup, db:reset, db:migrate など)に関するテストを分離
    • 例:
      • 初回セットアップ・リセット時の挙動
      • 既存DB・既存スキーマがある場合の挙動
      • seed 実行の確認 (db:seed, db:setup 内での seed 実行など)
  4. railties/test/application/rake/dbs_test.rb (+102/-745)

    • 元の肥大化したファイルから大幅に削減(745行削除、102行に縮小)
    • より「共通・ベースライン的な DB rake テスト」だけが残されている構成に近い

ポイント:

  • 1つの巨大ファイルに49個の isolated テストがあった状態から、
    • 複数のテストファイルに論理的に分割
    • CI は「ファイル単位」でテストジョブを分散できるため、4つのワーカーで並列実行可能 になる
  • +802 / -746 という統計からも、主に「リファクタ的な移動・分割」であり、テストカバレッジ自体は維持・整理されていると考えられます。

  1. 影響範囲・注意点
  • 影響範囲(主にテスト・CI 周り)

    • railties の rake DB テスト構成が大きく整理・分割されているため、
      • CI 設定で特定ファイルを直接指定していた場合(例: ruby -Itest railties/test/application/rake/dbs_test.rb のようなジョブ)は、新ファイル名に追随する必要があります。
      • ただし一般的な bundle exec rake testbin/test ベースの実行であれば、そのまま動作します。
    • テスト内容自体は基本的に移動・統合であり、仕様レベルの挙動変更は行っていない想定です。
  • テスト時間の見積もり変更

    • Rakefile での duration estimate の変更により、CI の「仕事配分」が変わります。
    • これにより、テスト全体のワーカーごとの時間バランスが変わるため、
      • 一部の CI 実行時間が短くなり
      • 別のワーカーに負荷が寄る場合がありますが、
      • トータルとしては「より均等」かつ「タイムアウトしにくい」構成を目指しています。
  • ローカル開発への影響

    • ローカルで railties の rake DB 周りのテストをいじる開発者は、
      • どのファイルにどの系統のテストを書くかを整理しやすくなっています
        • Postgres 固有 → dbs_postgresql_test.rb
        • スキーマ系 → dbs_schema_test.rb
        • セットアップ/リセット系 → dbs_setup_test.rb
        • その他・共通 → dbs_test.rb
    • テストファイルの責務が明確になったため、今後のテスト追加・メンテナンスもやりやすくなります。

  1. 参考情報 (あれば)
  • PR: https://github.com/rails/rails/pull/56336
  • 関連しうる観点:
    • Rails CI のテスト時間見積もりは過去実績に基づき Rakefile などにハードコードされており、それがずれると今回のようにテスト分散に悪影響が出る
    • Rails の DB 関連テストは「プロセス分離(isolated)」を多用するため、ファイルが肥大化すると fork コスト + アプリ構築コストが直撃する
    • 大きなテストファイルを「論理モジュール別」に分割しつつ、ERB YAML テストのような「同型テストの統合」で fork 回数を減らすのは、大規模テストスイートの典型的なチューニングパターンです。

#56175 Make flaky parallel tests easier to diagnose by deterministically assigning tests to workers

マージ日: 2025/12/9 | 作成者: @jeremy

  1. 概要 (1-2文で)
    Rails の並列テスト実行時に、テストをワーカーへ決定論的(再現可能)に割り当てる仕組みが入り、「同じ --seed とワーカー数なら同じワーカーが同じ順番でテストを実行できる」ようになりました。
    加えて、オプション work_stealing: true によってアイドルワーカーが他のワーカーからテストを奪う「ワークスティーリング」を有効化できるようになり、実行時間の平準化と再現性のトレードオフを選べます。

  1. 変更内容の詳細

全体像

これまでの Rails 並列テストは「どのワーカーがどのテストを実行するか」があまりコントロールされておらず、同じ --seed を指定してもテスト間依存によるフレーク(並列時のみ落ちるテスト)が再現しにくい問題がありました。

この PR では:

  • テスト -> ワーカーの割り当てを「ラウンドロビン + 決定論的」に変更
  • 任意で「ワークスティーリング」をオンにできるようにして、実行時間を平準化できるようにする
  • これらを支えるために新しい TestDistributorThreadPoolExecutor を導入
  • ドキュメントとテストを整備

という変更が入っています。


主要な技術的変更点

1. 決定論的なテスト割り当て (TestDistributor)

新ファイル:
activesupport/lib/active_support/testing/parallelization/test_distributor.rb

役割:

  • 並列実行する「テストキュー(テストファイル/テストケースの一覧)」を管理
  • ワーカーへのテスト配分を「ラウンドロビン + 決定論的」に行う
  • オプションで「ワークスティーリング」を提供

ポイント:

  • 「同じ seed」「同じワーカー数」であれば、テストが常に同じワーカー順・テスト順で実行される
  • これにより、テスト間依存が原因の失敗(例えば「Aが先に走るか後に走るかでBの結果が変わる」)を、再現しやすくなります

ラウンドロビンのイメージ:

text
Worker 1: test_a, test_d, test_g, ...
Worker 2: test_b, test_e, test_h, ...
Worker 3: test_c, test_f, test_i, ...

seed とワーカー数が同じであれば、この順番は常に同じになります。

2. ワークスティーリング機能 (work_stealing: true)

同じく TestDistributor でサポートされるオプションです。

  • 「ワークスティーリング」は、暇になったワーカーが、まだ仕事が残っているワーカーのキューからタスク(テスト)を奪って実行する仕組みです
  • 有効化すると、「あるワーカーに遅いテストが偏ったせいで全体時間が伸びる」のを軽減できます

この PR の重要なトレードオフ:

  • work_stealing: false(デフォルト想定)

    • 各ワーカーに割り当てられるテストが完全に決定論的
    • フレークテスト(特にテスト間依存由来)を再現しやすい
    • ただし、遅いテストが偏ると全体実行時間がばらつきやすい
  • work_stealing: true

    • 実行時間の平準化が期待できる
    • その代わり、「どのワーカーがそのテストを実行するか」が seed だけでは再現しなくなり、フレークテストの再現性はやや落ちる

3. 並列実行エグゼキュータの実装強化 (ThreadPoolExecutor)

新ファイル:
activesupport/lib/active_support/testing/parallelization/thread_pool_executor.rb

  • スレッドベースの並列実行のためのエグゼキュータが追加されています
  • TestDistributor と連携し、テストの取得・実行・完了報告を行う
  • テストコード(thread_pool_executor_test.rb)を見る限り、「キューの順序」「ワークスティーリングの挙動」がきちんとテストされています

4. サーバ・ワーカー周りの修正

対象ファイル:

  • activesupport/lib/active_support/testing/parallelization/server.rb
  • activesupport/lib/active_support/testing/parallelization/worker.rb
  • activesupport/lib/active_support/testing/parallelize_executor.rb

主な変更:

  • 旧来の「なんとなくキューから取る」方式から、TestDistributor を介して決定論的にタスクを配分する方式へ変更
  • サーバ側がテストをキューに積み、ワーカー側がキューから仕事を取る流れを整理
  • 「テスト失敗時に各ワーカーからレポートを並列に表示する」ような処理をしていたが、PR説明にある通り「concurrent test reports for each test failures」は削除されている(ログの混在や見づらさを避けるためと思われます)

5. ActiveSupport::TestCase からの設定 API 拡張

activesupport/lib/active_support/test_case.rb:

  • 並列実行に関する設定メソッドに work_stealing オプションが追加されている可能性が高いです
  • 例えば以下のような使い方になることが想定されます(実際の API 名はコードに依存しますが、概念として):
ruby
class ActiveSupport::TestCase
  parallelize(workers: :number_of_processors, work_stealing: true)
end

これにより「このテストスイートではワークスティーリングを有効にする/しない」を選択可能になります。

6. ドキュメント・CHANGELOG

  • guides/source/testing.md
    • 並列テストの節に「決定論的な割り当て」と「work_stealing オプション」の説明が追記
  • activesupport/CHANGELOG.md
    • ActiveSupport における新機能として並列テストの振る舞い変更が記載

7. テスト群の追加

  • activesupport/test/parallelization_test.rb
  • activesupport/test/parallelization/server_test.rb
  • activesupport/test/parallelization/test_distributor_test.rb
  • activesupport/test/parallelization/thread_pool_executor_test.rb

主な検証内容:

  • 同じ seed・ワーカー数で同じ配分になること(決定論的であること)
  • work_stealing の有無で挙動が変わること
  • スレッドプールエグゼキュータが正しくタスクを処理し、完了すること

  1. 影響範囲・注意点

影響範囲

  • Rails で ActiveSupport::TestCase.parallelize による並列テストを使っているプロジェクト全般
    • 特に CI 上で PARALLEL_WORKERS を使っているケースや、自前でワーカー数を設定しているプロジェクト
  • テスト間依存によるフレークテストがあるプロジェクトでは、失敗パターンの再現性が上がる(=バグを見つけやすくなる)

実行時間への影響

  • PR 説明上は、「ワークスティーリング有無・変更前後で統計的に有意な差はまだ見えていない(分散が大きい)」という評価
  • とはいえ、極端に遅いテストが偏っている場合、work_stealing: true の方が平均実行時間・ばらつきは改善する可能性がある

注意点・トレードオフ

  1. 再現性 vs 実行時間

    • フレークテストをデバッグするフェーズ:
      • work_stealing: false 推奨
      • --seed とワーカー数を固定して再実行すると、同じワーカー順・実行順で走らせやすい
    • 普段の CI での高速化・安定化:
      • work_stealing: true を検討(プロジェクトの特性と計測次第)
  2. seed とワーカー数の固定

    • 再現性を高めるには、CI などで --seed とワーカー数をログに残しておくとよいです
    • 過去ログから seed とワーカー数を再利用してローカルで再実行すれば、「どのワーカーがどのテストを実行して落ちたか」をかなりの精度で再現できます(work_stealing 無効時)
  3. ログ・レポート形式の変化

    • 「並列でのテスト失敗レポートの扱い」が変わっているため、CI ログの並び方が従来と若干変わる可能性があります
    • 「各ワーカーからの並列レポート」は削除されているため、以前のログフォーマット前提の解析ツールなどがある場合は要確認

  1. 参考情報 (あれば)

#56330 Enable debug events by default

マージ日: 2025/12/9 | 作成者: @gmcgibbon

  1. 概要 (1-2文で)
    Railsの新しいイベントレポーター(log subscriber 周り)で、デフォルトでは出なくなっていた「debug イベント」を、再びデフォルトで有効にする変更です。特にアプリケーション初期化前(テストやフレームワーク単体利用時)でも debug イベントが出力されるようになります。

  1. 変更内容の詳細

2-1. イベントレポーターのデフォルト動作変更

ActiveSupport::EventReporter の初期設定が変更され、デフォルトで debug モードが有効になります。

変更イメージ(擬似コード):

ruby
# 変更前 (イメージ)
class ActiveSupport::EventReporter
  def initialize(...)
    @debug = false
  end
end

# 変更後 (イメージ)
class ActiveSupport::EventReporter
  def initialize(...)
    @debug = true
  end
end

これにより、EventReporter が使われる場面で、今まで出てこなかった debug レベルのイベント(例えば内部的なトレース用イベント)が、明示的にオフにしない限り出力されるようになります。

2-2. アプリケーション未初期化状態での挙動の明確化

PR の説明で強調されているポイントは:

  • 「アプリケーションが初期化されていない状態」での利用
    • テストコードから直接 ActiveSupport の機能を呼ぶ
    • Rails 本体ではなく、ActiveSupport や ActiveRecord などを単体で利用する
  • そのような状況では、機密情報のマスキングなどを前提にした「本番ライクなロギングポリシー」を強制しない、という方針です。

具体的には:

  • Rails アプリ本体の設定がまだ読み込まれていない
  • けれどイベントやログを見たい(テストやデバッグのため)
  • その用途では、デフォルトで debug イベントを隠さずに出したほうが利便性が高い

という判断で、EventReporter 側のデフォルトが debug 有効になっています。

2-3. テストと CHANGELOG の更新

  • activesupport/test/event_reporter_test.rb
    • デフォルト設定で debug イベントが有効になっていることを確認するテストが追加。
  • activesupport/test/structured_event_subscriber_test.rb
    • 新しいデフォルト挙動に合わせたテスト修正。
  • activesupport/CHANGELOG.md
    • ActiveSupport における動作変更として、この「debug イベントをデフォルト有効にした」旨が追記。

  1. 影響範囲・注意点

3-1. ログ・イベント出力量の増加

  • これまで:
    • 新しい log subscriber / event reporter 導入以降、デフォルトでは debug イベントが出ない状態だった
  • これから:
    • 何も設定していない場合、debug イベントが出るようになる

そのため:

  • テスト環境やコンソール上で、イベント出力(ログやイベントサブスクライバ経由の出力)が増える可能性があります。
  • デバッグ目的ではメリットが大きい一方で、
    • 大量のイベントが発生する処理
    • もともとログ量を抑えていたシナリオ
      では、出力量が増えることに注意が必要です。

3-2. ActiveSupport 単体利用・ライブラリ作者への影響

ActiveSupport や Rails のイベント仕組みを使ってログやメトリクスを構築しているライブラリは:

  • デフォルトで debug イベントが流れる前提になるため、
    • 不要であれば EventReporter や subscriber 側で debug イベントをフィルタする
    • 本番環境向けライブラリであれば、利用者にログレベルのチューニングを案内する といった対応を検討したほうがよいかもしれません。

3-3. セキュリティ・情報漏えい観点

PR の意図として、

アプリケーションが初期化されていない状況 = テストや単体利用を想定
この場合、debug イベントを「秘匿すべき」とはみなさない

という割り切りがあります。

  • 本番環境で Rails アプリとして動いている場合は、
    • 通常は Rails のロガー設定や環境ごとの設定でログレベルを調整する想定
  • 一方で、もし初期化前の状態で本番相当の処理をしている特殊な構成があれば、
    • 想定より詳細な debug 情報が出力される可能性があるため、設計を見直した方が安全です。

  1. 参考情報 (あれば)
  • 該当 Issue: https://github.com/rails/rails/issues/56230
    • 「新しい log subscribers 導入後に debug イベントが出なくなった」問題を解消するもの。
  • 関連コンポーネント:
    • ActiveSupport::EventReporter
    • structured event subscriber(構造化イベントを受け取る仕組み)
  • 運用上のポイント:
    • テストやデバッグ用途ではそのままメリットが大きく、特別な変更は不要。
    • ログ量・情報量を厳密にコントロールしたい場合は、
      • Rails 側のログレベル設定
      • EventReporter / subscriber でのフィルタリング
        を使って debug イベントを抑制することを検討するとよいです。

#56334 Yield the transaction for Model#with_lock

マージ日: 2025/12/9 | 作成者: @ngan

  1. 概要 (1-2文で)
    ActiveRecord::Base#with_lock が、ブロック引数として「現在のトランザクションオブジェクト」を受け取れるように拡張されました。これにより、with_lock 内で after_commit などのトランザクションフックを、より扱いやすい形で利用できます。

  1. 変更内容の詳細(あればサンプルコードも含めて)

何が変わったか

従来の with_lock:

ruby
person.with_lock do
  # 悲観ロックされた状態での処理
end

この PR 以降は、ブロック引数としてトランザクションオブジェクトが渡されます:

ruby
person.with_lock do |transaction|
  transaction.after_commit { puts "hello" }
  # その他の処理
end

内部的には、ActiveRecord::Base#with_lock が開始するトランザクションを、そのままブロックに yield するように変更されています。
これは既に ActiveRecord::Base.transaction に導入されている挙動 (transaction do |tx| ... endtx.after_commit などが使える) と同様のパターンを、with_lock にも適用したものです。

コード上の変更ポイント(概略)

  • activerecord/lib/active_record/locking/pessimistic.rb
    • with_lock 内の transaction ブロックで、これまで yield のみだった箇所を「トランザクションオブジェクトを引数に渡して yield」するように変更。
  • activerecord/test/cases/locking_test.rb
    • with_lock にブロック引数を渡したときに after_commit などが正常に動作することを確認するテストが追加。
  • activerecord/CHANGELOG.md
    • 新機能として with_lock がトランザクションを yield することが明記。

※正確なメソッドシグネチャは PR 本体側の実装に依存しますが、意図としては以下のようなイメージです:

ruby
# 疑似コードイメージ
def with_lock(...)
  transaction(requires_new: true) do |tx|
    lock!
    yield tx
  end
end

  1. 影響範囲・注意点

既存コードへの影響

  • 後方互換性

    • 既存の with_lock の呼び出し方法(ブロック引数なし)はそのまま有効で、挙動も基本的に変わりません:
      ruby
      person.with_lock do
        # これまで通り動く
      end
    • ブロックの arity(引数の数)が 0 の場合でも Ruby のブロック仕様上問題なく動作します(余計な引数は無視される)。
  • 新機能としての利用

    • with_lock 内でトランザクションフックを直接呼びたい場面で、モデルのクラスメソッド経由などを使わずに、transaction 引数から直接 after_commit 等を呼べます。
      ruby
      person.with_lock do |tx|
        # DB 更新処理
        person.update!(status: "processed")
      
        # コールバックをこのトランザクションに紐づけたい
        tx.after_commit { NotificationJob.perform_later(person.id) }
      end

注意点

  • with_lock は内部でトランザクションを開始するため、既に外側でトランザクションを張っている場合、DB アダプタによっては「入れ子トランザクション(SAVEPOINT)」になる可能性があります。そのトランザクションオブジェクトをどのように扱うかは、ActiveRecord::Transaction の既存仕様に従います。
  • 複数の with_lock をネストするようなケースや、transactionwith_lock を組み合わせるケースでは、どのレベルのトランザクションに対して after_commit が紐づくかの理解が必要になります(これは本 PR 特有というより、transaction を yield するパターン全般の注意点です)。

  1. 参考情報 (あれば)
  • 類似機能の PR: AR.transaction でトランザクションを yield する
  • 対象メソッド: ActiveRecord::Base#with_lock(悲観ロック)
  • CHANGELOG 追記: activerecord/CHANGELOG.md に「with_lock がトランザクションをブロックに渡す」旨が追加されています。

#56332 Fix typo in comments: exisiting → existing

マージ日: 2025/12/9 | 作成者: @yujiteshima

  1. 概要 (1-2文で)
    Rails の Active Record に含まれるコメント内のスペルミス「exisiting」を正しい「existing」に修正した PR です。コードロジックの変更は一切なく、挙動への影響はありません。

  2. 変更内容の詳細(あればサンプルコードも含めて)

  • 対象ファイル:

    • activerecord/lib/active_record/encryption/encryptor.rb
    • activerecord/test/models/dats.rb
  • 修正内容: どちらのファイルも「exisiting」というスペルミスを「existing」に修正しています。
    変更はコメント行のみで、Ruby コード自体には変更がありません。

    想定されるイメージ(※実際のコメント文脈に沿った概略):

    ruby
    # Before
    # This feature allows searches on exisiting data...
    
    # After
    # This feature allows searches on existing data...
    ruby
    # Before
    # This model is used to persist data in exisiting tables...
    
    # After
    # This model is used to persist data in existing tables...
  • 背景:

    • codespell というツールでコードベースのスペルチェックを行った際に、2 つのコメント行で誤字が見つかり、それを修正したという位置付けです。
  1. 影響範囲・注意点
  • コードコメントのみの修正であり、実行パス・API・DB スキーマ・挙動には一切影響しません。
  • テスト (activerecord/test/models/dats.rb) のコメントも対象ですが、テストの内容や結果に影響はありません。
  • バージョンアップやパッチ適用時に、この変更に起因するマイグレーション・リリースノート上の特別な対応は不要です。
  • ドキュメント生成やソースコードブラウジング時(IDE でのコメント表示など)において、より読みやすく正確な説明になるという程度の改善です。
  1. 参考情報 (あれば)
  • PR: https://github.com/rails/rails/pull/56332
  • 誤字検出ツール: codespell
    • CI やローカル開発環境に導入することで、同様のスペルミスを自動で検出できます。
  • 対象領域の補足:
    • activerecord/lib/active_record/encryption/encryptor.rb は Active Record の暗号化関連の処理(暗号化・復号・検索など)を扱うクラス群で、今回の修正はその動作説明コメント部分です。
    • activerecord/test/models/dats.rb は Active Record のテスト用モデル定義ファイルであり、既存テーブルを利用したテストシナリオの説明コメントが修正対象になっています。

#56318 Silence ActiveStorage deprecation warnings in tests

マージ日: 2025/12/9 | 作成者: @p8

  1. 概要 (1-2文で)
    Active Storage のテスト実行時に、非推奨オプション:preprocessedに関する deprecation warning が大量に出る問題を、テスト側でサイレンスするPRです。Rails 9.0 での削除予定APIに関する警告を抑制し、テスト出力をノイズレスにしています。

  1. 変更内容の詳細

※差分は activestorage/test/test_helper.rb のみ(+24/-18)。
主なポイントは以下です。

  • Active Storage のテストで発生する、以下のような deprecation warning を抑制する設定をテストヘルパーに追加。

    text
    DEPRECATION WARNING: The :preprocessed option is deprecated and will be removed in Rails 9.0.
    Use the :process option instead. Replace `preprocessed: true` with `process: :later` and
    `preprocessed: false` with `process: :lazily`.
    (called from new at activestorage/lib/active_storage/reflection.rb:7)
  • 具体的には、テスト時に ActiveSupport::Deprecation の設定(または Active Storage が使っている deprecator)を調整し、
    :preprocessed に関する警告をフィルタ/サイレンスする形になっていると考えられます。

    典型的には、以下のようなコードが test_helper.rb に入るパターンです(イメージ):

    ruby
    ActiveSupport::Deprecation.behavior = :stderr
    
    ActiveSupport::Deprecation.silenced = true
    # または
    ActiveSupport::Deprecation.disallowed_warnings += [
      /The :preprocessed option is deprecated/,
    ]

    実際のコードはPR側の実装に依存しますが、

    • Rails 本体のテストでのみ有効
    • 本体機能(本番利用の deprecation 動作)は変えない
      という方向で、ActiveStorage のテスト補助コードにのみ変更が入っています。
  • あくまで「テスト出力の静音化」が目的であり、:preprocessedprocess: :later / :lazily への置き換え自体を行うものではありません(Active Storage の内部実装置き換えではない)。


  1. 影響範囲・注意点
  • 影響範囲

    • Rails リポジトリ内の Active Storage テストスイートのみ。
    • Rails を利用するアプリケーションコードや、アプリ側テストの挙動には影響しません。
    • CIのログなどで、該当 deprecation が出なくなることでログノイズが減ります。
  • 注意点

    • このPRは「テストで警告を隠す」だけであり、:preprocessed 自体が将来削除される事実は変わりません。
    • アプリケーション側で preprocessed: true/false を使っている場合は、引き続き自分のアプリで deprecation warning を確認し、
      推奨どおり process: :later / process: :lazily への移行が必要です。
    • コアのテストが警告をサイレンスしているため、「Rails 本体のテストが deprecation を検知してくれる」ことは期待できなくなります。
      ただし、これは「移行ガイドで正式に案内済みの deprecation であり、Rails 本体テストではノイズとして扱う」という判断によるものと考えられます。

  1. 参考情報 (あれば)
  • 該当 deprecation のメッセージ:
    • preprocessed: trueprocess: :later
    • preprocessed: falseprocess: :lazily
  • 将来のバージョン:
    • Rails 9.0 で :preprocessed オプションは削除予定。
  • 実アプリでの修正例:
    ruby
    # 旧:
    has_one_attached :avatar, preprocessed: true
    
    # 新:
    has_one_attached :avatar, process: :later

#56019 Add structured event for open redirects

マージ日: 2025/12/9 | 作成者: @adrianna-chang-shopify

  1. 概要 (1-2文で)
    config.action_controller.action_on_open_redirect = :notify を有効にしているアプリで、オープンリダイレクト検知時に自動で「構造化ログイベント」が発行されるようにした PR です。ActiveSupport::Notifications の購読を自前で書かなくても、標準の Structured Event Subscriber 経由でオープンリダイレクトの情報を取得できます。

  1. 変更内容の詳細

背景

  • 先行PR (#55496) で「オープンリダイレクト検知」と :notify の動作が導入済み。
  • これまでは :notify を使っても、「通知は飛ぶが、構造化ログとして扱うには独自 subscriber 実装が必要」という状態だった。
  • この PR で、Rails 標準の ActionController::StructuredEventSubscriberopen_redirect 通知を処理するようになり、「設定するだけで構造化ログ」が出るようになる。

具体的な変更点

  1. ActionController::StructuredEventSubscriberopen_redirect 用メソッドを追加

    • actionpack/lib/action_controller/structured_event_subscriber.rb に、open_redirect イベントを処理するメソッドが追加。
    • イベント名は ActiveSupport::Notifications の open_redirect.action_controller に対応している想定です。
    • メソッドの中で、構造化ログとして必要な情報を組み立てます。PR 説明から分かるポイント:
      • stack_trace には「スタックトレースの最後の 1 行」を使用
        • これは既存の #rescue_from_callback の出力形式と揃えるため
        • (将来的には「最後の数行」や「フィルタ済みトレース」など、よりリッチな情報を提供する拡張余地あり)

    擬似コードイメージ(実際のコードイメージ用・正確なキー名はリポジトリを参照してください):

    ruby
    module ActionController
      class StructuredEventSubscriber
        def open_redirect(event)
          payload = event.payload
          log :info, "open_redirect", {
            url: payload[:url],
            # その他、オープンリダイレクト検知に関する情報
            stack_trace: extract_last_line_of_backtrace(payload[:exception_object])
          }
        end
      end
    end

    これにより、:notify 発生時に標準の structured logging 系統(例: ActionController::LogSubscriber に似た仕組み)からオープンリダイレクトが一貫した形式で出力されます。

  2. Redirecting モジュール側の微調整

    • actionpack/lib/action_controller/metal/redirecting.rb は +1/-1 の微修正のみ。
    • 内容的には、open_redirect 通知の発火ロジック(または payload)に対する細かな調整で、StructuredEventSubscriber が扱いやすい形に合わせた可能性が高いです(メソッド名・イベント名・payload のキー揃え等)。
  3. CHANGELOG の更新

    • actionpack/CHANGELOG.md に、この新機能(オープンリダイレクト向け structured event サポート)が追加されたことが追記されています。
    • actionpack の利用者向けに、機能の追加が公式にアナウンスされた形です。
  4. テスト追加

    • actionpack/test/controller/structured_event_subscriber_test.rb に 20 行程度のテストを追加。
    • 主な観点:
      • open_redirect 通知が structured event として処理されること
      • stack_trace 等、構造化された payload が期待通りであること
    • これにより、今後のリファクタリングや拡張時にもこの振る舞いが壊れないよう担保されています。

  1. 影響範囲・注意点

影響範囲

  • 対象:
    • ActionController のオープンリダイレクト保護を有効化し、かつ
      config.action_controller.action_on_open_redirect = :notify
      を指定しているアプリケーション。
  • 影響内容:
    • 上記設定のアプリでは、オープンリダイレクト検知時に自動的に構造化ログイベントが出力される。
    • 既に独自で open_redirect.action_controller を購読しログを出していた場合、
      • Rails 標準の structured event と併せて 2 重にログが出る可能性があります。

注意点・移行上のポイント

  • すでに独自の subscriber で構造化ログを実装している場合:

    • 標準 StructuredEventSubscriber に合わせて統一することで、
      • ログ形式の一貫性
      • メンテナンスコストの削減 が期待できます。
    • 不要であれば、自前の subscriber を削る・または標準出力側をフィルタすることを検討してください。
  • stack_trace が「最後の 1 行のみ」である点:

    • デフォルトでは詳細なスタックトレースが欲しい場合には情報が足りない可能性があります。
    • よりリッチなスタック情報が必要なら:
      • 独自 subscriber を追加で実装し、payload から exception を参照して自前でトレースを組み立てる
      • あるいは将来的な Rails 側の拡張(「最後の X 行」など)が入るのを待つ を検討してください。
  • ログ出力基盤との統合:

    • structured event のキー設計は他の ActionController::StructuredEventSubscriber が出すイベントと整合しているはずなので、
      • 既に JSON ログや Observability 基盤(Datadog, New Relic, OpenTelemetry 等)に統合している場合、
      • 同じストリーム上に「open_redirect」イベントが自然に流れ込むようになります。
    • 監視・アラート設定で「open_redirect」のイベント名やタグをトリガーとして使うのが簡単になります。

  1. 参考情報 (あれば)
  • 先行PR:

  • 対応するクラス / モジュール:

    • ActionController::Redirecting
    • ActionController::StructuredEventSubscriber
  • Rails の Structured Logging / Structured Event 周り:

    • Rails 7.1+ 以降で強化されている structured logging 基盤の一部で、
      ActionController::StructuredEventSubscriber はコントローラ関連イベントを構造化してログに流すための標準 subscriber です。
      本 PR により、その対象イベントに「open_redirect」も加わった形になります。

#56296 Remove unnecessary defined? check in Active Job instrumentation

マージ日: 2025/12/8 | 作成者: @shivamsinghchahar

  1. 概要 (1-2文で)
    Active Job のインストルメンテーション処理内で、インスタンス変数へのアクセス前に行っていた defined? チェックを削除し、よりシンプルな条件分岐に変更した PR です。Ruby 2.7 サポート終了により、未定義変数アクセスに関する警告回避コードが不要になったことが背景です。

  1. 変更内容の詳細

対象ファイル: activejob/lib/active_job/instrumentation.rb
変更は 1 行のロジック差し替えのみです。

変更前

ruby
ActiveSupport::Notifications.instrument("#{operation}.active_job", payload) do |payload|
  value = block.call(payload) if block
  payload[:aborted] = @_halted_callback_hook_called if defined?(@_halted_callback_hook_called)
  @_halted_callback_hook_called = nil
  value
end
  • @_halted_callback_hook_called が「定義されているか」を defined? でチェックしてから payload[:aborted] に代入していました。
  • Ruby 2.7 以前では、未定義のインスタンス変数アクセスで警告が出ることへの配慮としてこうしたパターンがよく使われていました。

変更後

ruby
ActiveSupport::Notifications.instrument("#{operation}.active_job", payload) do |payload|
  value = block.call(payload) if block
  payload[:aborted] = true if @_halted_callback_hook_called
  @_halted_callback_hook_called = nil
  value
end

ポイント:

  • defined?(@_halted_callback_hook_called) の代わりに、単純に if @_halted_callback_hook_called で真偽判定するように変更。
  • 代入値も payload[:aborted] = true に固定化されました。
    • 以前は payload[:aborted] = @_halted_callback_hook_called だったため、フラグの実際の値(真偽値)をそのまま渡していましたが、ここでは「中断が起きたなら true」というブールフラグとして扱う想定に整理されています。
  • @_halted_callback_hook_called 自体はこの後で必ず nil にリセットされる点は変更なしです。

Ruby 3 系では、未初期化インスタンス変数にアクセスしても警告が出ない(もしくは問題にならない前提)ため、defined? による保護が不要になった、という判断です。
(クラスのどこか別の場所で @_halted_callback_hook_called は適切に設定される設計になっていることが前提になっています。)


  1. 影響範囲・注意点
  • 対象: Active Job のインストルメンテーション("#{operation}.active_job" の通知を送る際の payload 生成)部分のみ。
  • 主な影響:
    • payload[:aborted] の値が「@_halted_callback_hook_called の実際の値」から「条件成立時は常に true」に変わりました。
      • ロジックとしては「中断したかどうか」のフラグであれば、true/nil の二値で十分なので、意味的な後方互換性は高いです。
      • もし外部コードが payload[:aborted]true/false 以外の値(例えばシンボルや詳細な状態)を期待していた場合は注意が必要ですが、そのような利用は想定されていないはずです。
    • defined? を使わなくなったことで、Ruby のバージョン依存の警告回避コードが減り、読みやすさ・保守性が向上しています。
  • パフォーマンス影響:
    • 実質的に誤差レベルですが、defined? の呼び出しがなくなるため、極めてわずかに軽くなります。
  • テスト:
    • PR 上では新規テスト追加は行われていませんが、挙動としてはごく限定的な変更であり、既存テストでカバーされている前提です。

  1. 参考情報 (あれば)
  • 類似の変更:
    • PR 説明にもある通り、https://github.com/rails/rails/pull/50597 で同様の「Ruby 2.7 サポート終了に伴う defined? 除去」的なリファクタリングが行われています。
  • 背景:
    • Rails 7.2 以降で Ruby 3.0+ が前提となり、古い Ruby 向けの互換コードや警告回避コードが徐々に削除されている流れの一環です。

#56311 Deprecate PreviewImageJob

マージ日: 2025/12/8 | 作成者: @p8

  1. 概要 (1-2文で)
    Active Storage の ActiveStorage::PreviewImageJob を非推奨にしつつ、内部実装を現状の API に追従させる変更です。Rails 本体からは既に呼ばれていないものの、ドキュメント済みでパブリック API であるため、削除ではなく deprecate という形で扱っています。

  1. 変更内容の詳細

2-1. PreviewImageJob を非推奨化

activestorage/app/jobs/active_storage/preview_image_job.rb にて、PreviewImageJob クラスに非推奨のアナウンスが追加されています(実際には ActiveSupport::Deprecation を用いたメッセージか、コメント・ドキュメントとしての明示になります)。

背景:

  • Rails 本体(Active Storage の内部コード)は、もはや PreviewImageJob を呼び出していない。
  • 一方でこのクラスはドキュメントに載っており、パブリック API 扱いになっている。
  • いきなり削除すると、継承しているアプリケーションコードやテストが壊れる可能性がある。

そのため:

  • クラス自体は残しつつ、「非推奨である」と宣言し、将来的な削除を予告する方向に変更。

2-2. 廃止された preprocessed メソッドを使わないように修正

説明文より:

The preprocessed method no longer exist so inline the original implementation instead.

以前は PreviewImageJob 内部で preprocessed というメソッド(もしくはヘルパー)を利用していたが、そのメソッド自体が #51951 の変更などで削除されているため、今回の PR で以下のように対応しています。

  • preprocessed に委譲していた処理を、perform などのメソッド内に直接書く(インライン化する)。
  • これにより、PreviewImageJob を古いコードのまま放置しておくと発生していた NoMethodError などを防ぐ。

概要イメージ(あくまで構造イメージの擬似コード):

ruby
# 変更前 (イメージ)
class ActiveStorage::PreviewImageJob < ActiveStorage::BaseJob
  def perform(blob)
    preprocessed(blob) do |variant|
      # ...
    end
  end
end
ruby
# 変更後 (イメージ)
class ActiveStorage::PreviewImageJob < ActiveStorage::BaseJob
  def perform(blob)
    # もともと preprocessed がやっていた処理をここに展開
    # 例: blob を開いて preview を生成し、必要なら変換や保存を行う処理
    preview = blob.preview_image # など、現行 API に則った呼び出し
    preview.processed            # 実際の生成・処理
  end
end

※正確なメソッド名や処理は実装に依存しますが、「preprocessed 呼び出しをやめて、同等のロジックをクラス内に直に書いた」という修正が主旨です。

2-3. テスト追加

activestorage/test/jobs/preview_image_job_test.rb にテストが追加されています (+27 行)。

テストの目的:

  • PreviewImageJob が現行の実装でエラーなく動くことを保証。
  • 非推奨となるとはいえ、公開 API として既存利用がある可能性を考慮し、最低限の振る舞いをテストで担保。

  1. 影響範囲・注意点
  • 既存のアプリケーションで ActiveStorage::PreviewImageJob を使っている場合

    • クラスはまだ存在するため直ちに壊れません。
    • ただし非推奨になったため、今後の Rails メジャーバージョンで削除される可能性が高いです。
    • 直接呼んでいる場合は、代替手段(自前の Job で blob/attachment の preview 生成を行うなど)を検討してください。
    • 継承して独自 Job を作っている場合も、ベースクラスを自前のクラスに差し替えるなどの移行を考えた方がよいです。
  • preprocessed に依存した独自実装がある場合

    • この PR の対象は Rails 本体内の利用ですが、もしアプリ側で preprocessed を呼び出しているコードが残っている場合は、すでに別の PR (#51951) の時点で壊れている可能性があります。
    • 同様の方針で、preprocessed がしていた処理を直接自前で記述する形に移行する必要があります。
  • テストやドキュメント

    • ドキュメントで紹介されていたクラスに非推奨ラベルが付くことで、将来的な削除に向けたシグナルが明確になります。
    • ライブラリや gem で PreviewImageJob を利用・継承している場合は、非推奨である旨を README 等に記載しつつ、代替設計へ移行することが望まれます。

  1. 参考情報 (あれば)
  • この変更は、以前の変更 (#51951) で preprocessed メソッドが削除されたことに起因する追従対応です。
  • Active Storage の preview 周りの仕組みや代替実装を検討する際は、最新の Active Storage ガイドおよび ActiveStorage::Previewer 系のクラス実装を参照するとよいです。

#56321 Update GitHub Actions/Checkout to v6 in CI templates

マージ日: 2025/12/8 | 作成者: @tomczak-mateusz

  1. 概要 (1–2文で)
    GitHub Actions 用の CI テンプレートで利用している actions/checkout のバージョンを v5 から v6 に更新した PRです。これにより、新しく生成した Rails アプリで GitHub に push した直後に Dependabot から「v6 に上げてください」という PR が飛んでくる状況を解消します。

  1. 変更内容の詳細

何を変えたか

主な変更は2種類です。

  1. Rails 自身のリポジトリで使っている CI ワークフロー

    • 以下のワークフロー内の actions/checkout@v5 をすべて @v6 に更新:
      • .github/workflows/devcontainer-shellcheck.yml
      • .github/workflows/devcontainer-smoke-test.yml
      • .github/workflows/rail_inspector.yml
      • .github/workflows/rails-new-docker.yml
      • .github/workflows/rails_releaser_tests.yml
      • .github/workflows/release.yml
  2. 新規アプリ/プラグイン作成時に生成される GitHub Actions CI テンプレート

    • Rails アプリ生成 (rails new my_app --github ci) 時に出力される CI 用テンプレート:
      • railties/lib/rails/generators/rails/app/templates/github/ci.yml.tt
    • Rails プラグイン生成 (rails plugin new my_plugin --github ci) 時に出力される CI 用テンプレート:
      • railties/lib/rails/generators/rails/plugin/templates/github/ci.yml.tt
    • 上記テンプレート内の actions/checkout のバージョンを v5v6 に更新。

サンプル (生成される CI のイメージ)

変更前(例):

yaml
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: ruby/setup-ruby@vX
        with:
          ruby-version: 3.3
      # ...以下省略

変更後(例):

yaml
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: ruby/setup-ruby@vX
        with:
          ruby-version: 3.3
      # ...以下省略

中身のロジックやステップ構成には変更はなく、uses: actions/checkout@... のバージョン指定だけが更新されています。

背景となる問題

  • Rails 8.1.1 で --github ci オプション付きでアプリを生成すると、生成される CI の YAML には actions/checkout@v5 が書かれていた。
  • それを GitHub 上に push すると、Dependabot が「最新は v6 なので上げましょう」という PR を即座に作成してしまう。
  • 新規ユーザーにとっては「Rails が作ったばかりの CI 設定に、いきなり更新 PR が来る」状態で混乱しやすい。
  • テンプレートを最初から v6 にしておくことで、この余計な PR を防ぐのが目的。

  1. 影響範囲・注意点
  • 新規で生成する Rails アプリ・プラグイン

    • この PR が含まれた Rails バージョン以降で rails new / rails plugin new をし、--github ci オプションを付けた場合、CI テンプレートは最初から actions/checkout@v6 を使うようになります。
    • そのため、GitHub 上で Dependabot が即座に「checkout のバージョンを上げる PR」を出してくることはなくなります。
  • 既存の Rails アプリへの影響

    • すでに生成済みのアプリの CI 設定は自動では変わりません。
    • 既存プロジェクトで actions/checkout@v5 を使っている場合は、手動で @v6 に上げるか、Dependabot の PR をマージすれば同等の状態になります。
  • 互換性面のリスク

    • actions/checkout のメジャーバージョンアップでは挙動やデフォルト設定が変わる場合がありますが、この PR 自体は Rails テンプレート上のバージョン番号を追従させただけで、Rails のコードやテスト内容には影響しません。
    • 既存の GitHub Actions のベストプラクティスに合わせた更新なので、一般的には問題なく受け入れられる変更です。

  1. 参考情報 (あれば)
  • 対応している Issue: Fixes #56320
  • 変更対象となった GitHub Actions の公式アクション:

#56324 Require uri instead of uri/generic

マージ日: 2025/12/8 | 作成者: @jeremyevans

  1. 概要 (1-2文で)
    Rails内で uri/generic を直接 require していた箇所を、標準的な require "uri" に変更することで、URIautoload している環境でも require "rails" が失敗しないようにする修正です。Ruby 標準ライブラリの uri の使い方を正しくし、他ライブラリとの相互運用性を改善するバグフィックスです。

  1. 変更内容の詳細

何が問題だったか

Ruby の uri ライブラリは、本来は次のようにトップレベルで読み込むことが前提になっています。

ruby
require "uri"

しかし Active Support の一部では、内部実装ファイルを直接指定していました。

ruby
# 以前
require "uri/generic"

通常はこれでも動くことが多いのですが、以下のように URIautoload している環境では問題が発生します。

ruby
autoload :URI, "uri"
require "rails"

この状況で require "uri/generic" が走ると、uri ライブラリ内部の読み込み順と定数定義の前提(URI::Generic が定義済みであることなど)が崩れ、uninitialized constant URI::Generic という NameError が発生します。PR のログにある通り、uri/file.rbGeneric を参照した時点で未定義になってしまうためです。

具体的な変更箇所

変更ファイルは 2 つで、どちらも require 行の置き換えのみです。

  • activesupport/lib/active_support/core_ext/object/json.rb
  • activesupport/lib/active_support/message_pack/extensions.rb

それぞれで、

ruby
require "uri/generic"

となっていた箇所が、

ruby
require "uri"

に変更されています。

これにより、Ruby 標準ライブラリが想定している正しい入口 (uri) から読み込まれるようになり、URI::Generic を含む関連クラス・モジュールが正しく初期化されます。

なぜ autoload :URI, "uri" と衝突するのか

autoload :URI, "uri" は、URI 定数が最初に参照されたタイミングで uri を読み込む仕組みです。
ところが、Rails 側から uri/generic を直接 require すると、

  • uri 全体の初期化処理が完了していない状態で
  • 内部モジュールだけを先に読み込もうとする

ことになり、URI::Generic 等の依存関係が未定義のまま評価されてしまいます。

require "uri" に統一することで、この依存関係と初期化順が Ruby 標準の期待通りに保たれ、autoload とも整合するようになります。


  1. 影響範囲・注意点

影響範囲

  • 直接的な影響箇所

    • Active Support の
      • Object#to_json など JSON 拡張 (core_ext/object/json.rb)
      • MessagePack 拡張 (message_pack/extensions.rb) で使用している URI 関連機能の読み込み方法。
  • 間接的に恩恵を受けるケース

    • 他ライブラリが autoload :URI, "uri" を行っている場合
      • 例: asciidoctor
    • それらと組み合わせて Rails / Active Support を使う場合
      • 例: tilt のテストで asciidoctor + haml 6+(haml 6+ が Rails を読み込む)という構成

この PR により、そういった環境で require "rails" したときの NameError: uninitialized constant URI::Generic が解消されます。

後方互換性・リスク

  • 動作面:

    • uri/generic を直接読む代わりに uri を読むだけなので、通常の Ruby アプリケーションでは挙動は変わりません。
    • 既存のテストでカバーされる範囲の動作は維持される想定です(PR でも「既存テストが通るはず」と明言)。
  • パフォーマンス:

    • require "uri" によって読み込まれる範囲が若干広くなる可能性はありますが、uri/generic を使うケースでは多くの場合 uri 全体が必要になるため、実質的なオーバーヘッドはほぼありません。
  • 注意点:

    • URI 定数の上書きや、uri ライブラリのモンキーパッチを行っている特殊な環境では、require "uri" タイミングが変わることで副作用が顕在化する可能性はわずかにありますが、かなりレアケースです。
    • Rails/AS 自体は URI の実装詳細には依存しておらず、「正しい入口から require しているだけ」なので、一般的には安全な変更と見なせます。

  1. 参考情報 (あれば)
sh
ruby -e 'autoload :URI, "uri"; require "rails"'
# -> uninitialized constant URI::Generic (NameError) が発生していた

この PR 適用後は、上記ケースでもエラーなく require "rails" が完了することが期待されます。


#56310 [ci skip] Use email_address in Action Mailer Basics guide for consistency with authentication generator

マージ日: 2025/12/7 | 作成者: @Saidbek

  1. 概要 (1-2文で)
    Action Mailer Basicsガイド内のサンプルコードで使われていた email カラム名を、Rails標準の認証ジェネレータで使われている email_address に統一するドキュメント修正です。コード本体の挙動変更や機能追加はなく、ガイドの記述のみが変わっています。

  1. 変更内容の詳細
  • 対象ファイル

    • guides/source/action_mailer_basics.md のみ変更(+11/-11)
  • 主な変更点

    • ガイド内でユーザのメールアドレスを表す属性名として使われていた email を、すべて email_address に置き換え。
    • 例:
      • 変更前(イメージ):
        ruby
        class User < ApplicationRecord
          # t.string :email
        end
        
        class UserMailer < ApplicationMailer
          def welcome_email(user)
            @user = user
            mail(to: @user.email, subject: "Welcome")
          end
        end
      • 変更後(イメージ):
        ruby
        class User < ApplicationRecord
          # t.string :email_address
        end
        
        class UserMailer < ApplicationMailer
          def welcome_email(user)
            @user = user
            mail(to: @user.email_address, subject: "Welcome")
          end
        end
  • 背景・目的

    • Rails 7.2 以降に入った組み込みの認証ジェネレータ bin/rails generate authentication は、ユーザモデルのメールアドレスカラムを email ではなく email_address として作成します。
    • 一方で Action Mailer Basics ガイドでは従来どおり email を使っており、公式ドキュメント間で命名が不一致でした。
    • この PR により、
      • 認証ジェネレータ
      • Action Mailer の入門ドキュメント
        が同じカラム名を前提に説明されるようになり、チュートリアルをそのまま写経しても整合性が取れるようになります。

  1. 影響範囲・注意点
  • 影響範囲

    • 実コード・ライブラリの挙動には影響なし(ドキュメントのみの変更)。
    • 新しく Action Mailer Basics を読みながら開発する人が、認証ジェネレータと同じ email_address を自然に使うようになります。
  • 注意点(既存プロジェクトとの関係)

    • 既存アプリで email カラムを使っている場合、このPRによってアプリが壊れることはありませんが、ガイドのコード例とは名前が異なる状態になります。
    • 既に bin/rails generate authentication を使って email_address カラムを持つ User モデルを作っている場合は、この PR 後のガイドのほうが素直にコピペで動きます。
    • どちらのカラム名を使うかはアプリの設計次第ですが、Rails公式の最新フローに合わせるなら新規では email_address に寄せるのが無難です。

  1. 参考情報 (あれば)

#56303 Suppress unnecessary output in railties tests

マージ日: 2025/12/7 | 作成者: @yahonda

  1. 概要 (1-2文で)
    Rails の railties テスト実行時に、HTTP リクエストによる例外ログや RuboCop の警告メッセージなど、テスト結果と直接関係しない冗長な出力を quietly で抑制する変更です。これにより、テスト出力がシンプルになり、失敗箇所の把握がしやすくなります。

  1. 変更内容の詳細

全体方針

  • 対象は railties 配下の一部テスト。
  • 「テストの目的は満たすが、標準出力には不要な情報」が出ていた箇所を quietly ブロックでラップ。
  • テストの成否・挙動は変えずに、コンソール出力だけを静かにするためのリファクタリング。

2-1. HTTP リクエスト関連テストの出力抑制

対象ファイル(例):

  • railties/test/application/mailer_previews_test.rb
  • railties/test/application/middleware/exceptions_test.rb
  • railties/test/application/routing_test.rb
  • railties/test/application/sprockets_assets_test.rb

これらのテストでは、意図的に 404 / 500 などのエラーを発生させて挙動を確認するため、以下のような ActionController::RoutingError などのスタックトレースやログがテスト中に出力されていました。

変更前のイメージ:

ruby
# テスト実行中に
[UUID] ActionController::RoutingError (No route matches [GET] "/rails/mailers"):
[UUID]

変更後は、同じテストを実行しても、このようなログは出力されず、テスト名と "." のみが表示されます。

実際の適用イメージ(簡略化):

ruby
# 変更前(擬似コード)
get "/rails/mailers"
assert_response :not_found

# 変更後(擬似コード)
quietly do
  get "/rails/mailers"
end
assert_response :not_found

quietly は内部で $stdout$stderr を一時的に差し替えて出力を捨てるヘルパー(Rails テストでよく使われるパターン)で、HTTP リクエストの挙動自体には影響しません。

変更例:

  • mailer_previews_test.rb
    • /rails/mailers へのアクセスが本番環境では不可であることを検証するテストから、ログ出力を抑制。
  • middleware/exceptions_test.rb
    • 例外ハンドリング・エラーページ表示に関するテストのうち、意図的に例外を発生させるパスを quietly でラップ。
  • routing_test.rb
    • ルーティングエラーなどを発生させるテストのリクエスト処理部分を quietly で囲んで、Rails のログを表示しないように。
  • sprockets_assets_test.rb
    • アセット関連のリクエストで出る不要なログを同様に抑制。

2-2. RuboCop 実行テストの出力抑制

対象ファイル:

  • railties/test/generators/shared_generator_tests.rb

plugin_generator_test などから利用される共通テストで、ジェネレータが生成したコードに RuboCop の違反がないかを検査しています。

変更前は、RuboCop 実行時に「新しい Minitest 系 Cop が設定されていない」旨のメッセージがすべてテスト出力に流れていました:

text
The following cops were added to RuboCop, but are not configured. Please set Enabled to either `true` or `false` in your `.rubocop.yml` file.
...
Minitest/AssertInDelta: # new in 0.10
  Enabled: true
...
For more information: https://docs.rubocop.org/rubocop/versioning.html
.

これらは「ライブラリ側の RuboCop 設定に関する情報」であり、テストの成功・失敗とは無関係なノイズです。

変更後イメージ(擬似コード):

ruby
# 変更前
run_rubocop(generated_app_path)

# 変更後
quietly do
  run_rubocop(generated_app_path)
end

この結果、テスト出力には . のみが表示され、RuboCop の冗長なバージョン/設定関連のメッセージは表示されなくなります。


  1. 影響範囲・注意点
  • 影響範囲

    • 影響するのは railties の一部テストの「コンソール出力のみ」で、アプリ本体や本番挙動には一切影響しません。
    • テストのアサーション内容やカバレッジも変わらない想定です(HTTP レスポンスの検証などはそのまま)。
  • 利点

    • テストログが大幅に読みやすくなり、テスト失敗時の本質的なメッセージが見つけやすくなります。
    • CI ログが短くなり、ログ閲覧コストや保存容量の削減にも寄与します。
  • 注意点

    • quietly によって、トラブルシュート時に参考になる可能性があるログも一緒に抑制されるケースがあります。
      • ただし、ここで抑制しているのは主に「意図的に起こしているエラー」や RuboCop のバージョン通知であり、テスト失敗時にはアサーションエラー等は通常通り表示されます。
    • テストをデバッグする際に HTTP ログが必要な場合は、該当テストから一時的に quietly を外すか、ローカルで patch を当て直して実行する必要が出るかもしれません。

  1. 参考情報 (あれば)

#56316 ActionView Strict Locals: Adds support for case when closing parenthesis is on next line

マージ日: 2025/12/7 | 作成者: @shivabhusal

  1. 概要 (1–2文で)
    Action View の「Strict Locals」機能で、locals: コメントのカッコ閉じ ) が次の行に書かれている場合でも正しくパースできるようにした PR です。これにより、複数行に整形された locals 宣言のバリエーションが追加でサポートされます。

  1. 変更内容の詳細

背景のおさらい

Strict Locals は、テンプレート先頭のコメントで受け取るローカル変数とデフォルト値を宣言する仕組みです(ERB だとこんな感じ):

erb
<%# locals: (arg_1:, arg_2: nil, arg_3: []) -%>

直近の PR #56270 で、これが複数行にまたがるケースがサポートされました:

erb
<%# locals: (arg_1:,
             arg_2: nil,
             arg_3: []) -%>

ただし、閉じカッコ ) を別行にそろえて書くこのスタイルは未対応でした:

erb
<%# locals: (
              arg_1:,
              arg_2: nil,
              arg_3: []
             ) -%>

この PR は、この「閉じカッコだけ次の行」パターンを parser が認識できるようにする小さな修正です。

実際の変更点

変更ファイルは 2 つだけです。

  1. actionview/lib/action_view/template.rb

    • Strict Locals コメントをパースする正規表現 or 行処理ロジックを 1 行だけ修正。
    • 具体的には、locals: ( で始まるコメントから ) までを複数行にわたって読み取る際に、
      「引数行」と「閉じカッコ行」が明確に分かれているケースも含めるようになっています。

    イメージとしては、こんなパターンが新たに許可されるようになった、と考えてよいです:

    erb
    <%# locals: (
                 arg_1:,
                 arg_2: nil,
                 arg_3: []
                ) -%>
  2. actionview/test/template/template_test.rb

    • 上記のパターンが正しく解釈されることを保証するテストが 13 行分追加。
    • テストでは、Strict Locals コメントで宣言した arg_1, arg_2, arg_3 が期待通りに利用できるかを確認しています。

SLIM のサンプル

作者が Slim でも動作確認をした例:

slim
/# locals: (
           arg_1_req:,
           arg_2: 'default2Multi',
           arg_3: 'default3multi'
           )
p With a partial with strict locals!
p = arg_1_req
p = arg_2
p = arg_3

このように、コメント中の locals: ( から改行をはさみ、最後に単独行で ) を書いてもパースされます。


  1. 影響範囲・注意点
  • 対象:

    • Action View の Strict Locals 機能を利用している ERB / Slim /(おそらく)Haml テンプレート。
    • 特に「整形のために locals: コメント内の閉じカッコを次の行に出す」コーディングスタイルを採っている場合に恩恵があります。
  • 互換性:

    • 既存の 1 行 / 複数行の locals 宣言はそのまま動作します。
    • 追加で 1 パターンのフォーマットが許容されただけの変更なので、後方互換性への影響はほぼありません。
  • Haml について:

    • 明示的なテストはされていませんが、Action View 側のテンプレートパースロジック変更なので、同じコメント形式であれば Haml でも同様に動作するはず、という前提です。
    • プロジェクトで Haml + Strict Locals を使っている場合は、念のためこのフォーマットのコメントを 1 つ用意して CI/手動で確認しておくと安全です。
  • 注意点:

    • あくまで locals: コメントのフォーマット解釈の改善であり、Strict Locals の意味論(必須/任意、デフォルト値など)には変更はありません。
    • コメントの開始と終了(例: <%# ... -%>/# ...)のシンタックスは、各テンプレートエンジンのルールに従う必要があります。

  1. 参考情報 (あれば)
  • 対応した元の issue/PR:
  • Strict Locals 全般の利用イメージ:
    • Rails Guides にはまだ詳細がない場合がありますが、ActionView::Template の実装およびテスト (actionview/test/template/template_test.rb) が現時点での仕様のソースとなります。

#56304 Fix dumping materialized views indexes

マージ日: 2025/12/6 | 作成者: @fatkodima

  1. 概要 (1-2文で)
    このPRは、Active Record の schema_dumper が「マテリアライズドビューに付いているインデックス」を正しくダンプできていなかった問題 (#56301) を修正するものです。PostgreSQL などで materialized view を使う際に、schema.rb へのインデックス出力が正しく行われるようになります。

  1. 変更内容の詳細

※ 実際の diff は 2 行追加・2 行削除のみと非常に小さいですが、主なポイントは「インデックスをダンプする対象に materialized view を含めるようにした」ことです。

activerecord/lib/active_record/schema_dumper.rb 内で、インデックスを列挙する処理は通常「テーブル一覧」に対して行われますが、これが materialized view のインデックスを拾えていませんでした。

イメージとしては、次のような修正が行われています(疑似コードレベル):

ruby
# 修正前(イメージ)
tables.each do |table_name|
  indexes(table_name).each do |index|
    dump_index(index)
  end
end

# 修正後(イメージ)
(tables + materialized_views).each do |relation_name|
  indexes(relation_name).each do |index|
    dump_index(index)
  end
end

あるいは、インデックスを抽出する際に

  • table_exists? だけを見ていた → materialized view も対象にする条件へ変更
  • materialized view を除外するフィルタがかかっていた → そのフィルタを修正

といった形で、schema dumper が「インデックスを持つ relation として materialized view を扱う」ように2行分ロジックが修正されています。

この結果、たとえば PostgreSQL で:

sql
CREATE MATERIALIZED VIEW popular_articles AS
  SELECT id, title, views FROM articles WHERE views > 1000;

CREATE INDEX index_popular_articles_on_views
  ON popular_articles(views);

のような定義をしている場合、bin/rails db:schema:dump 後の schema.rb にも次のようなインデックス定義が含まれるようになります:

ruby
create_view "popular_articles", materialized: true, sql_definition: <<-SQL
  ...
SQL

add_index "popular_articles", ["views"], name: "index_popular_articles_on_views"

  1. 影響範囲・注意点
  • 影響を受けるのは「schema.rb を使っている」かつ「materialized view にインデックスを定義している」アプリケーションです。
  • これまで:
    • materialized view 上のインデックスが schema.rb に出力されず、
    • db:schema:load してもインデックスが再現されない
      という不具合があった場合、この修正により解消されます。
  • 変更は schema dump 時の出力内容のみであり、既存DBには直接変更を加えません。
    ただし、Rails をこの修正を含むバージョンに上げてから db:schema:dump すると、schema.rb に新たにインデックス定義が追加されるため、git diff が増える可能性があります。
  • SQL スキーマ (structure.sql) を使っている場合は、本修正の直接的な影響はほぼありません(pg_dump がそのまま出力するため)。

  1. 参考情報 (あれば)

#56297 Wrap console command with an executor by default

マージ日: 2025/12/6 | 作成者: @zzak

  1. 概要 (1-2文で)
    Rails コンソール (bin/rails console) が、デフォルトで Rails.application.executor にラップされた状態で起動するようになりました。
    runner と同様に、-w または --skip_executor オプションでこの挙動を無効化できます。

  1. 変更内容の詳細

2-1. コンソールが Executor でラップされるように

これまで rails console は、基本的に「素の」プロセス上で Rails 環境をロードしていましたが、この PR により、コンソールの実行処理が Rails.application.executor でラップされるようになりました。

イメージとしては、以下のようなことが行われるようになったと考えられます(実際の実装イメージ):

ruby
# railties/lib/rails/commands/console/console_command.rb (概念的なイメージ)

def start
  if options[:skip_executor] # -w / --skip_executor
    run_console
  else
    Rails.application.executor.wrap do
      run_console
    end
  end
end

Rails.application.executor.wrap は、Rails の「スレッド/リクエストライフサイクル」を意識したコンテキストを張るための仕組みで、たとえば以下のようなことを行います:

  • Current attributes(Current オブジェクト)などのスレッドローカル状態のセットアップ/クリーンアップ
  • Active Support Executor Hook(to_run, to_complete など)を通した前後処理の実行
  • 一部のキャッシュやコネクション管理の整合性向上

これにより、「本番環境のリクエストと近いコンテキスト」でコンソールを使えるようになります。

2-2. オプション -w / --skip_executor の追加

runner (bin/rails runner) と同じオプション体系に揃える形で、コンソールにも以下のオプションが追加/有効化されています:

  • -w
  • --skip_executor

これらを指定した場合は、これまで通り Executor に包まれない状態でコンソールを起動します。

bash
# Executor を使わないコンソール起動例
bin/rails console -w
# または
bin/rails console --skip_executor

テスト (railties/test/application/console_test.rb) では、以下のような内容が確認されているはずです:

  • デフォルトで Executor が使われること
  • --skip_executor 指定時には Executor を通さないこと
  • runner と同等の動作保証(オプションの互換性)

2-3. CHANGELOG への追記

railties/CHANGELOG.md に、この挙動変更が明記されています。
Rails をバージョンアップする際の互換性確認ポイントとして記録されました。


  1. 影響範囲・注意点

  2. コンソールでのコード実行が、本番リクエストに近いコンテキストになる

    • Current を使ったコンテキスト依存のコードや、Executor フック (ActiveSupport::Executor) を利用しているコードが、コンソールでもより本番に近い挙動になります。
    • これまで「コンソールだと動くが、本番だと動かない/逆に本番ではフックが効くがコンソールでは効かない」といった差がある場合、それが解消・もしくは現れ方が変わる可能性があります。
  3. Executor フックに副作用がある場合は要確認

    • Rails.application.executor.to_run / to_complete などで重い処理やログ出力などをしている場合、コンソール起動・終了時にもそれらが実行されるようになります。
    • コンソール起動が重く感じるようになった場合は、フックの中身を見直すか、コンソールだけ --skip_executor を使う運用も検討できます。
  4. 既存ツール・スクリプトへの影響

    • rails console をラップして独自にスレッドローカルやグローバル状態を扱っているツールがあると、Executor の導入で状態がリセットされるタイミングが変わることがあります。
    • 互換性を確保したい場合は、ツール側で rails console --skip_executor を使うか、Executor 前提の設計に寄せる必要があります。
  5. runner との挙動統一

    • 既に runner で -w/--skip_executor を使っていたプロジェクトは、「コンソールも同じ思想で使える」ようになります。
    • コンソールと runner で Executor の有無を揃えておくと、デバッグ時の挙動差分が減らせます。

  1. 参考情報 (あれば)
  • 関連 Issue: Fixes #56277
    コンソールでも Executor を使うべき、という要望・不整合報告に対応したものと思われます。
  • Rails.application.executor / ActiveSupport::Executor のドキュメント
    • Rails ガイド: 「Threading and Code Execution」や「Active Support Instrumentation」周辺
    • ソース: activesupport/lib/active_support/executor.rb
  • 類似仕様:
    • bin/rails runner が Executor でラップされており、-w / --skip_executor で無効化できる仕様との整合性をとる変更です。

#56270 Fix ERB strict locals parsing when comment spans multiple lines

マージ日: 2025/12/6 | 作成者: @Saidbek

  1. 概要 (1-2文で)
    ERB の「strict locals」用マジックコメントが複数行に渡る場合にローカル変数解析が失敗していたバグを修正した PR です。locals: コメントを複数行に分割して書いても、正しく必須/オプションローカル変数が解釈されるようになります。

  1. 変更内容の詳細(あればサンプルコードも含めて)

何が問題だったか

_partial.html.erb 内で strict locals を次のように 1 行で書く場合は動作していました:

erb
<%# locals: (arg_1:, arg_2: nil, arg_3: []) %>

しかし、可読性のために次のように複数行に分割すると:

erb
<%# locals: (arg_1:,
             arg_2: nil,
             arg_3: []) %>

arg_2 などがローカル変数として認識されず、undefined local variable or method 'arg_2' のようなエラーが発生していました。

原因は、strict locals のマジックコメントをパースするための正規表現 STRICT_LOCALS_REGEX が改行をまたいでマッチしない実装になっていたことです。

具体的な修正内容

actionview/lib/action_view/template.rbSTRICT_LOCALS_REGEX が以下のように変更されました。

ruby
# 変更前
STRICT_LOCALS_REGEX = /\#\s+locals:\s+\((.*)\)/

# 変更後
STRICT_LOCALS_REGEX = /\#\s+locals:\s+\((.*?)\)/m

ポイント:

  • m フラグの追加

    • Ruby の正規表現では、デフォルトでは . は改行にマッチしません。
    • /.../m とすることで . が改行も含めてマッチするようになります。
    • これにより locals: ( ... ) の中身が複数行でもパース可能になります。
  • .*.*?(非貪欲)に変更

    • .* は「できるだけ多くマッチ」するため、テンプレート中に複数の ) があると最後の ) まで巻き込んでしまう可能性があります。
    • .*? とすることで「できるだけ少なくマッチ」となり、最初に現れる ) で正しく止まるようになります。

テストの追加

actionview/test/template/template_test.rb に multiline の locals コメントをカバーするテストが追加されています。
テスト内容は PR 説明のスクリプトとほぼ同等で、以下の 2 パターンを検証します:

  1. 単一行の strict locals コメントで partial を render するケース(既存挙動の確認)
  2. 複数行の strict locals コメントで partial を render するケース(今回修正対象)

期待値:

  • arg_1locals で渡した "First" が表示される
  • arg_2 はデフォルト nil なので空文字として表示される
  • arg_3 はデフォルト [] なので "[]" と表示される

  1. 影響範囲・注意点
  • 影響範囲
    • Action View の ERB テンプレートで、strict locals マジックコメント(<%# locals: (...) %>)を利用している部分に限定されます。
    • 特に、マジックコメントを改行して書いている or これから可読性のために改行したい partial で恩恵があります。
  • 既存コードへの影響
    • これまで 1 行で書いていた strict locals コメントの挙動に変更はありません。
    • 正規表現が非貪欲(.*?)になったことで、逆に「誤って広くマッチし過ぎていた」ケースがあれば、より正確にマッチするようになりますが、通常の use case では問題にならないと考えられます。
  • 注意点
    • マジックコメント自体のシンタックスは従来通りで、# locals: (arg_1:, arg_2: nil) の形式を守る必要があります。
    • コメント内のカッコの対応が崩れている(( に対応する ) がないなど)場合は、正規表現も正しくマッチできないため、引き続き注意が必要です。

  1. 参考情報 (あれば)
  • 対象 Issue: Fixes #56239
  • 修正ファイル:
    • actionview/lib/action_view/template.rb
    • actionview/test/template/template_test.rb
  • 機能としての「strict locals」について(参考用イメージ):
    • partial 冒頭に
      erb
      <%# locals: (title:, content: nil) %>
      のように書くと、その partial は title を必須、content をオプションとして受け取ることを Action View がチェックする仕組みです。
    • 今回の修正により、これを
      erb
      <%# locals: (
            title:,
            content: nil
          ) %>
      のような複数行スタイルでも安全に使えるようになります。

#56300 Fix typos in JavaScript guide

マージ日: 2025/12/6 | 作成者: @Saidbek

  1. 概要 (1-2文で)
    Rails のガイド「Working with JavaScript in Rails」に含まれていた誤り・不整形な箇所を修正した PR です。主にドキュメントの整形改善と、非同期 JavaScript のコード例の誤り修正が行われています。

  1. 変更内容の詳細

変更ファイル:

  • guides/source/working_with_javascript_in_rails.md (+3/-3)

この PR のポイントは 2 つです。

(1) 「生ファイル」として読んだときのフォーマット改善

Markdown ガイドを GitHub 上の「Raw」表示などで直接読んだときにも読みやすくなるように、軽微な書式の修正がされています。
具体的には、インデント・スペース・改行位置などの微調整で、Markdown としてレンダリングされない環境でも文やコードブロックが崩れないようにした変更と考えられます。

※行差分が +3/-3 なので、見出しのレベルやスペースの補正、コードフェンス周りの修正など、ごく小さな変更のはずです。

(2) 非同期 JavaScript コード例 (async) の修正

ガイド内に掲載されていた async/await を使った JavaScript のサンプルコードに誤りがあり、それを修正しています。

典型的には次のようなタイプの問題が想定されます:

  • async / await の位置がおかしい
  • await していないため Promise がそのまま返ってしまう
  • エラーハンドリング (try/catch) の抜けや記法ミス
  • Rails のエンドポイント呼び出し例として不正な書き方 (fetch の使い方、ヘッダ/メソッド指定など)

この PR は「Fixed async js code example」と明記されているので、ガイドを読んでそのままコピペしても正しく動作しない、あるいは誤解を生む記述になっていたのを、正しい async/await のサンプルに直したものとみなせます。

例として、Rails ガイドでよく出る形は以下のようなコードです:

js
async function createPost() {
  const response = await fetch("/posts", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-CSRF-Token": document.querySelector("meta[name='csrf-token']").content,
    },
    body: JSON.stringify({ post: { title: "Hello", body: "World" } }),
  });

  if (!response.ok) {
    throw new Error(`HTTP error: ${response.status}`);
  }

  const data = await response.json();
  console.log(data);
}

今回の修正も、このように:

  • async function ... または const foo = async () => {} の形が正しい
  • await fetch(...) とし、レスポンスに対してさらに await response.json() などを行う
  • 文法エラーにならないように ; やカンマ、括弧などを整える

といった点が直されているはずです。


  1. 影響範囲・注意点
  • 影響範囲は ドキュメントのみ であり、Rails 本体の挙動や API には一切変更がありません。
  • ただし、以前のガイドの async サンプルコードをそのまま使っていた場合:
    • コードが動かなかったり、
    • たまたま動いていても「Promise を正しく待っていない」「エラーハンドリングが不十分」といった問題を含んでいる可能性があります。
      そのため、JavaScript ガイドの async コード例を参照している箇所があれば、新しいガイド版と差分を見て、自分のコードも修正する価値があります。
  • テストや CHANGELOG の更新は不要な種類の変更(ドキュメント修正)として扱われており、実際にテスト追加や CHANGELOG 変更は行われていません。

  1. 参考情報 (あれば)

この PR は、上記ガイドの質を高め、特に async/await を使った例を正しく理解・利用できるようにするための細かな改善です。


#56208 Fix bug when txn isolation would not reset or when requires_new is set

マージ日: 2025/12/6 | 作成者: @kirs

  1. 概要 (1-2文で)
    このPRは、トランザクションの分離レベル (isolation level) を一時的に変更した後に元に戻らないバグと、requires_new: true なネストトランザクションで isolation 指定が正しく扱われないバグを修正しています。Active Record のトランザクション分離レベルを多用するアプリケーション(例: Shopify)のワークフローに直接影響する問題へのピンポイント修正です。

  1. 変更内容の詳細

※実際のコード全文は省略しますが、PR説明と diff 情報から復元できる挙動レベルで解説します。

背景: #55549 での変更漏れ

#55549 で導入された「トランザクションの isolation レベルをブロック単位で変更する」機能において:

  1. ブロック実行後に current_transaction.isolation を元の値に戻していなかった
  2. requires_new: true のネストトランザクション時に isolation 指定を正しく扱っていなかった

という2点のバグが発生していました。

修正ポイント1: isolation レベルのリセット

ActiveRecord::ConnectionAdapters::DatabaseStatements#transaction(あるいはそれを内部で呼び出すヘルパー)の中で、
「トランザクション開始前の isolation」を退避し、ブロック終了後に必ず元の isolation を復元するように修正されています。

イメージとしては、以下のような処理になります:

ruby
def transaction(**options, &block)
  # もともとの isolation を保存
  previous_isolation = current_transaction.isolation

  # options[:isolation] が指定されていれば、その isolation でトランザクション開始
  # ...

  yield
ensure
  # ブロック終了後に isolation を元の値に戻す
  current_transaction.isolation = previous_isolation
end

これにより、以下のようなケースで isolation が「後続のトランザクションにも引き継がれてしまう」という誤動作が解消されます。

ruby
# 例: これまでは tx2 も :serializable のままになってしまうバグがあった

User.transaction(isolation: :serializable) do
  # ここだけ serializable を想定
  ...
end

User.transaction do
  # 本来はデフォルトの isolation (DB の設定) を期待
  ...
end

PR後は、tx1 の isolation 指定は tx1 のスコープだけに閉じるようになります。

修正ポイント2: requires_new: true での isolation の扱い

requires_new: true を指定したネストトランザクションに isolation を渡した場合のハンドリングも修正されています。

典型例:

ruby
User.transaction do
  # outer transaction: デフォルト isolation

  User.transaction(isolation: :serializable, requires_new: true) do
    # inner transaction: serializable
    ...
  end

  # outer transaction: isolation は変わらず(デフォルトのまま)
end

従来のバグでは、

  • inner トランザクションで指定した isolation が current_transaction に残り、outer に影響する
  • そもそも inner の isolation 指定が正しく効かない

といった挙動が起こりうる状態でした。

このPRでは、

  1. requires_new の inner トランザクションについても isolation が正しく設定される
  2. inner の終了後に、outer トランザクションの isolation に確実に戻す

というロジックが追加されています。

テストの追加

activerecord/test/cases/transaction_isolation_test.rb に8行のテストが追加されており、少なくとも以下のようなシナリオをカバーしていると考えられます:

  • isolation を指定したトランザクションの後、isolation 未指定のトランザクションを開始すると元の isolation に戻っていること
  • requires_new: true のネストトランザクションで isolation を指定しても、外側の isolation は変化しないこと

これにより、前述の2つのバグが再発しないことを保証しています。


  1. 影響範囲・注意点

影響範囲

  • Active Record のトランザクション API を使い、特に isolation: オプション・requires_new: true を併用しているコードが対象です。
  • transaction を素朴に transaction do ... end とだけ使っているアプリケーションへの影響はほぼありません(isolation を明示指定していなければ振る舞いは DB デフォルトのまま)。

アプリケーション側で変わる可能性がある挙動

  1. 「意図せず高い isolation が引き継がれていた」ケースが正常化される
    以前は、あるブロックで isolation: :serializable を指定すると、その後のトランザクションでも serializable が継続してしまっていた可能性があります。
    これを前提にしていた(=バグ依存)コードがあると、以降のトランザクションの isolation が「元に戻る」ことで挙動が変わります。

  2. requires_new ネスト時の isolation がより直感的になる
    requires_new 付きの中で isolation を変えると、その効果は inner トランザクションの中だけに留まり、outer には漏れなくなります。
    これにより、想定していた isolation 境界(outer: read committed / inner: serializable など)が、実装通りに機能するようになります。

注意点

  • 「特定の処理以降、アプリ全体のトランザクション isolation が変わる」という挙動に気づかずに依存していた場合、パフォーマンス特性やデッドロック発生率が変わる可能性があります。
    isolation を明示指定している部分があれば、その前後のトランザクションの挙動を確認した方が安全です。
  • マルチDB構成やカスタム connection handler を使っている場合も、current_transaction 単位で isolation がきちんとスコープされるようになるため、 isolation まわりの暗黙的な前提がないかを確認してください。

  1. 参考情報 (あれば)
  • このPRで修正している元の変更:
  • 同じ著者による、より大きな関連 PR(今回よりスコープが広いもの):
  • 対象ファイル:
    • activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
    • activerecord/test/cases/transaction_isolation_test.rb
    • activerecord/CHANGELOG.md (バグ修正として明記)

このPRは、小さなコード変更で isolation のスコーピングを正す「バグフィックス」であり、API 仕様の変更というよりは、ドキュメントや直感に沿った挙動への修正、と考えると理解しやすいです。


#56299 Fix typos in command line guide

マージ日: 2025/12/5 | 作成者: @Saidbek

  1. 概要 (1-2文で)
    Rails ガイド「Command Line」のドキュメント中の軽微なタイポ(重複語・スペース抜け・重複行)を修正した PR です。コードや挙動の変更はなく、文章表現のみの修正です。

  2. 変更内容の詳細
    guides/source/command_line.md に対して、以下3点の修正が行われています。

  • 文中の「with the the bin/rails」という表現から、重複していた "the" を1つ削除

    • 修正前: with the the \bin/rails``
    • 修正後: with the \bin/rails``
  • assets:clobber に関する記述が同じ内容で2回連続していたため、重複行を削除

    • 例:
      • 修正前(イメージ)
        md
        bin/rails assets:clobber
        bin/rails assets:clobber
      • 修正後
        md
        bin/rails assets:clobber
  • 「Thebin/rails」のように "The" とバッククォートの間のスペースが抜けていた箇所を修正

    • 修正前: The\bin/rails``
    • 修正後: The \bin/rails``

いずれもガイドの可読性・整合性を高めるための、純粋な文章レベルの修正です。

  1. 影響範囲・注意点
  • 対象はドキュメントガイドのみであり、Rails 本体のコード、挙動、API には一切影響がありません。
  • コマンドの使い方や意味は変わっておらず、既存アプリケーションやツールチェーンへの影響もありません。
  • ドキュメント参照時に、より正確・自然な英語で読めるようになります。
  1. 参考情報 (あれば)

#56213 Respect config.log_level to emit debug events

マージ日: 2025/12/5 | 作成者: @claudiob

  1. 概要 (1-2文で)
    Rails の「デバッグイベントを出すかどうか」の判定条件を、環境名(development かどうか)ではなく config.log_level に基づくように変更する PR です。これにより、RAILS_LOG_LEVEL=debug を設定すれば test や production でもデバッグイベントを出力できるようになります。

  1. 変更内容の詳細

変更点の本質

以前の PR #55657 では、「開発環境 (development) のときだけデバッグイベントを有効にする」という実装になっていました。この PR では、その条件を:

ruby
Rails.env.development?

から:

ruby
config.log_level == "debug"

に置き換えています(実際のコードは railties/lib/rails/application/bootstrap.rb 内の 1 行差し替えのみ)。

Rails ガイドにもある通り、デフォルトでは:

  • 全体のデフォルト: :debug
  • 生成された config/environments/production.rb のデフォルト: config.log_level = :info

となっているため、デフォルト構成では従来と動作はほぼ同じ(本番では debug イベントは出ない)ですが、config.log_level:debug に変えた環境ではデバッグイベントが有効になるよう変わります。

どうやって有効にするか(サンプル)

たとえば production でもデバッグイベントを見たい場合は、以下のどれかのように設定できます。

1. 環境変数で指定(推奨されている使い方)

bash
RAILS_LOG_LEVEL=debug bin/rails server

RAILS_LOG_LEVEL は Rails が config.log_level に反映するので、この PR により debug イベントが有効になります。

2. 設定ファイルで直接指定

config/environments/production.rb:

ruby
Rails.application.configure do
  config.log_level = :debug
end

これにより production でも development 同様にデバッグイベントが出力されます。


  1. 影響範囲・注意点
  • 環境に依存しない挙動になる
    以前は「development だけ特別扱い」でしたが、今後は「ログレベルが debug かどうか」で決まります。

    • test / production で config.log_level = :debug にしているプロジェクトでは、これまで出ていなかったデバッグイベントが新たに出る可能性があります。
  • デフォルト設定では互換性維持
    生成直後の Rails アプリの典型的な設定では:

    • development: log_level = :debug → デバッグイベント「あり」(従来通り)
    • test: 通常は :debug → この PR により test でも debug イベントが出るようになっている可能性がある(以前は「development 限定」だった場合との差分)
    • production: log_level = :info → デバッグイベント「なし」(従来通り)

    test 環境での挙動は、#55657 の実装内容や各プロジェクトの config.log_level 設定に依存します。
    もし test でログが多すぎると感じる場合は、config/environments/test.rbconfig.log_level:info 以上に上げることで抑制できます。

  • 本番で debug を有効にする際の注意

    • ログ量が非常に増える可能性があるため、ストレージ・ログローテーション・ログ集約基盤(例: ELK、Loki など)の負荷に注意が必要です。
    • デバッグログに機微情報が含まれる場合、コンプライアンス上のリスクも増えます。production で :debug を使う場合は、ログ内容の棚卸し・マスキングを検討してください。

  1. 参考情報 (あれば)

#51238 Extract ActionText::Editor base class and ActionText::TrixEditor adapter

マージ日: 2025/12/5 | 作成者: @seanpdoyle

  1. 概要 (1–2文で)
  • Action Text にエディタ共通の基底クラス ActionText::Editor と、Trix 向けアダプタ ActionText::TrixEditor を導入し、「Trix 前提の API」を整理・非推奨化した PRです。
  • これにより、将来的に Trix 以外のリッチテキストエディタを公式な拡張ポイントから統一的に統合できる土台が整えられています。

  1. 変更内容の詳細

2-1. ActionText::Editor 基底クラスとアダプタ層

新しく以下の構成が導入されています。

  • 基底クラス: ActionText::Editor
  • アダプタ:
    • ActionText::TrixEditor(Trix 用)
  • 周辺クラス:
    • ActionText::Editor::Registry
    • ActionText::Editor::Configurator

これらは「エディタごとの差異を Action Text 本体から切り離す」ためのアダプタ層です。
従来はクラス名やメソッド名、引数名に trix が直接現れていた箇所がありましたが、それらを editor ベースの抽象化に移し替えています。

主な責務の集約

PR 説明にある通り、多数のクラスメソッド・インスタンスメソッドが ActionText::TrixEditor へ集約されています。
多くは「元の実装をそのままコピー」したうえで、命名に含まれる trixeditor に置き換える程度の変更に留めてあり、振る舞いは最大限維持されています。

例イメージ(あくまで概念的なもの):

ruby
# 以前: Trix 前提のユーティリティが色々な場所に散らばっていた
ActionText::Content.foo_trix_html(...)
ActionText::TrixAttachment.some_trix_specific_behavior(...)

# 以後: エディタアダプタのメソッドとして集約
ActionText::TrixEditor.new(...).to_html(...)
ActionText::TrixEditor.new(...).convert_attachments(...)

今後、他エディタ (Quill, CKEditor など) を統合する場合は ActionText::Editor を継承したアダプタクラスを実装し、Registry 経由で登録する形が想定されます。


2-2. エディタ登録用の Registry / Configurator

  • ActionText::Editor::Registry

    • エディタアダプタクラスを名前などで引けるようにするレジストリ。
    • 「どのエディタ名がどのアダプタクラスに対応するか」を一元管理する役割。
  • ActionText::Editor::Configurator

    • インストール・初期化の段階で「どのエディタを既定にするか」などを設定するためのクラス。
    • Rails 側の初期化 (engine) や generator (install_generator.rb) から利用されることで、「アプリケーションとして Action Text をどのエディタと組み合わせるか」を決めやすくしています。

これらはまだ API としては「実装詳細扱い(private 的)」で、今後の拡張やドキュメント整備の余地を残しています。


2-3. Trix 依存コードの整理と非推奨化

PR 説明にある通り、2 つの種類の非推奨があります。

  1. モジュール / クラスの非推奨

    • Trix 特化、または内部用であるにもかかわらず public な定義だったクラス / モジュールが対象。
    • 今回、責務の一部または全てを ActionText::Editor / ActionText::TrixEditor 側へ移譲し、旧クラス/モジュールは deprecate されたうえで、新実装経由で動くようにラップされているものがあります。
    • ソース中では :nodoc: が付いていなかったものの、実質 internal 想定の物が主な対象です。
  2. メソッドの非推奨

    • 名前や引数に trix を含むもの、またはドキュメント / コメントがなく internal 想定と判断された public メソッドが対象。
    • 可能な限り、旧メソッドは warning を出しつつ ActionText::TrixEditor のメソッドを呼び出すだけの thin wrapper に変わっているため、既存コードは直ちに壊れずに移行が可能です。

例(あくまでイメージ):

ruby
# 旧 API: trix 固有の名前
def trix_attachment_url(...)
  ActiveSupport::Deprecation.warn("... use editor_attachment_url instead ...")
  editor = ActionText::TrixEditor.new(...)
  editor.attachment_url(...)
end

# 新 API: editor ベースの名前 (TrixEditor 内)
def attachment_url(...)
  # 実装は従来とほぼ同じ
end

2-4. 添付ファイル周りのリファクタ

変更ファイルから読み取れる範囲でのポイント:

  • actiontext/lib/action_text/attachments/conversion.rb 追加

    • 添付ファイルの変換処理(HTML への変換、Trix 用の attributes 生成など)を共通化するためのモジュール/クラス。
    • 従来 .../attachments/trix_conversion.rb にあった Trix 固有処理のうち、エディタ非依存にできる部分がこちらへ移されています。
  • actiontext/lib/action_text/attachments/trix_conversion.rb

    • 依然として存在しますが、内部でより汎用的な attachments/conversion を利用する形に寄せられています。
  • ActionText::Attachable, ActionText::Attachment, ActionText::Content

    • エディタに依存しない添付ファイル・コンテンツの抽象化を維持しつつ、必要な箇所のみ新しいエディタアダプタを呼び出すようになっています。

2-5. Engine / Generator / テンプレートの更新

  • actiontext/lib/action_text/engine.rb

    • Engine 初期化時に ActionText::Editor 周り(Registry など)をセットアップする処理が追加。
    • デフォルトで ActionText::TrixEditor を登録する処理が含まれている形が想定されます。
  • actiontext/lib/generators/action_text/install/install_generator.rb

    • インストール時の JS / ビュー / 初期化コード生成が、「Trix 固定」よりも「TrixEditor を使う構造」へと更新。
    • 将来的にはジェネレータのオプションで他エディタを選択する拡張がしやすくなります。
  • actiontext/test/dummy 以下の JS・View・Model

    • Dummy アプリ側も新しい Editor アダプタ構成に合わせてサンプルが更新されています。
    • 例:
      • application.js におけるエディタ関連の初期化コード
      • メッセージフォーム (messages/_form.html.erb) での rich_text_area / editor 選択の書き方

  1. 影響範囲・注意点

3-1. 一般的な Rails アプリ (通常の Action Text + Trix 利用)

  • rich_text_area など、通常の公開 API を使っているだけのアプリは基本的にそのまま動作します。
  • ただし、以下に当てはまる場合は deprecation warning が出る可能性があります。
    • ActionText::TrixAttachmentActionText::Content の Trix 依存メソッドを直接呼んでいる
    • trix_* を名前に含むメソッドをアプリケーション側で利用している

現時点では互換ラッパーがあるので即座に壊れることは少ないですが、ログの deprecation warning をチェックし、将来のメジャーバージョンでの削除を見越して 新しい Editor ベースの API への移行を検討した方がよいです。

3-2. エディタ連携を拡張しているライブラリ / エンジン作者

  • Action Text と外部エディタ(例: Quill, CKEditor)を連携する gem / plugin を書いている場合、この PR はかなり重要です。

    • これまで Trix 専用の内部 API をハックしていた部分を、ActionText::Editor のサブクラスとして正式に実装できる方向性が示されました。
    • まだアダプタインターフェイスは「実装詳細扱い」ですが、今後ここが公式拡張ポイントとして明文化されていく流れが読み取れます。
  • 新規実装の基本方針:

    1. class MyCoolEditor < ActionText::Editor を定義
    2. Registry に登録(ActionText::Editor::Registry.register(:my_cool_editor, MyCoolEditor) のような形が想定される)
    3. 添付ファイル変換・HTML 生成・ツールバー設定などを TrixEditor と同等のインターフェイスで実装

まだ細部仕様はコードを読まないと分からないため、この PR を足掛かりに actiontext/lib/action_text/editor/*.rb を確認する必要があります。

3-3. 内部 API への依存

  • PR で「これは実装詳細であり、API としては private のつもり」と明言されているため、ActionText::Editor 一式も将来のバージョンで破壊的変更が入る可能性があります。
  • ライブラリレベルでここに依存する場合は、バージョン制約やテストで追従コストを見込んでおくべきです。

  1. 参考情報
  • PR: https://github.com/rails/rails/pull/51238
  • 関連 Issue: https://github.com/rails/actiontext/issues/41
  • 変更された主なファイル:
    • actiontext/lib/action_text/editor.rb
    • actiontext/lib/action_text/editor/trix_editor.rb
    • actiontext/lib/action_text/editor/registry.rb
    • actiontext/lib/action_text/editor/configurator.rb
    • actiontext/lib/action_text/attachments/conversion.rb
    • actiontext/lib/action_text/attachments/trix_conversion.rb
    • actiontext/lib/generators/action_text/install/install_generator.rb
    • actiontext/lib/action_text/engine.rb
    • actiontext/CHANGELOG.md

この PR 時点では「Trix 用アダプタを抽出しつつ、まだ Trix がデフォルト」であり、「他エディタを正式にサポートするための下準備」がメインテーマです。


#56236 [ci skip] Update getting_started.md to point to application.html.erb for navbar menu entry

マージ日: 2025/12/5 | 作成者: @Tretent

  1. 概要 (1-2文で)
    Railsの「Getting Started」ガイド内で、ナビゲーションバーにログインメニューを追加する手順の記述ファイル名が誤っていた点を修正し、index.html.erb ではなく application.html.erb を参照するように更新したドキュメント専用のPRです。アプリケーションコードや挙動には一切変更はありません。

  1. 変更内容の詳細(あればサンプルコードも含めて)
  • 対象ファイル: guides/source/getting_started.md
  • 変更点の主旨:
    • ガイドの 11.3 節付近で、「Login リンクを index.html.erb に追加する」と書かれていたが、実際にはナビゲーションバーが定義されている application.html.erb に追加するべきであるため、説明文中のファイル名を application.html.erb に修正。
  • 変更されたのは Markdown 上の記述のみで、コード例やロジック自体が変わったわけではなく、「どのファイルを編集するのが正しいか」という参照先の修正です。

(サンプルイメージ: 実際のPR文面からの推定)

erb
<!-- app/views/layouts/application.html.erb のナビバー例 -->
<nav>
  <%= link_to "Home", root_path %>
  <%= link_to "Login", login_path %>
</nav>

従来のガイドでは、上記のようなナビゲーションを index.html.erb 側に書くかのような記述になっていたため、それを「レイアウトファイルである application.html.erb に書く」という正しい形に揃えています。


  1. 影響範囲・注意点
  • 影響範囲:
    • Rails Guides(ドキュメント)のみであり、Rails本体のコード、テスト、挙動には影響しません。
    • Getting Started ガイドを見ながらチュートリアルを進めているユーザーにとって、ナビゲーションバーの編集箇所が正確になり、混乱が減ります。
  • 注意点:
    • 既に旧ガイドの指示通りに index.html.erb にナビバーやログインリンクを追加していた場合は、レイアウト (application.html.erb) に移動するのが望ましい構造です。
      • レイアウトに置けば全ページで同じナビが共有されるため、チュートリアルの意図にも合致します。
    • CHANGELOG は更新されていない通り、リリースノート的な追跡も不要な軽微なDocs変更です。

  1. 参考情報 (あれば)
  • PR本体: rails/rails#56236
  • 関連コンセプト:
    • Railsにおけるレイアウトファイル: app/views/layouts/application.html.erb
      • 全ページ共通のヘッダー/フッター/ナビゲーションバーなどを定義するのが一般的
    • 個別ビュー (index.html.erb など) にナビバーを書くと、他アクションで共有されず冗長になりやすい

このPRにより、Getting Startedガイドが実際のRailsのベストプラクティス(共通ナビはレイアウトに置く)と一致するようになっています。


#51951 ActiveStorage immediate variants

マージ日: 2025/12/5 | 作成者: @tomrossi7

  1. 概要 (1–2文で)
    Active Storage のバリアント(variants)生成方式に、添付と同時に同期的に生成するための immediate: true オプションが追加されました。これにより、これまでの「初回アクセス時にオンデマンド生成」や「バックグラウンドでの preprocessed 生成」に加え、トラフィック集中時の“生成レース”を避ける運用がしやすくなります。

  1. 変更内容の詳細

新オプション immediate: true

has_one_attached / has_many_attached の variant 定義で、既存の preprocessed: true と同じ要領で immediate: true を指定できます。

ruby
class User < ApplicationRecord
  has_one_attached :avatar do |attachable|
    # 添付と同時に thumb variant を生成
    attachable.variant :thumb, resize_to_limit: [100, 100], immediate: true
  end
end

動作イメージ:

  • これまで
    • デフォルト: 最初のアクセス時に variant を同期生成(コントローラのリクエスト内)
    • preprocessed: true: 添付完了後にバックグラウンドジョブで生成
  • これから
    • immediate: true: 添付処理の完了と同じタイミングで、その場で同期生成
      → 添付直後に大量アクセスが来ても「最初の何リクエストかがそれぞれ生成を試みる」レース状態を軽減できる

新ジョブ ActiveStorage::CreateVariantsJob

activestorage/app/jobs/active_storage/create_variants_job.rb が追加され、まとめてバリアントを生成する仕組みが導入されています。

主な役割:

  • 添付されたレコード・添付名を元に、関連する immediate / preprocessed variants を列挙
  • 指定された attachment について、対象の variants を生成する
  • これにより、複数の variants 生成を一元的に扱えるようになり、テストしやすくなっている

TransformJob は引き続き存在し、個々の変換実行はこれを通じて行われますが、呼び出し元の整理・責務分担が行われています。

ActiveStorage::NamedVariant の拡張

activestorage/app/models/active_storage/named_variant.rb の変更により:

  • 各 named variant が immediate / preprocessed などのオプションを持てるように整理
  • 変換用のオプションと、生成タイミングを表すオプションがはっきり切り分けられた形になっている

概ね次のようなイメージで、variant 定義のメタデータとして管理されます:

ruby
attachable.variant :thumb, resize_to_limit: [100, 100], immediate: true
# => name: :thumb, transformations: { resize_to_limit: [100, 100] }, immediate: true

Attachment 周りの変更

activestorage/app/models/active_storage/attachment.rb などで:

  • 添付の作成時に、関連する immediate variants を検出して生成するフックが追加
  • representable まわりの責務が整理され、blob 側から attachment 側にロジックが移動している(ActiveStorage::Blob::Representable からの削除がその一部)

これにより、attachment 単位で「どの variants をいつ生成するか」が把握しやすくなっています。

Preview / Variant クラスの微調整

preview.rb, variant.rb, variant_with_record.rb などで、主に:

  • 生成タイミングを考慮したインターフェイスの調整
  • テスト・ジョブとの整合性確保

が行われています。挙動自体は互換を保ったまま、immediate / preprocessed の二軸に対応できるよう整理されています。

テスト・ドキュメント

  • 新しいジョブや immediate オプションに対するテストが追加
    • create_variants_job_test.rb
    • named_variant_test.rb
    • attachment_test.rb など
  • 従来の variant 生成テストの一部が整理・削減(preview_image_job_test.rb ほか)
  • ガイド active_storage_overview.mdimmediate オプションの使い方が追記され、preprocessed との違いが説明されている

  1. 影響範囲・注意点
  • パフォーマンス特性の変化

    • immediate: true を付けた variants は「添付の保存と同じトランザクション or リクエストの中」で生成されるため、
      • 添付時のレスポンスタイムが伸びる可能性
      • 同時に大量のファイルをアップロードするケースでは、アプリサーバ・ストレージの負荷増加
    • 一方で、添付直後のアクセス集中時に variant 生成が競合する問題は緩和されます。
  • immediatepreprocessed の使い分け

    • 高トラフィックで、「アップロード直後に確実に完成した variant を配信したい」場合は immediate: true
    • 生成負荷をアプリリクエストから切り離したい場合は preprocessed: true
    • どちらも付けない場合は従来どおり「初回アクセス時にオンデマンド生成」

    設計としては、UX とインフラ負荷のトレードオフを見ながら、重要な variant だけ immediate にするのが現実的です。

  • 既存コードへの互換性

    • immediate を使わない限り、挙動は基本的に後方互換です。
    • ただし Active Storage の内部クラス(NamedVariant, Representable 周りなど)に依存している場合は、
      • メソッドの呼び出し経路
      • variant メタデータの持ち方 が変わっている可能性があるので、独自拡張をしているプロジェクトは差分確認が必要です。
  • ジョブキュー設定

    • preprocessed / immediate どちら経由であっても、内部では TransformJob などバックグラウンドジョブを使う部分があります。
    • Active Job のアダプタ(Sidekiq, Delayed Job など)の設定やキュー名をカスタマイズしている場合は、新ジョブ CreateVariantsJob のキュー設定も確認してください。

  1. 参考情報 (あれば)
  • 該当 PR: https://github.com/rails/rails/pull/51951
  • Active Storage ガイド(immediate / preprocessed オプションの説明が追記済み):
    guides/source/active_storage_overview.md
  • 関連実装ファイル(主なもの):
    • activestorage/app/jobs/active_storage/create_variants_job.rb
    • activestorage/app/models/active_storage/attachment.rb
    • activestorage/app/models/active_storage/named_variant.rb
    • activestorage/app/models/active_storage/variant*.rb

#56279 Use pattern matching on sql instead of string match event payload name

マージ日: 2025/12/5 | 作成者: @zzak

  1. 概要 (1-2文で)
    Active Storage のコントローラテストで、ActiveSupport::Notifications のイベント名文字列に対するマッチングをやめ、代わりに SQL 文の内容に対してパターンマッチするように変更した PRです。テストが内部実装(通知名の文字列形式)の変更に依存しないようにし、より堅牢にしています。

  1. 変更内容の詳細

対象ファイル:

  • activestorage/test/controllers/representations/redirect_controller_test.rb

このテストでは、ActiveStorage::Representation のリダイレクト処理中に発行される DB クエリ(代表的には SELECT など)が実行されるかどうかを ActiveSupport::Notifications 経由で検証している部分があります。

もともとの実装では、おそらく以下のように「通知イベント名の文字列」を対象にパターンマッチしていました(イメージ):

ruby
events = []
ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
  event = ActiveSupport::Notifications::Event.new(*args)
  events << event
end

# 「イベント名のメッセージ」などを文字列でマッチしていた例
assert events.any? { |event| event.name.include?("Representation Load") }

この方法だと、通知の「イベント名」やペイロード中の「メッセージ」の書式が変わると(例: "SQL""ActiveRecord SQL" のような微妙な変更)、実際の挙動に問題がなくてもテストだけが壊れてしまいます。

この PR では、イベント名やメッセージ文字列ではなく、ペイロード中の SQL 文自体(payload[:sql])に対してパターンマッチ(Ruby のパターンマッチング / case-in / 正規表現など)を行うようにテストを変更しています。例としては以下のようなイメージです:

ruby
ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
  event = ActiveSupport::Notifications::Event.new(*args)
  sql = event.payload[:sql]

  case sql
  in /FROM "active_storage_blobs"/
    # Active Storage の特定のクエリが発行されたことを確認
  end
end

今回の差分は +4/-2 行と少なく、主な変更は「文字列によるイベント名マッチ」→「SQL に対するパターンマッチ」への書き換えです。
元 PR #56225 のフォローアップとして、テストの堅牢性を高める目的で行われています。


  1. 影響範囲・注意点
  • 影響範囲

    • 対象は Active Storage の Representations::RedirectController に関するテストのみで、アプリケーション本体の挙動には影響しません。
    • CI 上のテスト安定性が向上し、「通知メッセージのフォーマットが変わっただけ」でテストが落ちるリスクが減ります。
  • 注意点

    • 今後、同様に ActiveSupport::Notifications をテストで利用する場合、イベント名そのものの文字列フォーマットへの依存は避け、より安定した情報(SQL 文、payload の構造化されたキー、バインド値など)で検証するのが推奨されます。
    • SQL に対するパターンマッチも、スキーマ変更(テーブル名やカラム名の変更)には依存する点は変わらないため、「何に依存するか」を意識してテストを書く必要があります。

  1. 参考情報 (あれば)
  • 元 PR: #56225(今回のフォローアップ元。通知に関するテストまわりの変更が含まれている可能性が高い)
  • 関連 API:
    • ActiveSupport::Notifications
    • ActiveSupport::Notifications::Event
  • パターンマッチング: Ruby 2.7 以降の case ... in 構文(今回の PR タイトルにある “pattern matching” は、これや類似の機構を指していると考えられます)。

#56285 fix(activesupport): handle syntax errors in debug views when using editor

マージ日: 2025/12/5 | 作成者: @markokajzer

  1. 概要 (1-2文で)
    Rails のデバッグビューで RAILS_EDITOR を使っている場合に、ERB テンプレート内の構文エラーが正しく処理されずエラーになる問題を修正した PR です。ActiveSupport::SyntaxErrorProxy が、エディタ連携用に必要な absolute_path を返せるようにしています。

  1. 変更内容の詳細

問題の背景

  • RAILS_EDITOR 環境変数を設定していると、例外発生時のデバッグ画面から「編集」ボタン等を通じて、対応するファイルを手元のエディタで開けるようになります。
  • その際 debug_view.rb では、バックトレースの各フレームから absolute_path を取得して、editor_urlRAILS_EDITOR に基づく URL / コマンド)を生成します。
    参考(PR 説明からのリンク先コード):
    ruby
    # debug_view.rb のイメージ(簡略)
    backtrace_locations.each do |location|
      path = location.absolute_path || location.path
      # path を使って editor_url を生成
    end
  • ところが、ERB テンプレートの構文エラーが起きた場合、それは ActiveSupport::SyntaxErrorProxy 経由でラップされ、その BacktraceLocation オブジェクトが absolute_path を実装しておらず、debug_view 側から呼び出したときにエラーになる、という不具合がありました(#55295 の変更の影響)。

修正内容

  • ActiveSupport::SyntaxErrorProxy::BacktraceLocation クラスに #absolute_path メソッドを追加。
  • absolute_path が debug view から呼ばれたときに、通常のバックトレースフレームと同様の情報を返せるようにしたことで、RAILS_EDITOR 使用時でも構文エラーの箇所に対応するファイルパスが取得可能になります。
  • これにより、構文エラーでも「エディタで開く」が動くようになり、かつ NoMethodError などの二次的な例外が発生しなくなります。

変更イメージ(概念的な擬似コード):

ruby
module ActiveSupport
  class SyntaxErrorProxy
    class BacktraceLocation
      # もともと `path` や `lineno` などがある

      def absolute_path
        # 実ファイルフレームに合わせた形で絶対パスを返す
        @absolute_path || path # 実装は実フレームの情報に基づく
      end
    end
  end
end

テスト追加

actionpack/test/dispatch/debug_exceptions_test.rb に 12 行のテストを追加し、以下を確認しています:

  • RAILS_EDITOR が設定されている状態で、ERB の構文エラーが発生しても例外ハンドラが落ちない。
  • debug view から editor の URL 生成が正常に行われる(= absolute_path が正常動作する)。

  1. 影響範囲・注意点
  • 影響範囲:
    • RAILS_EDITOR を設定していて、かつ ERB テンプレート内で構文エラーが起きるケースに限定されます。
    • 通常の例外(構文エラー以外)や RAILS_EDITOR を使っていない環境では挙動への影響はほぼありません。
  • 利用者側での対応:
    • Rails をこのコミットを含むバージョンに更新すると、RAILS_EDITOR 利用時の構文エラー画面が安定し、エディタ連携も期待通り動作します。
    • 既に RAILS_EDITOR を使っていて、構文エラーで debug view が落ちる/エディタリンクが出ないといった症状がある場合、この修正を取り込むことで解消されます。
  • 後方互換性:
    • 新たに public API を壊す変更はなく、BacktraceLocation#absolute_path の追加は、標準のバックトレース仕様に沿う形の拡張であり、後方互換的です。

  1. 参考情報 (あれば)
  • 関連 Issue: #56284
  • 直前の変更(原因となった変更): #55295
  • 該当コード(debug view 側で absolute_path を使っている箇所):
    actionpack/lib/action_dispatch/middleware/debug_view.rbeditor_url 生成周り

#56290 Allow schema_dump configuration to be an absolute path.

マージ日: 2025/12/5 | 作成者: @flavorjones

  1. 概要 (1-2文で)
    database.ymlschema_dump 設定に「絶対パス」を書けるようにし、エンジン固有のスキーマファイルをアプリ本体とは別の場所(例: gem の中)に置けるようにした PR です。既存の相対パスの挙動は維持したまま、絶対パスの場合はそのまま利用されるようになります。

  1. 変更内容の詳細

これまでの挙動

ActiveRecord::Tasks::DatabaseTasks.schema_dump_path は、以下のような扱いをしていました:

  • database.ymlschema_dump 設定値は「DatabaseTasks.db_dir を基準とした相対パス」とみなされる
  • 絶対パスを指定しても db_dir と結合されてしまい、意図通りに使えない
  • 環境変数 SCHEMA だけは絶対パス指定が可能

そのため、エンジン側で

yaml
schema_dump: /path/to/engine/db/saas_schema.rb

のように書いても、db/ ディレクトリ配下への相対パスとして扱われてしまい、アプリ本体とは別の場所にスキーマを置くことが難しい状態でした。

今回の変更

ActiveRecord::Tasks::DatabaseTasks.schema_dump_path の実装が次のように拡張されています:

  • schema_dump に設定されたパスを Pathname でラップ
  • path.absolute?true のときは、そのパスを変換せずにそのまま返す
  • 絶対パスでなければ、従来通り DatabaseTasks.db_dir を基準に解決

擬似コードで表すと:

ruby
def self.schema_dump_path(db_config, format = ActiveRecord.schema_format)
  path = db_config.schema_dump # などから取得した値

  if Pathname.new(path).absolute?
    path # <- そのまま使う
  else
    File.join(db_dir, path) # <- 従来の相対パス挙動
  end
end

※ 実際のコードはもう少し文脈がありますが、ポイントは「絶対パスなら素通しする」という一点です。

利用例

エンジン側の gem ディレクトリにスキーマファイルを置きたい場合、database.yml で次のように書けます:

yaml
<% gem_path = Gem::Specification.find_by_name("fizzy-saas").gem_dir %>

production:
  primary:
    # ...
  saas:
    database: saas_production
    host: <%= mysql_database_host %>
    username: <%= mysql_app_user %>
    password: <%= mysql_app_password %>

    # migrations_paths は既に絶対パス対応済み
    migrations_paths: <%= File.join(gem_path, "db", "migrate") %>

    # この PR により絶対パスで指定可能に
    schema_dump: <%= File.join(gem_path, "db", "saas_schema.rb") %>

こうしておくと、db:schema:dump 等を実行したときに、saas DB 用のスキーマはアプリ本体の db/ ではなく、fizzy-saas gem 側の db/saas_schema.rb に出力されます。

テスト・ドキュメント

  • activerecord/test/cases/tasks/database_tasks_test.rb に、絶対パスを指定した場合の挙動を確認するテストが追加されています。
  • activerecord/CHANGELOG.md に、この挙動変更(新機能)が記載されています。

  1. 影響範囲・注意点
  • 相対パス指定の既存挙動は変わらない
    • これまでどおり schema_dump: schema.rb などの設定は db/ ディレクトリ配下として扱われます。
  • 絶対パスを指定した場合だけ新挙動
    • schema_dump: /foo/bar/schema.rb のようなパスは、その場所に直接スキーマが出力されます。
  • 複数データベース+エンジン構成で有用
    • マルチ DB(primary, replica, saas など)かつエンジンを gem として分離している場合に、各エンジンごとにスキーマファイルを分けて管理しやすくなります。
  • パスの権限・存在には注意
    • 絶対パス先のディレクトリが存在しない、または書き込み権限がない場合は、当然ながら db:schema:dump 実行時にエラーになります。
    • CI や本番環境などでパスを変える場合は、database.yml の ERB で環境変数等を噛ませる想定になります。

  1. 参考情報 (あれば)

#56283 ActionText: Validate RemoteImage URLs

マージ日: 2025/12/5 | 作成者: @flavorjones

  1. 概要 (1-2文で)
    Action Text でリッチテキストに添付される「リモート画像」の URL を検証し、image.png のような相対パス風の値が誤ってアセットパイプラインに流れないようにした変更です。これにより 500 エラーや意図しないアセット解決/悪用の可能性を減らします。

  1. 変更内容の詳細

背景となる問題

Trix 経由などでリッチテキストに画像が埋め込まれた際、画像の src が以下のような値だったとします:

html
<img src="image.png">

ActionText::Attachables::RemoteImage はこの src を元に RemoteImage オブジェクトを作成し、レンダリング時に Propshaft(アセットパイプライン)側で解決を試みていました。その結果:

  • image.png がアセットに存在しない場合:
    ActionView::Template::Error: The asset 'image.png' was not found in the load path. が発生し、特別に rescue していないと 500 エラーになる。
  • 同名のアセットが存在する場合:
    本来意図しないローカルアセットに解決される可能性がある。
    (セキュリティ上も、想定外のアセットを引き当てられる「踏み台」になりうる)

具体的な修正内容

RemoteImage.from_node の中で、src に対して「リモート URL として妥当か」を検証するロジックが追加されました。

  • 検証には、レンダリング時に AssetUrlHelper が用いているものと同じ正規表現 を使用します。
  • これにより、HTTP(S) などの「正しいリモート URL」だけが RemoteImage として扱われるようになります。
  • 一方で、次のような値は「リモート画像」としては不正とみなされます:
    • image.png
    • /images/foo.png
    • など、アセットパイプラインに解決されうる「相対パス/ルート相対パス」

そのような「不正 URL」だった場合:

  • 以前は RemoteImage を生成 → アセット解決を試行 → 例外 or 誤ったアセット解決、という流れになっていた。
  • 変更後は RemoteImage を生成せず、Action Text 内の通常のフォールバック処理(MissingAttachable)に委ねられます。

テストの追加

actiontext/test/unit/content_test.rb にユニットテストが追加され、以下が確認されています:

  • 正しいリモート URL(例: https://example.com/image.png)は RemoteImage として扱われ、これまで通り表示される。
  • 不正な URL(例: image.png)は RemoteImage としては扱われず、MissingAttachable ルートに落ちる。

actiontext/CHANGELOG.md にも、この挙動変更が記載されました。


  1. 影響範囲・注意点

影響範囲

  • 対象は Action Text で「リモート画像」を扱う部分のみ です。
  • 具体的には、Trix エディタなどから埋め込まれた <img src="..."> に対し、
    • src が完全なリモート URL(例: https://...)なら: 従来通り RemoteImage として扱われる。
    • そうでない場合(相対パス等): RemoteImage ではなくなり、MissingAttachable として処理される。

既存アプリへの影響・移行上の注意

  1. 相対パスを「リモート画像」として使っていた場合

    • これまではたまたま動いていた(あるいは 500 を rescue していた)ケースが、
      今後は MissingAttachable としてレンダリングされ、画像が表示されなくなる可能性があります。
    • 対応策:
      • リッチテキストに埋め込む画像 URL は、https://example.com/foo.png などの完全な URL に修正する。
      • あるいは、画像は Active Storage 経由で添付する(<action-text-attachment>)運用に寄せる。
  2. 500 エラー対策として Template::Error を rescue していた場合

    • この PR により、「不正な相対 URL がアセット解決で例外を出す」というパスは通らなくなるので、 そのためだけの rescue は不要になる可能性があります。
    • ただし他の理由で同じ例外を rescue している場合は、その用途と切り分けてください。
  3. セキュリティ上の意味合い

    • 任意の src="foo.png" が、アセットパイプライン経由で別のローカルアセットに解決される可能性が減ります。
    • 特に「ユーザ入力の HTML を Action Text に取り込み、レンダリングしている」ようなアプリでは、 意図しないアセットへのアクセスや情報露出のリスクが下がります。

  1. 参考情報 (あれば)
  • 該当 PR: https://github.com/rails/rails/pull/56283
  • 関連する内部 API:
    • ActionText::Attachables::RemoteImage.from_node
    • ActionText::Content
    • ActionView::Helpers::AssetUrlHelper(同一の URL 検証正規表現を使用)
  • 実運用上の推奨:
    • リモート画像は https://... の完全 URL で扱う。
    • アプリ内画像は、できるだけ Active Storage の添付機能を経由させる。

#56293 Combine rather than overwrite tag content supplied via both parameter and block

マージ日: 2025/12/5 | 作成者: @dhh

  1. 概要 (1-2文で)
    Rails の tag.* ヘルパで、引数とブロックの両方でコンテンツを渡した場合に「後勝ちで上書き」されていた挙動が、「結合して連結」される挙動に変更されました。これにより、tag.div("Hello ") { "World" }<div>Hello World</div> を返すようになります。

  1. 変更内容の詳細

これまでの挙動

ActionView::Helpers::TagHelpertag ヘルパ(例: tag.div)において、以下のように文字列引数とブロックを同時に渡した場合:

ruby
tag.div("Hello ") { "World" }

従来はブロック側が優先され、引数の "Hello " は無視されていました:

html
<div>World</div>

これは「サイレントに引数コンテンツを捨てる」挙動でした。

変更後の挙動

この PR により、引数で渡したコンテンツとブロックで返したコンテンツを結合して出力するようになりました。

ruby
tag.div("Hello ") { "World" }
# => <div>Hello World</div>

内部的には、おそらく以下のようなイメージで実装が変わっています:

  • 以前: content = block_given? ? capture(&block) : content_or_options_with_block
  • 以後: 引数側とブロック側のコンテンツが両方存在する場合は、両方を連結(+ など)して最終コンテンツとする

実際のコード変更は action_view/helpers/tag_helper.rb の1行程度のロジック修正にとどまり、テスト (tag_helper_test.rb) と CHANGELOG の追記が行われています。

サンプルコード

ruby
# 以前(〜このPR前)
tag.span("A") { "B" }   # => <span>B</span>

# これから(このPR後)
tag.span("A") { "B" }   # => <span>AB</span>

# 空文字や nil が絡んだ場合(イメージ)
tag.p(nil) { "Body" }   # => <p>Body</p>
tag.p("Intro ") { nil } # => <p>Intro </p>

  1. 影響範囲・注意点
  • 影響範囲

    • ActionView::Helpers::TagHelpertag API を使い、同じタグ呼び出しで「引数のコンテンツ」と「ブロック」を併用している箇所すべてに影響します。
    • content_tag ではなく tag.div, tag.span などの「新しい tag API」を使っているコードが対象です。
  • 後方互換性の観点

    • 従来はブロック側が常に優先されていたため、「引数は無視されるもの」と期待していたコードでは出力が変わります。
      • 例: 「引数側にダミー値」や「古いAPIとの互換用値」を入れていた場合など。
    • 多くのケースでは、明示的に引数とブロックの両方を渡していれば「両方とも表示したい」意図であることが多いため、挙動としては自然になった一方で、HTML 出力の差分が生じる可能性はあります。
  • テンプレートレビューのポイント

    • tag.xxx("something") do ... end のような呼び方をしているビューテンプレートを grep 等でチェックし、
      • その両方のコンテンツが出力されることが想定どおりか
      • 余計な空白や改行が増えないか("Hello " の末尾スペースなど) を確認すると安全です。
  • パフォーマンス

    • 追加の連結処理は非常に軽量であり、実質的なパフォーマンスへの影響はほぼ無視できるレベルと考えられます。

  1. 参考情報 (あれば)
  • 対象ファイル:
    • actionview/lib/action_view/helpers/tag_helper.rb
    • actionview/test/template/tag_helper_test.rb
    • actionview/CHANGELOG.md
  • 変更規模:
    • 3ファイル変更
    • 追加 14 行 / 削除 1 行
  • 関連ヘルパ:
    • tag.div, tag.span, tag.p など tag オブジェクト経由のタグ生成系ヘルパ全般に適用されます。

#56294 Fix MemCacheStore for connection_pool >= 3

マージ日: 2025/12/5 | 作成者: @fatkodima

  1. 概要 (1-2文で)
    ActiveSupport::Cache::MemCacheStoreconnection_pool gem の v3 以降と正しく連携できるように、初期化ロジックを1行だけ修正したPRです。直前の PR #56292 での connection_pool 対応から mem_cache_store だけ漏れていた部分をフォローしています。

  1. 変更内容の詳細

※ 実際の差分は 1 行の置き換えのみです。PR タイトルと文脈からすると、以下のような意図の修正です(擬似コードで説明します):

ruby
# 変更前(connection_pool 2 系まで前提の書き方)
@pool = ConnectionPool.new(size: options[:pool_size], timeout: options[:pool_timeout]) do
  build_client(...)
end

# 変更後(connection_pool 3 系に合わせた書き方)
@pool = ConnectionPool::Wrapper.new(size: options[:pool_size], timeout: options[:pool_timeout]) do
  build_client(...)
end

もしくは、#56292 で RedisCacheStore などに行ったのと同種の変更(例: 引数の取り回しや、pool のラッパークラスの指定方法の統一)を、MemCacheStore にも同様に適用しただけです。

要するに、MemCacheStore 内で connection_pool を使う部分の API 呼び出しを、connection_pool v3 に対応したものに揃える変更です。


  1. 影響範囲・注意点
  • 影響対象:
    • config.cache_store = :mem_cache_store などで MemCacheStore を使用しつつ、connection_pool gem v3 以上を利用しているアプリケーション。
  • 期待される改善:
    • connection_pool v3 系で MemCacheStore を使ったときに発生していた例外(例えば「uninitialized constant ConnectionPool::Wrapper」や、逆に Wrapper 必須なのに使っていないことによるエラーなど)が解消される。
    • RedisCacheStore など他のキャッシュストアと同様に、connection_pool v3+ と一貫した挙動をとる。
  • 互換性:
    • 変更は 1 行のみで、connection_pool の公式な v3 向け移行パスに沿った修正のため、通常の利用において後方互換性リスクは小さいと考えられます。
    • もしアプリ側で MemCacheStore の内部実装(@pool のクラスや API)に依存したメタプログラミングをしている場合は、@pool の型変更などにより影響する可能性があります(そのような利用は通常は非推奨です)。

  1. 参考情報 (あれば)

#56280 Use Digest::UUID.uuid_v5 to generate uuid instead of hand-rolled

マージ日: 2025/12/4 | 作成者: @zzak

  1. 概要 (1-2文で)
    Rails 内部で UUID を生成していた箇所を、自前実装ではなく Ruby 標準ライブラリ Digest::UUID.uuid_v5 を使うように置き換えた PR です。これにより UUID 生成ロジックが標準化され、保守性と一貫性が向上します。

  1. 変更内容の詳細
  • 対象ファイル:
    railties/lib/rails/devtools_controller.rb

  • 主な変更点:
    これまで Rails::DevtoolsController(開発用ツールのコントローラ)内で、名前空間付き UUID(おそらく v5 相当)の生成を独自実装していた部分が、Ruby 3.4 で追加された(もしくは近日追加予定の)Digest::UUID.uuid_v5 を呼び出す形にリファクタリングされました。

    変更のイメージは以下のようなものです(擬似コード):

    ruby
    # 変更前(例: 独自で UUIDv5 っぽいものを生成していた)
    uuid = Digest::UUID.new(namespace: SOME_NAMESPACE, name: some_string).generate
    
    # 変更後(Digest::UUID.uuid_v5 を使用)
    uuid = Digest::UUID.uuid_v5(SOME_NAMESPACE, some_string)

    実際には数行の修正で、3行追加・2行削除という軽微な変更です。
    PR 説明にある "Follow up to #56245" から、#56245 で Digest::UUID を導入したか、それに関連する土台を整え、その後続として実際の利用箇所を置き換えたものと考えられます。

  • なぜ uuid_v5 なのか
    v5 UUID は「名前空間+名前(文字列など)」から決定論的に同じ UUID を生成する方式です。

    • 同じ namespace + name なら同じ UUID
    • 異なる namespace もしくは name なら別の UUID
      開発ツール用コントローラで、同じキーから安定した UUID を作りたい用途に適しています。

  1. 影響範囲・注意点
  • 影響範囲

    • Rails::DevtoolsController が利用している UUID 生成ロジックのみが影響を受けます。
    • 本番アプリケーションに直接影響するようなコア機能ではなく、開発支援用ツールに限定された変更です。
    • UUID の計算方法が「完全に同一」でない場合、同じ入力から得られる UUID が従来と変わる可能性があります。ただし devtools 用なので、互換性リスクは小さいと考えられます。
  • 注意点

    • Digest::UUID / Digest::UUID.uuid_v5 が利用できる Ruby バージョンが前提になります。
      Rails をこのコミット以降のバージョンで使う場合、対応する Ruby バージョン(Digest::UUID を含むもの)が必要になります。
    • もし外部ツールやスクリプトが「devtools が返す UUID の値そのもの」に依存していると、生成値の違いにより影響が出る可能性がありますが、一般的な Rails アプリではほぼ無視できるレベルです。

  1. 参考情報 (あれば)
  • 該当 PR:
    • #56280: Use Digest::UUID.uuid_v5 to generate uuid instead of hand-rolled
    • #56245: この PR の前提となる変更(Digest::UUID の導入や関連リファクタリング)
  • Ruby 側の機能:
    • Digest::UUID モジュール(Ruby コア / 標準ライブラリに追加されつつある UUID サポート)
    • UUIDv5 仕様: RFC 4122 “Name-Based UUIDs”(SHA-1 ベース、namespace + name から決定論的に生成)

#56292 Fix RedisCacheStore for connection_pool >= 3

マージ日: 2025/12/4 | 作成者: @fatkodima

  1. 概要 (1-2文で)
    RedisCacheStoreconnection_pool gem のバージョン 3 以上で動かなくなる問題 (#56291) を修正した PR です。RedisCacheStoreconnection_pool のインターフェイスの差異を吸収するための、ごく小さな互換性修正が 1 行だけ入っています。

  1. 変更内容の詳細

※PR本文の実コード断片は提示されていないため、ここでは Rails と connection_pool 3 系の変更点から「どのような修正か」を技術的に推測・整理します。実際の 1 行差分は、この方向性のいずれかです。

背景: RedisCacheStoreconnection_pool の関係

ActiveSupport::Cache::RedisCacheStore は内部で connection_pool を使って Redis クライアントのプーリングを行います。典型的な利用イメージは以下のようなものです。

ruby
cache = ActiveSupport::Cache::RedisCacheStore.new(
  url: ENV["REDIS_URL"],
  pool: ConnectionPool.new(size: 5, timeout: 1) { Redis.new }
)

Rails 側のコードでは、pool.with { |conn| ... } のような形で各種 Redis 操作を実行します。

問題の発生点: connection_pool >= 3 での非互換

connection_pool 3 系では、以下のような変更が入っており、2 系までを前提としたコードが壊れることがあります。

  • メソッド名や引数の扱いの変更
  • ConnectionPool::Wrapper の使い方や返り値の挙動変更
  • with ブロックの呼び出しパターンの厳格化 など

そのため、RedisCacheStore 内部で

  • pool.with の呼び出し方
  • pool.checkout / pool.checkin 相当の処理
  • pool.method(...) の転送

などが旧インターフェイスを前提に書かれていると、connection_pool 3 にアップデートしたタイミングで NoMethodErrorArgumentError が発生します。

この PR の実質的な修正内容

差分が「+1/-1 の 1 行のみ」であることから、以下のようなピンポイント修正の可能性が高いです。

一例:

ruby
# 変更前(connection_pool 2 系までは動くが、3 系で壊れる書き方)
@pool.with do |conn|
  # Redis 操作
end

# 変更後(3 系の挙動に合わせた呼び出し方)
@pool.with do |redis|
  # Redis 操作
end

または、キーワード引数・ブロック引数の扱いが変わったことに伴う修正:

ruby
# 例1: ブロックあり/なしのパターンに対応
@pool.with { |client| client.public_send(command, *args, **kwargs) }

# 例2: `then` のようなメソッドチェーンとの組み合わせを修正
@pool.with { |client| yield client } # と明示して新しいシグネチャに合わせる

いずれにせよ、

  • connection_pool 2 系でも動く
  • かつ 3 系でもエラーにならない

という互換的な呼び出し方に 1 行書き換えた修正と考えられます。


  1. 影響範囲・注意点

影響範囲

  • ActiveSupport::Cache::RedisCacheStore を利用しており、
  • かつアプリケーションが connection_pool 3 以上を採用している場合

に、キャッシュアクセス時の例外発生や挙動不良が解消されます。

Rails 自身が内部で利用する Redis ベースのキャッシュ(config.cache_store = :redis_cache_store など)にも関係するため、Redis キャッシュ利用アプリ全般に影響しうる変更です。

注意点

  • 既に connection_pool 2 系を使っていて問題なく動いているアプリに対しては、今回の修正による後方互換性ブレイクはほぼ考えにくく、安全なマイナー修正とみなせます。
  • もし connection_pool 3 系にアップグレードした際に
    • Redis のキャッシュ操作で NoMethodError, ArgumentError, または undefined method 'with' / wrong number of arguments などが出ていた場合は、この PR を含む Rails バージョンへの更新で解消される可能性が高いです。
  • Rails 本体よりも先に connection_pool だけを major upgrade していた場合、Rails をこの PR を含むバージョンまで引き上げることが推奨されます。

  1. 参考情報 (あれば)
  • 該当 Issue: #56291 — RedisCacheStoreconnection_pool 3 系で壊れる不具合報告
  • PR: #56292 “Fix RedisCacheStore for connection_pool >= 3”
  • 関連しうる外部リソース:
    • connection_pool GitHub リポジトリの 3.0 リリースノート / Changelog
    • Rails ガイド: Caching with Redis(最新版を参照)

#56286 [ci skip][docs] Fix RDoc markup for Time.zone.today in Date.current

マージ日: 2025/12/4 | 作成者: @Yuhi-Sato

  1. 概要 (1-2文で)
    Date.current の RDoc 内で、コード参照 Time.zone.today だけが <tt> でマークアップされていなかった問題を修正し、他のコード参照と統一したドキュメント整備のPRです。コードの挙動やAPI自体は一切変わっておらず、見た目と可読性の改善のみです。

  1. 変更内容の詳細 (サンプルコード含む)

対象ファイル:

  • activesupport/lib/active_support/core_ext/date/calculations.rb

Date.current のドキュメントコメント(RDoc)の中にある、以下のような説明文が元々ありました:

rdoc
# Returns Time.zone.today when config.time_zone is set, otherwise just returns
# Date.today.

この文中で、

  • Time.zone
  • config.time_zone
  • Date.today

<tt>...</tt> でコードとしてマークアップされていた一方で、Time.zone.today だけがプレーンテキストになっていました。

PRでは、この不整合を解消するために Time.zone.today<tt> で囲むように1行を修正しています:

diff
- # Returns Time.zone.today when <tt>config.time_zone</tt> is set, otherwise just returns
- # <tt>Date.today</tt>.
+ # Returns <tt>Time.zone.today</tt> when <tt>config.time_zone</tt> is set, otherwise just returns
+ # <tt>Date.today</tt>.

※ 上記はイメージであり、実際の前後の文脈や位置はファイル内の Date.current のRDocコメント部分になります。

これにより、RDoc生成時に Time.zone.today が他と同様に等幅フォント・コードとして表示されるようになります。


  1. 影響範囲・注意点
  • 影響範囲

    • Railsアプリケーションの挙動、API仕様、型や返り値などには一切変更なし。
    • 影響があるのは 生成されるドキュメント(RDoc) の表示のみ。
    • RDoc / edgeguides / 各種自動生成ドキュメントで、Date.current の説明におけるコード表記が統一されます。
  • 注意点

    • 既存コードの修正やテストの変更はなく、バージョンアップに伴う互換性問題はありません。
    • CIを回す必要のないドキュメントのみ変更のため、タイトルに [ci skip][docs] が付いています。

  1. 参考情報 (あれば)
  • 対象メソッド: ActiveSupport::CoreExtensions::Date::Calculations#current(一般に Date.current として使用)
  • Date.current の挙動(改めて確認用):
    • config.time_zone が設定されている場合: Time.zone.today を返す
    • 未設定の場合: Date.today を返す
      今回のPRはこの挙動の説明テキストの見た目を整えただけであり、挙動そのものは従来どおりです。

#56289 Fix typos in comments

マージ日: 2025/12/4 | 作成者: @yujiteshima

  1. 概要 (1-2文で)
    このPRは、Rails本体のコメント中の英単語のスペルミスを修正しただけの変更です。コードの挙動や公開APIには一切影響しません。

  2. 変更内容の詳細

  • 修正内容

    • Action Controller のコメント:
      • ExplictlyExplicitly
    • Action Cable (PostgreSQL サブスクリプションアダプタ) のコメント:
      • purposedlypurposely
  • 対象ファイル

    • actionpack/lib/action_controller/metal/redirecting.rb
    • actioncable/lib/action_cable/subscription_adapter/postgresql.rb

どちらも「コメントのみ」の変更であり、実行パスに関わるコードは一切変更されていません。
codespell というスペルチェッカーツールで検出された誤字を修正した、ドキュメント改善系のPRです。

(疑似的なイメージ例)

ruby
# Before
# Explictly redirect to ...

# After
# Explicitly redirect to ...
ruby
# Before
# This is purposedly left ...

# After
# This is purposely left ...
  1. 影響範囲・注意点
  • 影響範囲

    • 実行時の挙動、パフォーマンス、セキュリティ、API 仕様に影響はありません。
    • 変更箇所はコメントのみのため、バイナリ・コンパイル成果物にも実質的な差分は出ません。
    • ドキュメントとしての読みやすさ・正確さがわずかに向上しています。
  • 注意点

    • スペル修正に伴うコメント内テキストの変更のみのため、既存コードやアプリケーション側での対応は不要です。
    • コメントを参照している外部ツール(ドキュメント生成等)を使っている場合も、意味は変わらず、破壊的な変更にはなりません。
  1. 参考情報 (あれば)
  • PR: https://github.com/rails/rails/pull/56289
  • 使用ツール: codespell(OSSプロジェクトでよく使われる英単語スペルチェックツール)
  • Rails の機能的な変更ではなく、品質改善(コメントの整備)カテゴリの変更として扱って問題ありません。

#56288 Add SecureRandom.base32

マージ日: 2025/12/4 | 作成者: @monorkin

  1. 概要 (1-2文で)
    Rails の SecureRandom に、人間が読みやすく誤読しづらい Crockford 方式の Base32 文字列を生成する SecureRandom.base32 が追加されました。これにより、2FA コードやマジックリンク用トークンなどを、安全かつユーザーフレンドリーな形式で簡単に生成できます。

  1. 変更内容の詳細

追加された API

ActiveSupport の SecureRandom 拡張に、新しく SecureRandom.base32 が追加されています。
既存の hex, base64, urlsafe_base64, base58 と同じパターンで実装されており、暗号学的に安全な乱数ソースを使いつつ、文字集合を Crockford Base32 にしたものです。

典型的な使い方は以下のようなイメージです(PR 本文から推測される使用感):

ruby
# 例: 16 文字程度の人間向けコードを生成(実際の引数仕様は他のメソッドに準拠する想定)
SecureRandom.base32          # デフォルト長
SecureRandom.base32(10)      # 桁数(もしくはバイト数)を指定

※ 実際の引数の意味(「バイト数指定」か「文字数指定」か)は SecureRandom.base58 等と同じインターフェイスに揃えられているはずなので、Rails/ActiveSupport のドキュメントか、core_ext/securerandom.rb の他メソッドと同様に扱えばよいです。

文字集合 (Crockford's Base32)

SecureRandom::BASE32_ALPHABET が定義され、Crockford 方式の Base32 アルファベットが使われます。Crockford Base32 の特徴:

  • 大文字・小文字非依存
  • 人間が混同しやすい文字を避ける / マッピングする設計
    • 例: 0 と O, 1 と I / L など
  • 読み上げや手入力に向いた設計

Git 上では:

  • activesupport/lib/active_support/core_ext/securerandom.rb
    • BASE32_ALPHABET を追加
    • 既存の Base 系メソッドと同じテンプレートで base32 を追加
  • activesupport/test/core_ext/secure_random_test.rb
    • 新しい base32 メソッド用のテストを追加(長さ・文字集合・ランダム性などを検証)
  • activesupport/CHANGELOG.md
    • ActiveSupport に SecureRandom.base32 を追加した旨を記載

これにより、アプリケーション側で毎回「安全な乱数 + 人間向け文字集合」の実装を自前で用意する必要がなくなります。


  1. 影響範囲・注意点
  • 影響範囲

    • 新規メソッド追加のみで、既存 API の挙動変更や削除はありません。
    • ActiveSupport をロードしている Rails アプリ/ライブラリで SecureRandom.base32 が利用可能になります。
    • 2FA コード、招待コード、マジックリンク用トークンなど「人が読む・打つ前提のコード」を生成するユースケースで特に有用です。
  • 注意点

    • 「Base32」とだけ書かれているものには複数のバリアント(RFC 4648, Crockford など)があるため、この base32Crockford Base32 固定である点に注意してください。
      既存の RFC 4648 準拠の Base32 実装とは互換ではありません。
    • Rails 側では base58 が Bitcoin 形式を特に名前に含めずに採用している慣例にならって、base32 という汎用的な名前で Crockford バリアントを提供しています。
    • 既に独自の Base32 実装(特に Crockford Base32)を持っている場合は、徐々に SecureRandom.base32 に置き換えることで、暗号学的安全性と実装のシンプルさを統一できる可能性があります。
    • 「長さの指定方法」(引数の意味)は SecureRandom.base58 等と合わせているはずなので、置き換え時には base58 との対応関係を確認しておくと安全です。

  1. 参考情報 (あれば)

#56287 Fix ActiveRecord::SoleRecordExceeded#record to return the relation

マージ日: 2025/12/4 | 作成者: @byroot

  1. 概要 (1-2文で)
    ActiveRecord::SoleRecordExceeded 例外オブジェクトの #record メソッドが、本来返すべき「元の Relation」ではなく誤った値を返していたリグレッションを修正し、再び Relation を返すようにした PR です。sole / sole! 利用時に、例外から安全にクエリ条件を再利用できる挙動が復元されています。

  1. 変更内容の詳細

背景

  • ActiveRecord::Relation#sole / #sole! は「レコードがちょうど1件だけ存在する」ことを期待する finder です。
    • 0件: ActiveRecord::RecordNotFoundsole!) / nilsole
    • 1件: そのレコードを返す
    • 2件以上: ActiveRecord::SoleRecordExceeded を発生させる
  • ActiveRecord::SoleRecordExceeded には #record メソッドがあり、「どの Relation に対して sole を呼び出したか」を参照できるようになっていました。
  • しかし、過去の PR(#50396)の影響で、この #record が Relation ではない別の値を返すように壊れてしまっていた、というのが今回の Issue(#56281)の内容です。

今回の修正内容

finder_methods.rb の修正

activerecord/lib/active_record/relation/finder_methods.rb に1行の修正が入り、SoleRecordExceeded 例外にセットされる「record」が再び Relation になるように戻されています。

概念的には、以下のようなイメージです(実際のコードは Rails 内部実装ですが、挙動の意味として):

ruby
# 修正後のイメージ
relation = User.where(active: true)

begin
  relation.sole
rescue ActiveRecord::SoleRecordExceeded => e
  e.record # => `relation` と同じ User::ActiveRecord_Relation が返る
end

以前のリグレッションにより、e.record が Relation ではなく、他のオブジェクト(例: 実際に取得された最初のレコードなど)を指してしまうケースがありましたが、それが元通り Relation を返すようになりました。

テストの追加

activerecord/test/cases/finder_test.rb に10行のテストが追加されています。

趣旨としては:

  • relation = topics.where(条件...)
  • relation.soleActiveRecord::SoleRecordExceeded を発生させる
  • rescue ブロック内で error = e; end
  • assert_equal relation, error.record

という形で、SoleRecordExceeded#record が元の Relation をそのまま返すことを明示的に保証するテストです。

これにより、今後の変更で同じリグレッションが再発しないように保護されています。


  1. 影響範囲・注意点

影響範囲

  • 対象: ActiveRecord::Relation#sole / #sole! を使っていて、「複数レコードヒット時に発生する ActiveRecord::SoleRecordExceeded 例外の #record を利用しているコード」。
    • 例外の record からクエリ条件を再利用するような実装:
      ruby
      begin
        user = User.where(email: email).sole
      rescue ActiveRecord::SoleRecordExceeded => e
        # e.record を基にログ出し・デバッグ・再クエリなど
        Rails.logger.error("Multiple users found: #{e.record.to_sql}")
      end

期待できる挙動

  • この PR 適用後:
    • SoleRecordExceeded#record は必ず Relation を返すことが保証される。
    • 以前のバージョン(リグレッション前)と互換のある挙動に戻る。
  • リグレッション期間中に e.record を「壊れた挙動」(Relation 以外)を前提に利用するコードを書いていた場合は、逆に今回の修正で挙動が変わりますが、これは本来の仕様に戻す修正(Bugfix)です。

注意点

  • 例外から取得できるのは「複数ヒットしたうちの1件」ではなく、「検索に使われた Relation(クエリ条件)」です。
    e.record をレコードインスタンスとして扱うと誤りなので、where / to_sql / pluck など Relation API 前提で扱う必要があります。
  • ランタイムの型チェックやドキュメントを参照せずに e.record を直接モデルインスタンスとして扱っていた場合は、この修正で NoMethodError 等が顕在化する可能性があります(もともと仕様違反な使い方です)。

  1. 参考情報 (あれば)
  • Issue: https://github.com/rails/rails/issues/56281
    SoleRecordExceeded#record が Relation を返さなくなったことが報告されている Issue。
  • リグレッションの原因となった PR: https://github.com/rails/rails/pull/50396
  • sole / sole! のドキュメント:
    Rails Guides または API ドキュメントの「ActiveRecord::FinderMethods#sole」を参照すると、SoleRecordExceededrecord の意味付けが確認できます。

#56159 Restore missing Postgres type decoding

マージ日: 2025/12/4 | 作成者: @matthewd

  1. 概要 (1-2文で)
    Rails の PostgreSQL アダプタで、moneybytea 型の値が「公開されているクエリ実行 API (select_value / exec_query など)」経由でも正しくデコードされるように復元した PRです。既存アプリへの影響を最小化するための設定フラグと、PG::Connection.unescape_bytea の安全策(ダブルデコード防止)も併せて導入されています。

  1. 変更内容の詳細

背景

  • もともと内部用メソッド query では money / bytea を Ruby の値にデコードする処理が存在していた。
  • しかし Rails 4.0 の変更(コミット a92af3fbf0)で、select_value / select_all / exec_query など、公開クエリ API からこの型変換が抜け落ちていた。
  • モデル経由 (Model.find, attribute 経由) の取得では attribute typecasting が補っていたため、主に以下のケースでのみ問題になっていた:
    • select_value, select_all, exec_query などを直接使う場合
    • pluck + 生 SQL 式(モデル属性に紐付かない式)で money/bytea を扱う場合

この PR はその「抜け落ちた型デコード」を復元し、かつ既存コードを壊さないための互換策を入れています。


PostgreSQL アダプタでの型デコードの復元

主なポイント:

  • ActiveRecord::ConnectionAdapters::PostgreSQLAdapter に、公開 API での型デコードを有効にするための実装が追加。
  • money / bytea を含む結果に対して、PostgreSQL OID(型情報)をもとに適切な OID クラス (PostgreSQL::OID::Money / PostgreSQL::OID::Bytea 等) でデコードする処理が、exec_query 系パスに統合された。
  • これにより、以下のようなコードで「Ruby らしい値」が返ってくるようになる:
ruby
# 例: money
value = ActiveRecord::Base.connection.select_value("SELECT '12.34'::money")
# 以前: ロケール依存の文字列などがそのまま返るケースがあった
# 今回: 型情報に基づいて numeric / decimal などにデコードされる

# 例: bytea
data = ActiveRecord::Base.connection.select_value("SELECT '\\xDEADBEEF'::bytea")
# 以前: エスケープ表現の文字列が返る場合があった
# 今回: Ruby のバイナリ文字列(すでに unescape 済み)が返る

※実際の戻り値の型は Rails / PG ドライバ / 設定に依存しますが、「PostgreSQL ネイティブ表現 → Ruby 側で扱いやすい形へ」の変換が公開 API でも行われるようになった、というのが要点です。


PG::Connection.unescape_bytea のモンキーパッチ

この変更により問題となり得るパターン:

ruby
# これまで: select_value がエスケープされた bytea 文字列を返していた
raw = ActiveRecord::Base.connection.select_value("SELECT bytea_col FROM ...")
decoded = PG::Connection.unescape_bytea(raw)  # 正しくデコードされていた

# 変更後: select_value が「すでにデコード済みのバイナリ」を返し始める
# 同じコードだと「二重 unescape」が起きて壊れる可能性がある

そのため、この PR では PG::Connection.unescape_bytea をラップし、以下のような安全策を導入しています:

  • Rails の設定で「select 系 API で bytea をデコードする」機能を有効にした場合でも、
  • 引数が「既にデコード済みの値」(Rails 内部が付けた印などで判別)であれば、再度 unescape しない
  • つまり、既存コード unescape_bytea(select_value(...)) は「余計なことをしている」状態にはなるものの、動作が壊れないように吸収する実装になっている。

実装レベルでは PostgreSQL::OID::Bytea にマーカー情報を持たせる形で、「Rails が decode した bytea かどうか」を検知しているテストが追加されています。


設定オプション / 初期化フロー

railties 側にも変更が入っており、Rails アプリの設定からこの挙動を制御できるようになっています。

  • Rails::Application::Configuration に Postgres 型デコード関連の新しいフラグが追加。
  • config/initializers/new_framework_defaults_8_2.rb のテンプレートに、Rails 8.2 での新しいデフォルト挙動としてこの機能を有効化する設定が追加。
  • guides/source/configuring.md にも、設定方法や挙動の違いについてのドキュメントが追記。

典型的には、以下のような設定が想定されます(名前は概念的な例・実際のキー名は PR で追加されたものに依存します):

ruby
# config/application.rb など
config.active_record.postgresql_enable_type_decoding = true

新規アプリでは new_framework_defaults_8_2.rb でこのフラグがオンになる方向、既存アプリでは互換のためオフ、といった「いつもの Rails の移行パターン」に従う設計です。


テストの追加

以下のようなテストが追加・拡充されています:

  • activerecord/test/cases/adapters/postgresql/*_with_decoder_test.rb
    • money / bytea について、「デコード機能を有効にした際に期待通りの Ruby 値が返るか」を検証。
  • postgresql_adapter_test.rb
    • 公開 API(select_value, select_all, exec_query 等)での型デコードの有無、設定フラグによる挙動の違いを網羅的にテスト。
  • railties/test/application/configuration_test.rb
    • 設定フラグのデフォルト値、new_framework_defaults による切り替えを検証。

  1. 影響範囲・注意点

影響があるコードパターン

  1. AR モデルを経由しない生クエリ実行money / bytea を扱っている場合:

    • select_value / select_all / exec_query などで money / bytea カラム(またはそれらを返す式)を直接取得しているコードは、戻り値の型・内容が「より正しくデコードされた値」に変わる可能性があります。
    • 特に bytea は、文字列エスケープを前提にした独自処理を書いていると、二重デコードなどの不整合が起きかねないので注意が必要です。
  2. PG::Connection.unescape_bytea を明示的に呼んでいるコード:

    • 典型的なパターン: PG::Connection.unescape_bytea(ActiveRecord::Base.connection.select_value(...))
    • 新挙動有効化後も壊れないよう PR 側で防御策を入れていますが、
      将来的にはこのような「手動 unescape」は不要になるため、
      デコード済み値を前提にコードを整理していく方が安全です。
  3. pluck で生 SQL を使っている箇所:

    • 例: Model.pluck("price_in_cents::money"), Model.pluck("encode(bytea_col, 'hex')") など
    • これまでは attribute typecasting が効かず「生 Postgres 表現に近い値」が返っていたものが、
      型デコードにより Ruby 側の型に寄せて返る可能性があります。
    • 期待する値のフォーマットに依存したロジックがある場合には確認が必要です。

互換性と移行のポイント

  • Rails 8.2 系で導入される新デフォルトとしては「型デコード有効」が目指されているが、
    既存アプリ向けには new_framework_defaults_8_2.rb でフラグを明示的にオンにするステップを踏む形で、段階的な移行ができる。
  • すぐに新挙動を使いたくない場合:
    • config/application.rb または new_framework_defaults_8_2.rb でフラグをオフにしておけば、
      これまでと同じ「ほぼデコードなし」の挙動を維持できる。
  • 新挙動を有効にする際の推奨手順:
    1. テスト環境でフラグをオンにして CI を回す。
    2. ログ検索・grep などで PG::Connection.unescape_bytea の利用箇所を洗い出す。
    3. select_value / exec_query を使って money / bytea を取得している箇所の戻り値を確認し、
      文字列前提のパース処理などがないかチェックする。
    4. 問題がなければ本番でもフラグをオンにする。

  1. 参考情報 (あれば)

#56267 Move async execution into QueryIntent

マージ日: 2025/12/4 | 作成者: @matthewd

  1. 概要 (1–2文で)
  • Active Record の非同期実行まわりの内部構造が変更され、実際のクエリ実行ロジックを FutureResult から QueryIntent に移し、コネクションプールの非同期キューには QueryIntent を積むようになりました。
  • これにより、FutureResult はあくまでユーザー向けの結果ハンドラに近い役割になり、非同期実行の責務がより明確に分離されています。

  1. 変更内容の詳細

全体像の変化

Before

  • 非同期ロード(load_async など)を行うと、コネクションプールの async キューには FutureResult が積まれ、その中でクエリ実行ロジックを持っていました。
  • FutureResult が「キューイングされる実行単位」兼「結果を待ち受けるオブジェクト」という二重の役割を持っていた状態。

Now

  • コネクションプールの async キューには QueryIntent インスタンスが積まれます。
  • 実際のクエリ実行・例外処理・接続ハンドリングなどの責務は QueryIntent に集約され、FutureResult は結果を表すラッパ・フロントオブジェクトとして振る舞うだけになります。
  • ユーザーコード視点では引き続き FutureResult を通じて #result 等で結果を取得しますが、その内部実装が大きく簡素化されています。

ファイルごとの主な変更ポイント

activerecord/lib/active_record/connection_adapters/query_intent.rb (+185 / -19)

  • この PR の中心。QueryIntent に非同期実行のロジックが大きく追加されています。

  • 典型的には以下のような責務が QueryIntent 側に寄せられたと考えられます(擬似的なイメージ):

    ruby
    class QueryIntent
      def initialize(connection, sql, binds, ...)
        @connection = connection
        @sql = sql
        @binds = binds
        # ...
      end
    
      # 非同期ワーカーから呼ばれる実行メソッド
      def perform
        # コネクションの checkout / execute / ensure での cleanup など
        result = @connection.exec_query(@sql, "Load Async", @binds)
        fulfill(result)  # FutureResult などに結果を渡す
      rescue => e
        reject(e)        # 例外を FutureResult 側に伝搬
      end
    end
  • 実行中・完了・失敗などの状態管理や、コネクションの扱い、例外処理も QueryIntent が担う形に整理された可能性が高いです。

activerecord/lib/active_record/future_result.rb (+7 / -128)

  • 行数が大きく減っており、実行ロジックがほぼ削除されています。
  • 役割は以下のように単純化されたと思ってよいです:
    • 「この非同期クエリの結果をあとで受け取る」ためのオブジェクト。
    • #result でブロックしつつ結果を返す。
    • 例外が発生していれば #result 呼び出し時に再スローする。
  • 具体的には、FutureResult が保持していた「キューに積まれ、実行されるためのインターフェイス」はなくなり、QueryIntent のライフサイクルにぶら下がるだけになっています。
  • ユーザー API (relation.load_async, future.result など) の表面上の振る舞いは基本的に従来と互換のはずですが、裏側の管理単位が FutureResultQueryIntent にシフトしています。

abstract/database_statements.rb ほか各アダプタの database_statements.rb

  • +5/-19 など小さな修正ですが、以下のような変更が考えられます:

    • 非同期クエリ作成時に FutureResult.new(...) していた部分が、 QueryIntent.new(...) を作り、それに紐づく FutureResult を返すような形に変更。
    • もしくは、QueryIntent 自体が to_future_result のようなメソッドを持ち、呼び出し元には FutureResult だけが見えるようにするパターン。

    擬似コード例:

    ruby
    # 旧実装イメージ
    def select_all_async(...)
      future = FutureResult.new(self, sql, binds, ...)
      pool.enqueue(future)
      future
    end
    
    # 新実装イメージ
    def select_all_async(...)
      intent = QueryIntent.new(self, sql, binds, ...)
      pool.enqueue(intent)
      intent.future_result
    end
  • MySQL2 / PostgreSQL / SQLite3 / Trilogy の各 DB アダプタにも 1 行ずつの調整が入っており、共通の非同期インターフェイスに従うように整えられています。

activerecord/lib/active_record/connection_adapters/abstract_adapter.rb (+1 / -1)

  • 抽象アダプタ層の非同期関連メソッド(select_all_async 等)で、QueryIntent ベースの実装に合わせた微修正が入っています。
  • ここで非同期フローのエントリーポイントが QueryIntent を作るようになっているはずです。

activerecord/test/cases/relation/load_async_test.rb (+1 / -1)

  • テストはほぼそのままで、1 行だけの修正にとどまっています。
  • これは「外部仕様(load_async の振る舞い)は変わっていない」ことの裏付けとも言えます。

  1. 影響範囲・注意点

アプリケーション開発者への影響

  • 公開 API レベル(Relation#load_async, FutureResult#result など)は原則として互換であり、通常のアプリケーションコードは変更不要と思って問題ありません。
  • ただし、以下に該当する場合は注意が必要です:
    • ActiveRecord の非同期実行内部 (FutureResult の内部状態やコールバック) に依存したメタプログラミングをしている。
    • FutureResult を継承 / モンキーパッチして非標準な挙動を追加している。
    • コネクションプールの async キュー内部(キューに載るオブジェクトのクラスなど)を前提にした独自コードを書いている。
  • そのような場合、今後は QueryIntent が実行単位であることを前提に実装・パッチを書き換える必要があります。

ライブラリ・フレームワーク作者への影響

  • ActiveRecord の非同期クエリをラップしている gem / フレームワーク (GraphQL の load_async 連携など) で、FutureResult の内部のメソッドやインスタンス変数を直接触っている場合は互換性確認が必要です。
  • 将来的に非同期実行の拡張(キャンセル、タイムアウト、優先度制御など)を行う場合は、QueryIntent が拡張ポイントになる可能性が高く、FutureResult ではなく QueryIntent の API に注目すべきです。

パフォーマンス・安定性の観点

  • 実行の責務が QueryIntent に移ったことで、以下のようなメリットが狙われていると考えられます:
    • 責務分離によりコードが読みやすくなり、バグが減る。
    • 将来的な最適化(実行戦略の変更、バッチ化、より賢いスケジューリングなど)を QueryIntent に閉じた形で行いやすくなる。
  • この PR 自体は大規模な挙動変更を意図していないようですが、実装が大きく書き換わっているため、非同期クエリを多用するアプリではアップグレード後に実働環境での負荷テストや例外パターンの確認を推奨します。

  1. 参考情報 (あれば)
  • PR タイトル: Move async execution into QueryIntent (#56267)
  • 関連クラス:
    • ActiveRecord::FutureResult — ユーザーが扱う非同期結果オブジェクト(インターフェイスは継続、内部は簡素化)。
    • ActiveRecord::ConnectionAdapters::QueryIntent — 非同期クエリ実行の実体となるオブジェクト。今回の変更で責務が集中。
  • PR に添付された mermaid 図(Before / Now)は、以下を示しています:
    • 以前は FutureResult が非同期ワーカーに直接渡されていた。
    • 現在は QueryIntent が非同期ワーカーに渡され、その完了結果が FutureResult に反映される構造に分割されている。

この PR は、「非同期実行の内部設計を整理するリファクタリング」であり、将来の非同期関連機能拡張の基盤整備、と捉えるのが適切です。


#56229 [docs] Update documentation for block-level scoping [ci-skip]

マージ日: 2025/12/3 | 作成者: @shivabhusal

  1. 概要 (1-2文で)
    ActiveRecord::Relation#scoping の挙動、とくに all_queries: true を使ったブロックレベルスコープの動きやネスト時の挙動について、Rails Guides(Active Record クエリインターフェイスガイド)に明確な説明とサンプルを追加したドキュメント専用のPRです。コード本体の挙動変更はなく、ガイドの追記のみです。

  1. 変更内容の詳細

対象ファイル:

  • guides/source/active_record_querying.md に約 49 行程度のドキュメント追記

主な追加内容:

2-1. Relation#scoping の基本的な説明強化

  • Model.where(...).scoping { ... } 形式で「一時的に」デフォルトスコープのような条件を適用できる、という点をガイドで明示。
  • ブロック内で発行されるクエリに対して、その Relation が持つ条件や includes, joins, order などが反映されることを説明。

例(典型的なイメージ・ガイドに沿った内容のサンプル):

ruby
Post.where(published: true).scoping do
  Post.first
  Post.where(user_id: 1).to_a
end

上記ブロック内では、暗黙的に WHERE published = TRUE が掛かったクエリが発行される、という旨を SQL 例付きで説明しています。

2-2. all_queries: true オプションの挙動の明確化

このPRの中心は Relation#scoping(all_queries: true) の説明です。

  • 通常の scoping は「同じモデルのクエリ」にだけ適用されることが明示されます。
    • 例: Post.where(...).scoping の中で Comment をクエリしてもスコープは適用されない。
  • all_queries: true を指定すると、ブロック内で発行される「すべてのモデルのクエリ」に対してスコープが影響する、という挙動を説明。

イメージとなるサンプル:

ruby
Post.where(published: true).scoping(all_queries: true) do
  Post.first   # => WHERE published = TRUE が付く
  Comment.first # => Post のスコープがかからないのが従来だが、
               # all_queries: true の場合は、AR::Base レベルでの条件が
               # どのモデルにも適用されうる、という趣旨が説明される
end

※実装としては ActiveRecord::Base.scoping との関係などがありますが、ガイドでは「どのモデルのクエリにもスコープが及びうる」という使い方・注意点に焦点を当てています。

ガイドでは、これに対する具体的な SQL 例(SELECT ~ FROM ~ WHERE ...)を併記し、「all_queries: true を付けた場合に、どのクエリに WHERE 句が乗るのか」を視覚的に理解できるようになっています。

2-3. ネストされたスコープの挙動の説明

  • Relation#scoping をネストさせた場合、内側のスコープが外側のスコープを「上書き」するのか「マージ」されるのか、といった挙動に関する説明や例を追加。
  • 一般的には Relation のマージルールに従う(where 条件は AND マージ、order は後勝ちなど)ため、その点を含めて例で示しています。

例(趣旨イメージ):

ruby
Post.where(published: true).scoping do
  Post.where(archived: false).scoping do
    Post.first
  end
end
  • 発行される SQL は WHERE published = TRUE AND archived = FALSE のように、ネストされたスコープが組み合わさる形になることを説明。
  • また、orderlimit などについても、内側のスコープで指定したものがどのように作用するかに簡単に触れていると考えられます。

2-4. 使いどころ・注意に関する簡単なガイダンス

ガイド内で、以下のような「使い方のヒント」も含めています:

  • ブロックレベルで一時的にスコープを切り替えたい場合に scoping を使うと便利であること。
  • default_scope の代わりに「明示的な」スコープを適用する用途としても使えること。
  • ただし all_queries: true は影響範囲が広く、意図しないクエリに条件が掛かる可能性があるため注意すべき、という旨の注意喚起。

  1. 影響範囲・注意点
  • これは ドキュメントのみ の変更であり、Relation#scoping の実装には一切変更がありません。
  • 既存コードの挙動・パフォーマンスには影響しません。
  • 影響があるのは「開発者がこのガイドを読んだときの理解・使い方」であり、特に:
    • これまで scoping / all_queries: true の挙動が曖昧だった部分が明文化される。
    • all_queries: true の利用を検討する際に、影響範囲やネスト時の挙動を事前に把握しやすくなる。
  • 強力なため、テストコードや一部のバッチ処理などで all_queries: true を多用する場合、ガイドのサンプルを参考にしながら「どこまで影響が及ぶか」を確認すると安全です。

  1. 参考情報 (あれば)

#56275 Use Bundler.with_unbundled_env for GeneratorsTestHelper run_app_update

マージ日: 2025/12/3 | 作成者: @zzak

  1. 概要 (1-2文で)
    Generators のテストヘルパーで run_app_update を実行する際に、Bundler の影響を受けないように Bundler.with_unbundled_env を使うように変更した PR です。これにより、bundle exec 配下でテストを実行しても、アプリ更新用コマンドが意図しない Gem 環境に縛られずに動くようになります。

  1. 変更内容の詳細

対象ファイル:

  • railties/test/generators/generators_test_helper.rb

GeneratorsTestHelper 内の run_app_update メソッドで、Bundler.with_unbundled_env を使用するようになりました。概ね以下のような変更です(イメージ・擬似コード):

ruby
def run_app_update(*args)
  # 変更前(イメージ)
  # system(app_update_command(*args))

  # 変更後
  Bundler.with_unbundled_env do
    system(app_update_command(*args))
  end
end

ポイント:

  • Bundler.with_unbundled_env ブロック内では、BUNDLE_GEMFILEBUNDLE_BIN_PATH など Bundler が設定する環境変数がクリアされ、いわゆる「素の」Ruby/Gem 環境でコマンドが実行されます。
  • Generators のテストは、テンプレートアプリケーションを生成・更新するようなコマンド(bin/rails app:update 相当)を内部的に実行しますが、それらが「テストフレームワーク側の Bundler 環境」に引きずられないようにしています。

  1. 影響範囲・注意点
  • 影響範囲

    • Rails 本体のランタイム挙動ではなく、「Generators 関連のテストコード」に限定された変更です。
    • 主に Rails 開発者・コントリビュータ、および Rails を fork して独自にテストを回している人が対象になります。
    • CI などで bundle exec ruby -Itest ... のように Bundler 経由でテストを走らせている場合に、Generators のテストがより安定・再現性の高い形で動作するようになります。
  • 解決している問題の方向性

    • Issue #56271 で報告された、run_app_update 実行時に Bundler による環境変数や Gem ロードパスが干渉して失敗・不整合が起きる問題を緩和/解消する意図があります。
    • 典型的には、テスト実行側の Gemfile に縛られて、本来想定していない Gem バージョンで app:update が走ってしまう、といった症状を防ぎます。
  • 注意点

    • Bundler.with_unbundled_env を使うと、ブロック内では「現在の Bundler コンテキスト外」の世界になるため、その中で別の bundle exec を前提とするような処理を追加すると挙動が変わる可能性があります。
    • ただし、このメソッドはテスト専用ヘルパー内で完結しているため、一般的な Rails アプリケーション開発者がこの PR を意識する必要はほぼありません。

  1. 参考情報 (あれば)

#56272 Skip all system test files on app generation

マージ日: 2025/12/2 | 作成者: @eileencodes

  1. 概要 (1-2文で)
    Rails アプリ生成時に常に作られていた application_system_test_case.rb を、システムテストを生成したときに限って作成するように変更した PR です。scaffold でのシステムテストスキップ対応 (#55743) の取りこぼしを補完し、不要なシステムテスト関連ファイルが生成されないようにしています。

  1. 変更内容の詳細

背景

  • 以前の PR (#55743) で「scaffold 実行時に system test を生成しない」ようにしたが、
    • rails new(アプリ生成)時には依然として test/application_system_test_case.rb が自動生成されていた。
  • システムテストを使わないプロジェクトでも、このファイルが毎回作られてしまうのは一貫性がなく、ノイズになる。

主な変更点

1. アプリ生成時の system test ファイル生成ロジックの変更

対象: railties/lib/rails/generators/rails/app/app_generator.rb

  • これまで:
    • rails new でアプリを作ると、デフォルトで test/application_system_test_case.rb が生成されていた。
  • これから:
    • application_system_test_case.rb「システムテストを生成するとき、かつまだ存在しない場合」 にのみ生成されるようになる。
    • つまり、rails new 直後にはこのファイルは存在せず、
      • rails g system_test <name> などを実行したときに初めて application_system_test_case.rb が生成される、という動作になる。

この条件分岐は app generator 内のテンプレートコピー条件の見直し・削除/移動で実現されており、app 生成そのものでは system test 用のベースクラスを作らなくなっています。

2. scaffold generator 側での system test ベースクラステンプレート追加

対象:

  • railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb

  • railties/lib/rails/generators/test_unit/scaffold/templates/application_system_test_case.rb.tt

  • scaffold 用の test_unit generator に、application_system_test_case.rb 用のテンプレート (.tt) を追加。

  • scaffold から system test を生成する場合、このテンプレートを使って test/application_system_test_case.rb を作成するようにしている。

  • 中身は通常の ApplicationSystemTestCase 定義で、例えば以下のような構成になります(イメージ):

    ruby
    # test/application_system_test_case.rb
    require "test_helper"
    
    class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
      driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
    end

    ※ 実際のオプション値は Rails の標準テンプレートに準拠。

3. テストの追加・更新

対象:

  • railties/test/application/test_runner_test.rb (+22)
  • railties/test/commands/devcontainer_test.rb (+8)
  • railties/test/generators/app_generator_test.rb (-9)

主なポイント:

  • app generator の振る舞い変更に合わせたテスト

    • rails new 実行後に test/application_system_test_case.rb が存在しないこと、
    • system test 生成時に必要に応じて生成されること
      を確認するテストが追加/修正されています。
  • devcontainer 関連テストの更新

    • devcontainer 環境での test 実行やファイル有無の前提が変わる影響を受けるため、test/commands/devcontainer_test.rb にも検証が追加されています。
  • 既存の app generator テストから、もはや生成されない application_system_test_case.rb を前提としたアサーションが削除されています (-9 行)。


  1. 影響範囲・注意点
  • 新規プロジェクト (rails new)

    • これまで: test/application_system_test_case.rb が最初から存在した。
    • これから: デフォルトでは存在しない。
    • システムテストを使う場合は:
      • rails g system_test <name> を実行すれば、自動的に application_system_test_case.rb も生成される(まだ存在しない場合)。
      • あるいは自前で作成してもよい。
  • 既存プロジェクトへの影響

    • 既存アプリ内にすでに application_system_test_case.rb がある場合、この PR は生成ロジックの変更のみであり、既存ファイルには影響しない。
    • CI 設定やドキュメントなどで「rails new 直後に application_system_test_case.rb がある前提」を書いている場合は、前提が変わるため更新が必要なことがある。
  • ジェネレータ拡張/テンプレートをいじっている場合

    • 自作の generator が Rails 標準の app generator / scaffold generator の挙動に依存している場合、
      • 「ベースのシステムテストケースファイルはアプリ生成時に必ず存在する」
        という前提はもはや成り立たないので、必要に応じて
        • 存在チェック (File.exist? など)
        • なければ生成
          を行うようにする必要があります。

  1. 参考情報 (あれば)

#56269 Add a top level bin/test

マージ日: 2025/12/2 | 作成者: @byroot

  1. 概要 (1-2文で)
    Rails リポジトリのルートに bin/test が追加され、サブディレクトリへ cd しなくても各サブプロジェクト(activemodel, activerecord など)のテストを直接実行できるようになりました。あわせて、失敗テストの「再実行コマンド」もリポジトリルートからそのままコピペで使える形に改善されています。

  1. 変更内容の詳細

2-1. bin/test の新規追加

リポジトリ直下に bin/test が追加され、Rails コアの各コンポーネントのテストを簡単に実行できるようになりました。

使い方の例(PR本文のまま):

bash
# コンポーネント単位のテスト
$ bin/test activemodel
...

# 特定ファイルだけを実行
$ bin/test activemodel/test/cases/dirty_test.rb
...

# 複数ファイル + 任意のオプション(ここでは -v)を付けて実行
$ bin/test activemodel/test/cases/dirty_test.rb activemodel/test/cases/error_test.rb -v
...

特徴:

  • リポジトリルートから実行可能
    いちいち cd activemodelcd activerecord せずにテストを叩ける。
  • 単一サブプロジェクト内でのテスト実行を想定
    一回のコマンドで「activemodel と activerecord を同時に」など、複数サブプロジェクトを混在させた実行はサポートしていないと明記されています。
  • おそらく内部的には既存の tools/test.rb / tools/test_common.rb を呼び出す薄いラッパーになっており、引数に応じて対象コンポーネントを解決している形です。

bin/test の追加によって、これまで

bash
cd activemodel && bin/test test/cases/dirty_test.rb

のようにしていた作業が

bash
bin/test activemodel/test/cases/dirty_test.rb

だけで済むようになります。

2-2. テストレポーター (reporter.rb) の修正

railties/lib/rails/test_unit/reporter.rb が変更され、Rails 自身のテストを実行したときに表示される「失敗テストの再実行コマンド」が、ルートディレクトリからそのまま使える形に変わりました。

従来は、CI やローカルで失敗したときに、次のようなコマンドが表示されていました:

bash
bin/rails test /rails/activerecord/test/cases/adapters/postgresql/connection_test.rb:187
  • 絶対パス (/rails/...) になっていて、そのままコピペしても意図通りに動きにくい。
  • 実行パスも bin/rails で、コンポーネント単体テストを回す文脈とやや合わない。

これが PR によって、以下のようになります:

bash
bin/test activerecord/test/cases/adapters/postgresql/connection_test.rb:187
  • リポジトリルートからの相対パスになり、そのままコピペで実行可能。
  • コマンドが bin/test に統一され、今回追加されたワークフローと整合します。

2-3. tools/test.rb / tools/test_common.rb の微修正

tools/test.rbtools/test_common.rb にはそれぞれ 1〜2 行程度の軽微な修正が入っています。

推測される内容:

  • bin/test からの呼び出しをサポートするために、エントリーポイントの扱い引数のパース方法を少しだけ調整。
  • コンポーネント名(activemodel, activerecord など)とパスの解決ロジックを bin/test と共有するための小さな共通化。

このあたりは内部実装の整合性調整であり、外部 API/インターフェースとしては主に bin/test の追加とレポーターの出力変更が本質的な変更です。


  1. 影響範囲・注意点
  • 対象は Rails リポジトリの開発者・コントリビューター
    Rails を利用するアプリ開発者向けではなく、「Rails 本体のテストを書く・直す人」の開発体験改善が主目的です。
  • 既存のテスト実行方法は引き続き使用可能
    cd activerecord && bundle exec ruby -Itest test/cases/... のような既存手法は変わらず動作し、bin/test は追加のエイリアス・ラッパーの位置づけです。
  • 複数サブプロジェクトへの同時テスト実行はサポート外
    一回の bin/test 呼び出しで activemodelactiverecord のテストを同時に指定するような使い方は想定されていません。
    → そのようなケースでは、これまで通り各ディレクトリで個別に実行するか、独自スクリプトを使う必要があります。
  • CI ログやローカル実行時の「再実行コマンド」が変わる
    今後は bin/test ... 形式のコマンドが出力されるため、ドキュメント・社内 Wiki 等で「失敗テストの再実行方法」を案内している場合は、必要に応じて記述を更新するとよいです。
  • パス前提の変化
    再実行コマンドは「リポジトリルートからの相対パス」を前提にしています。そのため、コマンドをコピペするときは「Rails リポジトリのルートで実行しているか」を意識する必要があります。

  1. 参考情報 (あれば)
  • PR 本文: Add a top level bin/test (#56269)
  • CI の実行例(Buildkite):
    https://buildkite.com/rails/rails/builds/124229#019ade2f-ecf0-485f-ba07-5e26f23fa3e9/1245-1259
    → 実際に bin/test ベースの再実行コマンドが出ているログを確認できます。
  • 関連ファイル:
    • bin/test(新規追加スクリプト)
    • railties/lib/rails/test_unit/reporter.rb(再実行コマンドの出力元)
    • tools/test.rb, tools/test_common.rb(既存のテスト実行ユーティリティ)

#56265 Stub bundle install for ApiAppGeneratorTest

マージ日: 2025/12/1 | 作成者: @zzak

  1. 概要 (1-2文で)
    Api アプリケーションジェネレータのテスト (ApiAppGeneratorTest) で、bundle install(正確には kamal 実行時の Bundler 解決)に依存していた部分をスタブ化し、テストが外部の gem 解決に依存しないようにした PR です。これによりテスト時間が約 3 秒 → 約 1 秒に短縮され、ログノイズも解消されています。

  1. 変更内容の詳細

何をしている PR か

  • railties/test/generators/api_app_generator_test.rb の特定のテストに対して、
    • bundle install を実行したかのように振る舞うスタブを追加
    • 実際には gem 解決やインストールを行わないようにしている
  • これは既に他のテスト(PluginGeneratorTest, ActionTest::Generators::InstallGeneratorTest)で採用されている「ジェネレータの副作用をスタブするパターン」と揃えたものです。

もともと問題になっていたのは:

  • Api ジェネレータのテストの中で kamal を使った何らかの処理(おそらく kamal コマンドを実行する想定のテンプレート or ジェネレータ)があり、
  • 実行時に Bundler が rails (~> 8.2.0.alpha) を解決しようとして失敗
  • その失敗メッセージが標準出力/エラーに大量に出る上、Bundler の解決処理によりテストが余計に遅くなる

という状況でした。

PR の説明文にあるログから分かるポイント:

  • テストは「失敗はしていない」が、内部で kamal + Bundler が動いてエラーを吐いているだけ、という状態
  • Bundler が rails (~> 8.2.0.alpha) の解決を延々試み、膨大なバージョンリストを出力している
  • 最後に Bundler::GemNotFound が発生し、kamal 実行が失敗しているが、テスト側はそのエラーを結果としては無視できる形になっていた

この PR で行われたこと(推測を含むが Rails 他テストのパターン的にかなり確度が高い):

  • 該当テストケースで、ジェネレータが呼び出す kamal / bundle の実行部分をテストダブルに差し替え

    • 例(イメージ):

      ruby
      def stub_kamal
        File.write("bin/kamal", <<~RUBY)
          #!/usr/bin/env ruby
          # no-op for tests
        RUBY
        FileUtils.chmod("+x", "bin/kamal")
      end

      もしくは、システムコール層のスタブ:

      ruby
      Kamal::Commands.stub(:run, true) do
        # テスト本体
      end
  • 実際に Bundler を起動せず、ジェネレータとして「kamal 向けの設定/ファイルを正しく出力しているか」だけを検証するようにしている

  • スタブ化により、ログで出ていた Could not find gem 'rails (~> 8.2.0.alpha)' のようなメッセージが一切出なくなり、テストのアサーション数も 6 → 8 に増えているので、スタブした結果をより細かく検証するアサーションが追加された可能性が高いです。

Before / After の違い(テスト観点)

  • Before:
    • 実質的に Bundler / kamal を起動してしまう
    • 失敗ログを大量に吐きつつ、最終的なテスト結果としてはパスしている
    • 実行時間: 約 3.0 秒
    • アサーション: 6 件
  • After:
    • Bundler / kamal 実行をスタブ
    • ログは一切出ずクリーン
    • 実行時間: 約 1.2 秒
    • アサーション: 8 件(スタブが正しく呼ばれたこと/生成物の検証が増えた)

  1. 影響範囲・注意点
  • 影響範囲
    • Rails 本体のプロダクションコードには影響せず、「railties のテストコードのみ」が対象です。
    • 特に ApiAppGeneratorkamal 用のファイル/設定をどう生成するかをテストする部分のみが変更されています。
  • 良くなった点
    • テストが外部の gem レジストリ状態(rails 8.2.0.alpha がインストールされているかどうか)に依存しなくなる
    • CI / ローカル環境共にテストが安定し、ノイズログがなくなる
    • テスト実行時間の短縮
  • 注意点
    • スタブにより「実際に kamal コマンドを走らせたときの挙動」はこのユニットテストではカバーしない形になっています。
      • これは他のジェネレータテストと同じ方針で、「ジェネレータの責務(ファイル生成やコマンド呼び出しのセットアップ)だけ」をテストし、外部ツールの実際の挙動はそのツール自身のテストに任せる、という設計です。
    • kamal 側のインターフェースが変わった場合は、このテストだけでは気付きにくくなるので、
      • 統合テストや手動検証など、別レイヤーのテストで補完する必要があります。

  1. 参考情報 (あれば)
  • 類似パターンがすでに存在するテスト:
    • PluginGeneratorTest
    • ActionTest::Generators::InstallGeneratorTest
  • テストがスタブしている対象は、
    • bundle install や外部 CLI(kamal)の実行部分
    • Rails のジェネレータテストでは「外部コマンド呼び出しを no-op 化して、生成されたファイルのみ検証する」というパターンが広く使われています。

#56256 Include HTTP_FORWARDED header in IpSpoofAttackError message if available

マージ日: 2025/12/1 | 作成者: @zzak

  1. 概要 (1-2文で)
    Rails の IpSpoofAttackError が発生した際のエラーメッセージに、従来の X-Forwarded-For に加えて Forwarded (HTTP_FORWARDED) ヘッダの値も含められるようになりました。これにより、IP なりすまし検出時のデバッグ情報がより充実します。

  1. 変更内容の詳細

どの部分が変わったか

変更ファイル:

  • actionpack/lib/action_dispatch/middleware/remote_ip.rb
  • actionpack/test/dispatch/request_test.rb

ActionDispatch::RemoteIp ミドルウェア内で IpSpoofAttackError を生成するときのメッセージに、利用可能であれば HTTP_FORWARDED ヘッダを含めるように修正されています。

従来はおおむね次のようなメッセージでした(例):

txt
IP spoofing attack?! HTTP_CLIENT_IP="1.2.3.4" HTTP_X_FORWARDED_FOR="5.6.7.8"

この PR により、Forwarded ヘッダがある場合は:

txt
IP spoofing attack?! HTTP_CLIENT_IP="1.2.3.4" HTTP_X_FORWARDED_FOR="5.6.7.8" HTTP_FORWARDED="for=9.10.11.12;proto=https"

のように HTTP_FORWARDED もメッセージに追加されます。

コード上のイメージ

※PR 内容からの推定を含むサンプルです(実際の実装のニュアンスとして):

ruby
# 例: エラー生成部分のイメージ
raise IpSpoofAttackError.new("IP spoofing attack?! " \
  "HTTP_CLIENT_IP=#{@client_ip.inspect} " \
  "HTTP_X_FORWARDED_FOR=#{forwarded_for.inspect}" \
  "#{forwarded_header_part}"
)

# forwarded_header_part は HTTP_FORWARDED があれば
# ' HTTP_FORWARDED="..."' を付与するような処理

テスト (request_test.rb) では、HTTP_FORWARDED を含むリクエストヘッダで IP spoofing 状況を作り、その例外メッセージ内に HTTP_FORWARDED の値が含まれていることを検証するテストが追加されています。


  1. 影響範囲・注意点
  • 影響範囲

    • ActionDispatch::RemoteIp を使用していて、かつ IP なりすまし (spoofing) 判定が走った場合の「例外メッセージ」が変わります。
    • ランタイムの挙動(例外が発生するかどうか、どの条件で発生するか)自体は変わっていません。あくまでエラーメッセージの情報量追加です。
  • ログ解析・監視との互換性

    • IpSpoofAttackError のメッセージ文字列をパースして独自に解析している場合、HTTP_FORWARDED の追加でフォーマットが変わる可能性があります。
    • 「メッセージ全体の完全一致 (exact match)」でアラート設定やテストを書いている場合は、これを機に「部分一致」や「正規表現ベース」に変更した方が安全です。
  • Forwarded ヘッダを利用している環境

    • RFC 7239 準拠の Forwarded ヘッダを積極的に使っているリバースプロキシ構成(例: 一部の CDN / LB)では、IP spoofing 検出時のトラブルシューティングがしやすくなります。
    • どのヘッダでどの IP が渡されているかが 1 つの例外メッセージにまとまるため、プロキシの設定ミスや想定外のヘッダ上書きの発見に役立ちます。

  1. 参考情報 (あれば)
  • 関連 Issue: #56186
    • この PR は、IpSpoofAttackErrorForwarded ヘッダ情報が含まれないためデバッグが難しい、という報告を解消するものです。
  • 対象コンポーネント:
    • ActionDispatch::RemoteIp ミドルウェア
    • IpSpoofAttackError 例外クラス
  • 標準化された Forwarded ヘッダ仕様:
    • RFC 7239 – Forwarded HTTP Extension (プロキシチェーン経由のクライアント情報の標準的な表現方法)

#56264 ActiveJob.perform_all_later should respect job_class.enqueue_after_transaction_commit

マージ日: 2025/12/1 | 作成者: @byroot

  1. 概要 (1-2文で)
    ActiveJob.perform_all_later が、ジョブクラスに設定された enqueue_after_transaction_commit を正しく尊重するように修正された PR です。これにより、トランザクション完了後にキュー投入すべきジョブが、perform_all_later 経由でも期待通りに動作します。

  1. 変更内容の詳細

背景

  • Rails には、enqueue_after_transaction_commit を有効化することで「DBトランザクションがコミットされるまでジョブの enqueue を遅延させる」仕組みがあります。
  • 従来は perform_later ではこの設定が反映されていた一方で、複数ジョブを一括で投入する ActiveJob.perform_all_later(jobs) 使用時には、このフラグが正しく反映されていませんでした。
  • その結果、本来は「コミット後に enqueue」されるべきジョブが、トランザクション中に即座にキューへ入ってしまう不整合が発生し得ました。

コードレベルの変更点

1) enqueue_after_transaction_commit 対応ロジックの拡張

activejob/lib/active_job/enqueue_after_transaction_commit.rb に、perform_all_later 用の処理が追加されています。
ざっくりいうと、以下のようなことをやっています:

  • 各ジョブクラスの enqueue_after_transaction_commit 設定を確認
  • true のジョブについては「トランザクションコミット後に enqueue するためのフック」に登録
  • それ以外は従来通り即 enqueue

イメージとしては、perform_later と同じようなラッピングロジックを、配列形式の複数ジョブに対して適用する形になっています。

参考となる利用イメージ(疑似コード):

ruby
class MyJob < ApplicationJob
  self.enqueue_after_transaction_commit = true

  def perform(record_id)
    # ...
  end
end

ActiveRecord::Base.transaction do
  jobs = [MyJob.new(1), MyJob.new(2)]
  ActiveJob.perform_all_later(jobs)
  # → ここではまだ実際のキューには入らず、
  #   トランザクションがコミットされた後に enqueue される
end

2) テストの追加

activejob/test/cases/enqueue_after_transaction_commit_test.rb に 34 行のテストが追加されています。
主に以下を検証しています:

  • enqueue_after_transaction_commit = true なジョブを perform_all_later で enqueue した場合:
    • トランザクション中は実際のアダプタへ enqueue されない
    • コミット後に enqueue される
  • enqueue_after_transaction_commit = false or 未設定のジョブは、perform_all_later でも即時 enqueue される

これにより perform_laterperform_all_later 間での挙動の一貫性がテストで担保されています。


  1. 影響範囲・注意点
  • 影響を受けるケース

    • enqueue_after_transaction_commit を有効にしている ActiveJob クラスを定義しており
    • かつ ActiveJob.perform_all_later(jobs) を使用しているコード
      このようなコードは、これまで「トランザクション中に enqueue されていた」ものが、この変更により「トランザクションコミット後に enqueue される」ようになります。
  • 既存コードへの実質的な変更

    • これまでが「バグ寄りの挙動」であり、enqueue_after_transaction_commit の契約通りに動いていなかったため、修正後の挙動のほうが API の意図に沿っています。
    • ただし、もしこの「バグ前提の挙動」(トランザクション中に enqueue されること)に依存したコードやテストがある場合は、動作タイミングが変わるため注意が必要です。
  • ジョブの順序・実行タイミング

    • トランザクションがロールバックされた場合、enqueue_after_transaction_commit = true のジョブは enqueue されません。
    • この特性は元々の perform_later と同じであり、perform_all_later がこれに揃えられます。

  1. 参考情報 (あれば)
  • 修正元 PR(バグ報告 / 初期修正案): https://github.com/rails/rails/pull/56246
    • この PR はオリジナル作者がメンテナによる force push を許可していなかったため、本 PR (#56264) としてやり直されています。
  • 機能そのものの背景:
    • Rails ガイド: Active Job のドキュメント(enqueue_after_transaction_commit 周辺)
    • ActiveRecord のトランザクションコールバック (after_commit) と連携する仕組みで、DB 一貫性の担保に有用です。

#56258 Add schematized json for has_json

マージ日: 2025/11/30 | 作成者: @dhh

  1. 概要 (1–2文で)
    Rails の JSON 属性に対して「型付き・スキーマ付き」でアクセスできる schematized_json 機能が ActiveModel に追加されました。has_json / has_delegated_json を使うことで、UI から文字列で値を渡しても、DB には boolean / integer / string の正しい JSON 型で保存されるようになります。

  1. 変更内容の詳細

2-1. 機能の概要

  • 目的

    • JSON カラムを「なんでも入る Hash」ではなく、事前に決めたキーと型を持つ半構造化データとして安全に扱えるようにする。
    • フォームや API からはすべて文字列として送られてきても、モデル側で boolean / integer などに 自動キャスト したい。
  • サポートされる型

    • boolean
    • integer
    • string
    • ネストは非対応(フラットなキーのみ)

2-2. 代表的な API と使い方

PR 説明の例をベースに整理します。

ruby
class Account < ApplicationRecord
  # JSON カラム :settings に対してスキーマ定義
  has_json :settings,
           restrict_creation_to_admins: true,
           max_invites: 10,
           greeting: "Hello!"

  # JSON カラム :flags に対して、delegated なスキーマ定義
  has_delegated_json :flags,
                     beta: false,   # デフォルト値から boolean 型だと解釈
                     staff: :boolean # 明示的に boolean 型
end

has_json

  • 対象: モデルの JSON カラム(例: settings

  • 使い方:

    • キー名 => デフォルト値 で渡すと、デフォルト値の型から JSON 型が決まる
      • true / false → boolean
      • 10 → integer
      • "Hello" → string
    • デフォルト値を持たないキーは symbol で型指定できる(例: staff: :boolean
  • 実行時の挙動:

    • Account.new 時に、定義されたキーに デフォルト値がセット される。
    • before_save でも再度デフォルトセットが行われる(nil のままの場合などの補完)。
ruby
a = Account.new
a.settings.restrict_creation_to_admins? # => true (boolean 判定メソッド)
a.settings.max_invites                  # => 10 (integer)
a.settings.greeting                     # => "Hello!" (string)

has_delegated_json

  • has_json が返す「アクセサオブジェクト」を介さず、モデル直下にメソッドを生やすパターン。
ruby
a = Account.new
a.beta    # => false (flags["beta"] の boolean 値)
a.staff   # => nil   (デフォルトなし、型は boolean)
a.staff = true
a.staff?  # => true

2-3. 文字列入力からの自動型変換

この PR のキモは、「UI からは全部文字列でも、JSON としては型付きで扱える」点です。

ruby
a = Account.new

# string を代入しても integer にキャストされる
a.max_invites = "100"
a.max_invites        # => 100 (Integer)
a.settings["max_invites"] # => 100 (JSON 上も数値)

# まとめて Hash で代入した場合も同様
a.settings = {
  "restrict_creation_to_admins" => "false",
  "max_invites" => "500",
  "greeting" => "goodbye"
}

a.settings.restrict_creation_to_admins? # => false (文字列 "false" が boolean に変換)
a.settings.max_invites                  # => 500 (Integer)
a.settings.greeting                     # => "goodbye" (String)

サポートされる変換イメージ(推測を含むが一般的には):

  • boolean
    • "true", "1", "on", "yes"true
    • "false", "0", "off", "no"false
  • integer
    • "-10", "0", "42"-10, 0, 42to_i ベース)
  • string
    • そのまま保存(トリム・バリデーション等はこの層では行わない想定)

2-4. 実装位置

  • 追加ファイル:
    • activemodel/lib/active_model/schematized_json.rb
      • スキーマと JSON を結び付ける中核クラス/モジュールが定義されている。
    • activemodel/lib/active_model.rb, activemodel/lib/active_model/api.rb
      • schematized_json 機能を Active Model API から利用できるように require / include。
  • テスト:
    • activemodel/test/cases/schematized_json_test.rb
      • 型付け、デフォルト適用、キャスト、has_delegated_json の挙動などがカバーされている。

  1. 影響範囲・注意点
  • 対象バージョン

    • PR #56258 がマージされる Rails の次リリース以降で利用可能(少なくとも edge / main ブランチ)。
  • JSON カラムの前提

    • DB 側は json / jsonb(PostgreSQL)など、ネイティブ JSON 型または text + serialize でも動くが、Rails 的には JSON 属性として定義されている前提。
  • 型の制約

    • サポートは boolean / integer / string のみ。
    • 配列・オブジェクト・ネストした JSON 構造はサポート外。
      • これらを使いたい場合は、従来通り store, store_accessor, serialize, JSON カスタム型などを使う必要あり。
  • 既存データとの整合性

    • すでに JSON カラムに異なる型が入っている場合、
      • 読み出し時に想定と違う型が来る可能性がある。
      • その場合の挙動(強制キャストかエラーか)は実装依存なので、マイグレーションやデータクレンジングが望ましい。
  • before_save でのデフォルト適用

    • before_save でもデフォルト値が適用されるため、
      • 「ユーザーが nil を明示的に入れたが、保存時にデフォルトで上書きされる」ケースをどう扱うかに注意。
      • 「nil を許容したい」場合は、この schematized_json に乗せるべきか、仕様を検討した方がよい。
  • フォーム/フロントエンド側への利点

    • すべて文字列で送ってよい、という前提が作れるので、JS 側の型管理がシンプルになる。
    • ただし、boolean の "true" / "false" などは UI からのバリエーションを考慮したうえで、変換ロジックに合わせた値を送る必要がある。

  1. 参考情報 (あれば)
  • 追加クラス: ActiveModel::SchematizedJson(推定名)
  • 近い既存機能:
    • store / store_accessor(ActiveRecord::Store)
    • attribute :settings, :json, default: { ... }
  • PR:
    • GitHub: rails/rails #56258 「Add schematized json for has_json」
      (実際のメソッド名・オプション・細かいキャストルールは PR 本文・diff の schematized_json.rb / テストコードを参照するとより正確に把握できます)