Ruby on Rails PR Digest - 2025年 12月
このページは rails/rails リポジトリにマージされたPull Requestを自動的に収集し、AIで要約したものです。
#56484 Remove deprecated fetch cast type
マージ日: 2025/12/30 | 作成者: @seuros
- 概要 (1-2文で)
Rails 8.1 以降では schema cache dump に常にcast_typeが含まれるようになったため、接続オブジェクトからcast_type(キャスト用の型情報)をフォールバック取得する古い互換コードが削除されました。これにより Active Record の型キャスト周りの実装が簡素化され、古いバージョン向けのデプリケート経路が整理されています。
- 変更内容の詳細
※この PR は「振る舞いの変更」よりも「古い経路の削除/整理」が中心です。主なポイントを機能単位でまとめます。
(1) cast_type のフォールバック削除
これまでは、以下のような流れのコードが内部的に存在していました(概念的な例):
# 旧来のイメージ
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.rbactiverecord/lib/active_record/connection_adapters/column.rbactiverecord/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.rb や database_statements.rb では、型情報を取得して値を SQL に埋め込むためのロジックがありましたが、そこでも「cast_type があればそれを使う/なければ connection から取得」という if/else・条件分岐が存在していました。
この PR で:
cast_typeが schema cache に存在する前提で記述できるようになったため、条件分岐が減少- 引数として渡る型オブジェクトやカラム定義から、直接 cast/quote するだけのシンプルな形に整理
(3) schema 定義・dumper 周りの TODO 解消
abstract/schema_definitions.rb・schema_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が常に存在する)に沿うように期待値やセットアップが簡素化
これにより、テストコードも現行バージョンの前提のみを検証するものになっています。
- 影響範囲・注意点
対象バージョン・互換性
- Rails 8.1 以降を前提としたクリーンアップなので、「Rails 本体だけを通常利用しているアプリケーション」に対して目に見える挙動変更はほぼありません。
- 影響が出る可能性があるのは、内部 API に依存しているライブラリやメタプログラミングを多用したコードです。
影響しうるケース
独自に schema cache dump を生成・加工している場合
- Rails 8.1 より前形式の schema cache(
cast_typeを含まない)を、そのまま新しい Rails に読み込ませようとしていると、今回のフォールバック削除により想定外の動作をする可能性があります。 - 対応策: Rails 8.1 以降の環境で schema cache を再生成しておくことを推奨します。
- Rails 8.1 より前形式の schema cache(
ActiveRecord::ConnectionAdapters::Columnや type_caster の private/内部的 API に依存している場合- 「
cast_typeがなければ connection から調べる」といった挙動に期待していたコードは壊れます。 - 新しい前提: カラムには常に
cast_typeが紐付いているとみなすべきで、なければそれは想定外(= バグ)扱いです。
- 「
独自 adapter / connection adapter 拡張
- もし独自 adapter で schema cache の dump/load ロジックをカスタマイズしている場合、
cast_typeを含めて dump しているかどうかを確認しておく必要があります。
- もし独自 adapter で schema cache の dump/load ロジックをカスタマイズしている場合、
パフォーマンス上の意味合い
cast_typeを逐次 connection から問い合わせる経路がなくなるため、理論上はキャッシュミス時の余計な問い合わせが減り、型キャストまわりがわずかにシンプルかつ高速になります(ただし体感できるほどではない可能性が高いです)。
- 参考情報 (あれば)
関連する前提機能: 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-2文で)
Rails のActiveRecord::Relationをmergeする際、Arel の等価比較 (=) を含む where 句と、**NULL を返す relation(例:Model.noneや空条件)**をマージしたときに発生していた不具合を修正する PR です。
これにより、where(x: y)などの等価条件を含む relation を他の relation と安全にマージできるようになりました。
- 変更内容の詳細
どんな問題だったか
関連 Issue: #56481 で報告されているのは、例えば以下のようなケースです(イメージ):
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 行修正されています。
このあたりのコードは簡略化すると以下のようなイメージです(実際のコードとは多少異なりますが、趣旨を説明するための疑似コードです):
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 句が得られること(等価条件が失われない/壊れないこと)
を確認するテストです。
擬似コード例:
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- 影響範囲・注意点
- 影響範囲:
ActiveRecord::Relation#mergeを多用しているコードベース全般- とくに
Arelで自前の predicate を組み立てているケース(arel_table[:col].eq(...)など) - スコープチェーンで
none, 条件分岐付きスコープなどを組み合わせている箇所
- 期待される挙動:
- これまで
merge時に例外やおかしな where 句が出ていた箇所が、正常に動作するようになる はずです。 - 逆に「既存の動作に依存したワークアラウンド」を書いていた場合、その前提が崩れる可能性はありますが、そのようなコードは基本的にバグを前提にしているため削除・簡略化が望ましいです。
- これまで
- マイグレーションの必要性:
- データベーススキーマやマイグレーションには影響しないため、特別な対応は不要です。
- バージョンアップ時のテスト:
scopeとmergeを組み合わせて複雑な検索条件を組んでいる箇所(検索画面・管理画面など)を中心に、- 例外が出ていないか
- 生成される SQL(
to_sql)が想定どおりか
を確認すると安心です。
- 参考情報 (あれば)
- PR: https://github.com/rails/rails/pull/56482
- 関連 Issue: https://github.com/rails/rails/issues/56481
- 関連コード:
ActiveRecord::Relation#mergeActiveRecord::Relation::WhereClause(activerecord/lib/active_record/relation/where_clause.rb)
- 類似するバグパターンとして、「
mergeでor,not,noneなどを含む relation を組み合わせたときの where 句の壊れ方」が過去にも度々修正されており、今回もその一種と考えると理解しやすいです。
#56458 Select only relevant SQL for payload name on eagerload test
マージ日: 2025/12/29 | 作成者: @zzak
- 概要 (1-2文で)
このPRは、Active Record の eager load に関するインストゥルメンテーションテストで、sql.active_record通知のうち「books テーブルに対するクエリ」だけを対象にするようフィルタリングを追加した修正です。これにより、SHOW max_identifier_lengthなど別目的の SQL が混ざってテストが不安定になる問題 (#56456) を防ぎます。
- 変更内容の詳細
対象ファイル:
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 文だけを対象にするフィルタを追加/変更しています。説明にあるように、テストが本当に見たいのは以下のようなクエリです:
sqlSELECT "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として通知されるため、従来はこれらもテストの対象になってしまっていました:sqlSHOW max_identifier_lengthこのPRでは、「このファイル内の他のテストと同様に」テスト内で SQL をフィルタして、
booksテーブルに対するクエリだけを payload 名・SQL 判定に使うように変更しています。- 典型的には、
payload[:sql]やnameに対する条件でFROM "books"を含むか、あるいは不要なSHOWなどを除外する、といったフィルタリングが行われます。 - 実際のコードは1行削除・2行追加程度の小さな差分で、
notificationsを集計するブロック内、もしくはそこから取り出す段階で、対象となるエントリをselect/rejectなどで絞り込んでいると考えられます。
- 典型的には、
サンプルイメージ(実際のコードイメージ、概念的なもの):
# 変更前イメージ
sql = @queries.find { |event| event.payload[:name] == "SQL" }
# 変更後イメージ(例)
sql = @queries
.select { |event| event.payload[:sql].include?('FROM "books"') }
.first実際にはこのファイル内の他のテストと同様のスタイルで、SHOW 系のクエリを除外する、あるいは books を含む SQL だけを選ぶような条件が追加されています。
- 影響範囲・注意点
影響範囲:
- Rails 本体の挙動(本番コードパス)は一切変更されず、テストコード (
instrumentation_test.rb) のみが変更対象です。 - したがって、Rails を利用しているアプリケーションへの直接の影響はありません。
- 主な効果は、CI 環境やローカルでの Rails 本体テストスイート実行時に、eager load 関連のインストゥルメンテーションテストが安定することです。
- Rails 本体の挙動(本番コードパス)は一切変更されず、テストコード (
注意点:
- この種のフィルタリングは、DBアダプタ・バージョン・設定などにより発行される余分な SQL が増えた場合にも有効ですが、フィルタ条件が厳しすぎる/緩すぎると再びテスト不安定要因になりうるため、今後 SQL 文字列のフォーマットが変わった場合は再調整が必要になる可能性があります。
- テストでインストゥルメンテーション (
ActiveSupport::Notifications) を扱う場合には、「目的の SQL かどうかを判定するフィルタ」を明示的に入れるのが推奨される、という指針にもなります。
- 参考情報 (あれば)
- 対応 Issue:
- #56456 – eager load テストで意図しない
sql.active_record通知(例:SHOW max_identifier_length)が紛れ込む問題の修正
- #56456 – eager load テストで意図しない
- 関連箇所:
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-2文で)
Rails の開発用 Dev Container(.devcontainer)で使用する Ruby のバージョンが 3.x 系などから Ruby 4.0.0 に更新されました。VS Code Dev Containers / GitHub Codespaces などで Rails を開発する際、デフォルトで Ruby 4.0.0 が使われるようになります。
- 変更内容の詳細
- 対象ファイル:
.devcontainer/Dockerfile - 変更内容は「コンテナ内で利用する Ruby バージョン指定」を Ruby 4.0.0 に上げる、という一点のみです(+2/-2 行)。
推測される変更イメージは以下のようなものです(実際の PR を要約した擬似コード):
# 変更前(例)
ARG RUBY_VERSION=3.3.0
# 変更後(例)
ARG RUBY_VERSION=4.0.0PR の説明にあるように、DevContainer 内で ruby -v を実行すると以下のようになります:
$ 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、という位置付けです。
- 影響範囲・注意点
影響範囲
- 対象となるのは Dev Container 環境のみ です。
- VS Code Remote - Containers / Dev Containers
- GitHub Codespaces
.devcontainer/Dockerfileをベースにしたローカル Docker 開発環境
- Rails 自体の gem コードやテストコードには変更はありません。
想定される影響・注意点
Ruby 4.0.0 での互換性確認が必要
- Rails 本体の開発・CI では Ruby 4.0.0 でテストが回ることが前提となるため、Ruby 4 系での非互換・警告・新機能に依存した問題が早期に表面化します。
- プロジェクト独自の開発でこの DevContainer を流用している場合、アプリ側の gem / アプリコードが Ruby 4.0.0 に対応しているか確認が必要です。
PRISM パーサ利用による挙動差
+PRISMビルドであるため、Ruby の構文解析周りで微妙な挙動差やパフォーマンス差が発生する可能性があります。- RuboCop や Syntax 解析系ツールを使っている場合、Ruby 4 + PRISM を前提とした更新が追いついていないと警告やエラーが増えることがあります。
ローカル環境との差異
- 手元で Ruby 3.x を使い続けている開発者は、DevContainer の Ruby 4.0.0 と挙動が異なる可能性があります(特に:
- 新しい警告
- deprecated機能の削除
- パフォーマンス特性の差 など)。
- Rails のコントリビュート用には、可能であればローカルも Ruby 4.0.0 に合わせるのが望ましいです。
- 手元で Ruby 3.x を使い続けている開発者は、DevContainer の Ruby 4.0.0 と挙動が異なる可能性があります(特に:
テスト追加・CHANGELOG 更新は無し
- この PR は開発環境構築用コンテナのバージョンアップのみで、Rails の挙動そのものに直接影響を与える変更ではないため、テスト・CHANGELOG の更新は行われていません。
- 参考情報 (あれば)
- この PR が参照している DevContainer 側の対応:
rails/devcontainerリポジトリの Ruby 4.0.0 追加 PR:
https://github.com/rails/devcontainer/pull/106
- Rails の Dev Container 全体像を把握したい場合:
- Rails 本体リポジトリの
.devcontainer/ディレクトリ一式
(Dockerfile / devcontainer.json など)を参照すると、VS Code / Codespaces 向けの標準開発環境構成を確認できます。
- Rails 本体リポジトリの
#56474 Add ActionDispatch::Request#bearer_token to extract the bearer token
マージ日: 2025/12/28 | 作成者: @dhh
- 概要 (1-2文で)
ActionDispatch::Requestに#bearer_tokenメソッドが追加され、Authorizationヘッダから Bearer トークンを簡単かつ統一的に取得できるようになりました。主に API や MCP (Model Context Protocol) リクエストでのトークン認証を想定したユーティリティです。
- 変更内容の詳細
2-1. 追加されたメソッド
ActionDispatch::Request#bearer_token が追加されました。
目的:Authorization ヘッダから Bearer トークンだけを安全に抽出するためのヘルパです。
典型的には、以下のようなヘッダを想定しています:
Authorization: Bearer abc123xyzこのとき:
request.bearer_token
# => "abc123xyz"のようにトークン文字列だけを取得できます。
実装的には以下のような挙動が考えられます(概念的なサンプル):
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が空文字/空白のみ →nilAuthorization: Basic xxxなど別スキーム →nilを返すBearerとトークンの間にスペースがないなど、形式が不正 →nilor 無視
これにより、bearer_token の挙動が明確に規定されています。
- 影響範囲・注意点
3-1. 影響範囲
- 対象:
ActionDispatch::Requestを使うすべての Rails アプリケーション(API モードを含む) - 性質: 既存メソッドへの変更ではなく、新規追加のため後方互換性への影響はほぼありません。
- 利用シーン:
- API 認証 (JWT, API トークン)
- MCP 経由のリクエスト処理時の認可判断
- Rack ミドルウェアやコントローラでのヘッダ解析
3-2. 注意点・設計上のポイント
Bearer 以外は対象外
BasicやDigestなど他のAuthorizationスキームには反応せず、nilを返す想定です。
→ 既にAuthorizationヘッダを生でパースしている処理がある場合、bearer_tokenへの置き換えは「Bearer 前提」のコードにだけ行うべきです。形式依存
たとえばAuthorization: Bearerのみ(トークンなし)や、Bearerの前後の空白が異常な形式の場合、nilになる可能性があります。
→ 不正なトークン形式を 401 にしたい場合は、nilチェックを行い、エラー応答を返す責務はアプリ側にあります。大文字小文字
多くの場合Bearerのスキーム名は大文字小文字を無視する実装になりますが、テストで case-insensitive が担保されているか(Bearer/bearer/BEARER)を前提に使うとよいです。既存実装からの移行
すでに以下のようなコードがある場合:rubyauth = request.headers["Authorization"] token = auth.to_s.split(" ").last if auth&.start_with?("Bearer ")これは次のように簡略化できます:
rubytoken = request.bearer_token共通ユーティリティを使うことで、認証周りの処理を統一しやすくなります。
- 参考情報 (あれば)
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-2文で)
ActiveJobのenqueue_after_transaction_commitのデフォルト値が、ActiveRecord 利用時にはtrueになるように変更され、トランザクション内でenqueueされたジョブはコミット後にキュー投入される挙動が標準になります。併せて、ActiveRecordがない環境でenqueue_after_transaction_commit = trueを設定するとエラーにするバリデーションが追加されました。
- 変更内容の詳細
デフォルト挙動の変更
これまで:
- グローバルには
enqueue_after_transaction_commitのデフォルトはfalse - トランザクション内で
perform_laterすると、トランザクションの成否に関係なく即時enqueue されうる
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されない
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を設定すると
→ 例外を発生させて、誤った設定を早期に検出
イメージ:
class SomeJob < ActiveJob::Base
self.enqueue_after_transaction_commit = true
end
# ActiveRecord がロードされていない環境では、ここでエラーになる設定まわり・ガイドの更新
変更ファイルから分かること:
activejob/CHANGELOG.md- この挙動変更がリリースノートとして明示される
guides/source/configuring.md/8_2_release_notes.mdconfig.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 で新規/アップグレード時に、この設定を明示的に制御できるようなテンプレート行が追加
(例: 新バージョンのデフォルトを段階的に有効化/無効化できるようにする)
- Rails 8.2 で新規/アップグレード時に、この設定を明示的に制御できるようなテンプレート行が追加
- 影響範囲・注意点
(1) 既存アプリでの挙動変化
- ActiveRecord を使用しているアプリで、これまで「トランザクション中に即enqueueされる」前提でジョブを書いていた場合、挙動が変わる 可能性があります。
- 例: 「DBトランザクションが終わる前にバックグラウンドで別システムに通知を投げたい」などのユースケース
そうしたケースでは、ジョブクラス側で明示的に元の挙動に戻せます:
class ImmediateNotifyJob < ApplicationJob
self.enqueue_after_transaction_commit = false
endあるいはグローバル設定として:
# 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を遅らせる」こと自体がサポート対象外なので、代替手段(アプリ側で明示的にコミット後に実行するなど)を検討する必要があります。
- 参考情報
- 該当PR: #55788 – Activejob: Enable enqueue_after_transaction_commit by default with ActiveRecord and validate invalid configurations
- DHHコメント: #53375 のコメント
“Yes, by default, all jobs should enqueue after the commit.”
- 関連するベストプラクティス:
- 「トランザクション内で非同期ジョブをenqueueしない」方が安全、という話題がRailsコミュニティ内で繰り返し共有されており、今回の変更はそれをフレームワークレベルでサポートする方向のアップデートです。
#56455 Allow Rails.app.creds to access .env values in dev
マージ日: 2025/12/28 | 作成者: @dhh
- 概要 (1-2文で)
Rails 7.2(?) 以降の開発環境において、Rails.app.credsから.envファイルの値を参照できるようになり、ENV /.env/ 暗号化 credentials を統一的な優先順位で扱えるようになりました。これにより、開発時の秘密情報管理や外部ツール連携 (例: 1Password CLI) が、credentials API に一本化されます。
- 変更内容の詳細
2-1. 設定の検索順序の変更
Rails.app.creds(内部的には Rails.application.credentials 相当の仕組み)が、次の優先順位で値を探すようになります(開発環境):
- 環境変数
ENV .envファイル (新規)- 暗号化 credentials (
config/credentials.yml.encなど)
これにより、たとえば以下のようなコード:
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 の値を参照して組み立てできます:
DB_HOST=localhost
DB_PORT=5432
DATABASE_URL=postgres://${DB_HOST}:${DB_PORT}/appこの場合、.env ロード後に DATABASE_URL は以下のように展開されます:
DATABASE_URL = "postgres://localhost:5432/app"Rails.app.creds[:database_url] からは、この展開済みの値を取得できます。
コマンド実行
値の中で $(...) を使うと、シェルコマンドを実行してその結果を値として埋め込めます。例として 1Password CLI の利用が想定されています:
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 を読み込む DotEnvConfiguration が Rails.app.creds の構成に組み込まれます。
また、以下も変更されています:
railties/lib/rails/generators/rails/app/templates/env.tt- Rails new 時に生成される
.envテンプレートが追加され、利用が推奨される方向になっています。
- Rails new 時に生成される
- 各種 generator テスト (
app_generator_test,api_app_generator_test) やcreds_testで.env連携の挙動を検証するテストが追加。
- 影響範囲・注意点
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 に書くか」を明確にしておくと運用がスムーズです。
- 参考情報 (あれば)
- 追加クラス:
ActiveSupport::DotEnvConfigurationActiveSupport::EnvConfigurationと同等 API を持つ.env向け実装。
- 関連テスト:
activesupport/test/dot_env_configuration_test.rb- 変数展開・コマンド実行・優先順位などの詳細な挙動が確認できる。
activesupport/test/combined_configuration_test.rb- ENV /
.env/ credentials の組み合わせ時の挙動を確認。
- ENV /
railties/test/application/creds_test.rbRails.app.credsから.envが見えることの統合テスト。
この PR によって、「開発は .env、本番は ENV/credentials」というよくある構成でも、アプリコードからは一貫して Rails.app.creds にアクセスするだけで済むようになり、設定アクセスパターンの統一がしやすくなっています。
#56183 Document how to enable Rate Limiting in tests
マージ日: 2025/12/27 | 作成者: @Kerrick
- 概要 (1-2文で)
このPRは、Railsのrate_limitをテスト環境で有効にするための設定方法を公式ドキュメントに追記したものです。デフォルトのテスト用キャッシュ (NullStore) のままだとレートリミットが動作しない、という「ハマりどころ」を明示的に説明しています。
- 変更内容の詳細(あればサンプルコードも含めて)
- 対象:
ActionController::RateLimiting::ClassMethods#rate_limitのAPIドキュメント - 変更内容: 「バックエンドのキャッシュストアに関する説明」直後に、テスト環境向けの注意書きを1パラグラフ追加
ドキュメントに追加された主旨は次のような内容です(要約):
- Railsアプリはデフォルトでテスト環境に
ActiveSupport::Cache::NullStoreを使うよう生成される。 rate_limitは内部でキャッシュストアを利用するため、NullStoreのままではレートリミットが機能しない。- コントローラのレートリミットをテストしたい場合は、テスト環境でも実際に状態を保持するキャッシュストアを設定する必要がある。
実際の設定イメージ(config/environments/test.rb など):
# 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あるいは、一部のテストだけで一時的にストアを差し替えるパターンもありえます:
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自体はコードロジックの変更ではなく、こうした点をドキュメントとして明示しただけです。
- 影響範囲・注意点
実行時挙動への影響
- ランタイムコードの変更はなく、
ActionController::RateLimitingの挙動は従来通りです。 - 既存アプリの挙動が変わることはありません(ドキュメント追記のみ)。
- ランタイムコードの変更はなく、
テスト環境での実務的な影響
- これまで「
rate_limitをテストで呼んでいるのに、リミットがかからない」という状況に気付きにくかった問題が、ドキュメントで明示されます。 - レートリミットをテストしたい場合は、テスト環境の
cache_store設定を見直す必要があることを、開発者が理解しやすくなります。
- これまで「
注意点
NullStoreはリクエスト間で状態を保持しないため、レートリミットのように「一定期間のアクセス回数を数える」機能には不向きです。memory_storeはシンプルでテスト向きですが、並列テスト・プロセス分割(parallel tests)をしている場合、プロセスごとに独立したメモリを使うため、「プロセスを跨いだレートリミット挙動」は再現できません。並列実行・実環境に近い挙動をテストしたい場合はredis_cache_storeなどの外部ストアを検討する必要があります。- キャッシュを変えると他のテストの前提に影響する可能性があるため、
- 環境全体の
config.cache_storeを変えるか - テストクラス/テスト単位で一時的に差し替えるか
をプロジェクトの方針に沿って選ぶ必要があります。
- 環境全体の
- 参考情報 (あれば)
該当メソッドのAPIドキュメント:
https://api.rubyonrails.org/classes/ActionController/RateLimiting/ClassMethods.html#method-i-rate_limitレートリミティングの実装概要(参考):
rate_limitは、指定したキーとウィンドウ(期間)・回数上限にもとづいて、内部でキャッシュストアにカウンタ等を保存して判定する仕組みです。- そのため、キャッシュストアが「何も保存しない (
NullStore)」だと、毎回リセットされた状態とみなされ、リミットがかからない状態になります。
関連ディスカッション(PR本文での参照):
#56433 don't create unused routes in authentication generator
マージ日: 2025/12/23 | 作成者: @gregmolnar
- 概要 (1-2文で)
Rails の認証用ジェネレータが、実際には使われていない RESTful ルートまで routes.rb に生成していた問題を修正し、必要なルートだけを生成するようにした PR です。これにより、不要なルーティングが減り、生成されるアプリのルーティングがシンプルかつ意図に沿ったものになります。
- 変更内容の詳細
※ PR 本文にはコード断片がありませんが、差分から読み取れる範囲での解説です。
何が問題だったか
rails g authentication(Rails 8 で入った認証ジェネレータ)を実行すると、生成される config/routes.rb に「コントローラ側で実装していないアクション用のルート」まで含まれていました。典型的には、以下のようにフルセットの RESTful ルートが生成されていたと考えられます。
# イメージ(以前の状態の例)
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 に書き込むルーティング定義を変更しています。イメージとしては次のような変更です(擬似コード/概念図):
# 変更前(例)
route <<~RUBY
resources :passwords
RUBY
# 変更後(例)
route <<~RUBY
resource :password, only: [:new, :create, :edit, :update]
RUBYあるいはセッションの場合:
# 変更前(例)
resources :sessions
# 変更後(例)
resource :session, only: [:new, :create, :destroy]ポイントは次の通りです。
resources→resourceにして単数系にしたり、only: [...]オプションを付けて、ジェネレータが用意するアクションに合わせてルートを絞り込んだり、- あるいはそもそも不要な
resources定義そのものを削除したり、
といった形で、「コントローラが使うアクションだけのルート」を生成するようにしています。
テストの変更
railties/test/generators/authentication_generator_test.rb では、認証ジェネレータ実行後に生成される config/routes.rb の内容を検証するテストが更新されています。主な確認内容は:
- 不要な RESTful ルートが生成されないこと
- 必要なルート(例:
new_session,session,destroy_session相当)が正しく生成されていること
テスト行数が少し増えているので、具体的にはマッチさせるルーティングのパターン(assert_file "config/routes.rb" 内の期待値)が変更・追加されたと考えられます。
- 影響範囲・注意点
- 対象は 新しく認証ジェネレータを実行した場合の routes.rb の内容 です。既に生成・編集済みの
config/routes.rbには、自動では影響しません。 - これまで「ジェネレータが勝手に作っていた余分なルート」に依存していたコードは、おそらく存在しない前提ですが、もしカスタマイズした上でそれらを利用していた場合、新規生成したプロジェクトではルートが足りなくなる可能性があります。
- その場合は、手動で
resourcesに戻すか、必要なアクションをonly:/member/collectionで追加してください。
- その場合は、手動で
- 不要ルートが消えることで、以下のメリットがあります:
- ルーティングの衝突(他のリソースの
index/showなど)リスクの軽減 rails routes出力のノイズ削減- 認証に関する “公式な” エンドポイントの意図がより明確になる
- ルーティングの衝突(他のリソースの
- 参考情報 (あれば)
- Rails Guides: Routing – RESTful Routes
https://guides.rubyonrails.org/routing.html#resource-routing-the-rails-defaultresourceとresourcesの違いや、only:/except:オプションの使い方がまとまっています。
- Rails 8 の Authentication Generator の紹介(Edge Guides)
https://edgeguides.rubyonrails.org/authentication.html(URL は将来的に変わる可能性あり)- 認証ジェネレータがどのコントローラ・アクションを生成する想定かを確認すると、この PR の意図がより理解しやすいです。
#56442 Pin Minitest to version 5 on 7-2-stable
マージ日: 2025/12/23 | 作成者: @yahonda
- 概要 (1-2文で)
Rails 7.2 系(7-2-stable ブランチ)で利用する Minitest のバージョンを「5 系」に固定し、Minitest 6 への自動アップデートを防ぐ変更です。理由は、Minitest 6 が Ruby 3.2 以上を要求するのに対し、Rails 7.2 は Ruby 3.1 をサポート対象としているためです。
- 変更内容の詳細
- 変更ファイル:
activesupport/activesupport.gemspec - 変更点は 1 行のみで、依存関係として指定している Minitest のバージョン条件が変更されています。
イメージとしては、以下のような変更です(実際の記述はブランチ上で多少異なる可能性がありますが、意味としてはこういう変更です):
# 変更前(例)
spec.add_development_dependency "minitest"
# 変更後(例)
spec.add_development_dependency "minitest", "~> 5.0"ポイント:
- 「Minitest 5 系にピン留め(pin)」することで、
bundle update等を行っても Minitest 6 には上がらないようにしています。 - Rails 7.2.x はすでに「セキュリティメンテナンスのみ」のフェーズに入っており、大きな互換性変更を入れない方針であるため、このような保守的な対応になっています。
- 影響範囲・注意点
対象:
- 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 を使っていても、
注意点:
- Rails 7.2.x アプリを Ruby 3.1 で運用している場合は、この変更によりテストが動かなくなることは基本的にありません(むしろ、Minitest 6 への意図しない更新で落ちるリスクを下げる変更です)。
- Ruby 3.2 以上を使っていて「Minitest 6 を積極的に使いたい」場合でも、7-2-stable では Rails 側が 5 系を前提としているため、バージョンを上げると互換性の問題が出る可能性があります。その場合は Rails 7.2 ではなく、より新しい Rails バージョンへのアップグレードを検討した方がよいです。
- 参考情報 (あれば)
Minitest 6 が Ruby 3.2 を要求する変更コミット:
https://github.com/minitest/minitest/commit/8a50ebfee5d17dc231e5fb87bf936bdf250429a1Rails 7.2 の Ruby 対応バージョンを示す gemspec(例):
https://github.com/rails/rails/blob/a24be8d963d2d43c295c89c997c98508ab6237bf/rails.gemspec#L12Rails のメンテナンスポリシー(7.2.x がセキュリティメンテナンスフェーズである説明):
https://rubyonrails.org/maintenance
#56440 Fix live_streaming_excluded_keys docs [ci skip]
マージ日: 2025/12/23 | 作成者: @zzak
- 概要 (1-2文で)
ActionController::Liveのlive_streaming_excluded_keysに関するドキュメントの誤りが修正されました。実装に合わせて説明が正しくなるように調整しただけで、挙動そのものの変更はありません。
- 変更内容の詳細
※PR本文から読み取れる範囲では「コードの挙動変更」ではなく「ドキュメントの修正」が中心です。変更ファイルは以下の2つです。
actionpack/CHANGELOG.mdactionpack/lib/action_controller/metal/live.rb
live_streaming_excluded_keys は、ActionController::Live を使ったライブストリーミング時に Rack 環境(request.env)からコピーしないキーを定義するための設定です。
PRタイトルが示す通り、「どのキーが除外されるのか」「どういう目的で使うのか」といったドキュメント上の説明が、実際のコードと合っていなかった部分が修正されています。
典型的には、以下のような形でクラス毎に上書きできる設定です:
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 の意味やデフォルト値についての説明テキストが修正されたと考えられます。
- 影響範囲・注意点
ランタイムの挙動変更はなし
live_streaming_excluded_keysの実際の値や、その適用ロジックは変わっていません。- 既存アプリケーションの動作に影響はありません。
ドキュメントに依存していた場合の「認識ズレ」が解消
- これまでドキュメントを読んで設定していた開発者は、「仕様だと思っていた内容」が実装と食い違っている可能性がありましたが、それが修正されます。
- ライブストリーミング時に「コピーされないはず」「コピーされるはず」と認識していた環境変数・ヘッダキーの扱いを、あらためて実装ベースで確認しておくと安心です。
将来的な追従がしやすくなる
- ドキュメントとコードが揃ったことで、
live_streaming_excluded_keysをカスタマイズするときの判断材料が正しくなり、保守性が向上します。
- ドキュメントとコードが揃ったことで、
- 参考情報 (あれば)
- 該当 PR: https://github.com/rails/rails/pull/56440
- 指摘元のディスカッション: https://github.com/rails/rails/pull/56393#discussion_r2631312560
- 関連コード:
ActionController::Live実装actionpack/lib/action_controller/metal/live.rb
- ライブストリーミングの公式ガイド(英語):
#56434 [8-1-stable] Minitest 6 support
マージ日: 2025/12/23 | 作成者: @zzak
- 概要 (1–2文で)
このPRは、Rails 8.1 系 (8-1-stableブランチ) に Minitest 6 対応をバックポートしたものです。Minitest 6 での非互換・仕様変更に追従しつつ、rails testや ActiveSupport::TestCase まわりの動作が壊れないように調整しています。
- 変更内容の詳細
※元 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.rbactivesupport/lib/active_support/testing/assertions.rbactivesupport/lib/active_support/testing/autorun.rbactivesupport/lib/active_support/testing/parallelization/worker.rbactivesupport/test/test_case_test.rb
Minitest 6 での変更点に合わせて、Rails 独自のテスト拡張を調整しています。
主なポイント:
a. ActiveSupport::TestCase の互換層・拡張
ActiveSupport::TestCase が内部で継承している Minitest::Test 側の API 変更に追従するためのコードが追加されています。
典型的には以下のような点が調整対象になります:
- ライフサイクルフック (
before_setup,after_teardownなど) の呼び出し順や定義位置 - Minitest 側の非推奨 API に依存していた場合の差し替え
PR diff から見ると:
# 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) なので、主に以下のような内容が想定されます:
module ActiveSupport::Testing::Assertions
# Minitest 6 での失敗オブジェクト/メッセージ仕様に合わせて、
# 内部で使う assertion 呼び出しやブロックの扱いを微調整
endc. 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.rbrailties/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 側フィルタ実装を大きめに書き換えていると考えられます。
想定される実装イメージ:
module Rails::TestUnit::LineFiltering
# 対象ファイルの AST / ソースコードから、行番号に対応する
# テストメソッド名を推論し、Minitest 側のフィルタに渡せる
# 条件 (正規表現など) に変換する処理が変更されている。
endMinitest 6 の Minitest::Test#name や Minitest::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 を読み込む順序の調整、並列化設定など)が主と考えられます。
- 影響範囲・注意点
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 周辺の実装を一度確認するのが無難です。
- 参考情報 (あれば)
- 元 PR:
- #56207, #56385, #56202
→ 本 PR はこれらの「Minitest 6 サポート」関連の変更を 8.1 用にバックポートしたものです(ただし #56202 のコミットc5c678a6...は除外)。
- #56207, #56385, #56202
- Minitest 6 の仕様変更
- 実際に影響を知りたい場合は Minitest のリリースノート / CHANGELOG を参照してください。
- 特に
Minitest::Testのライフサイクル、autorun, 並列実行、フィルタリングまわりが関係します。
- Rails 側のテスト関連コード:
ActiveSupport::TestCaseActiveSupport::Testing::*Rails::TestUnit::*railties/lib/minitest/rails_plugin.rb
これらを読むと、Minitest 6 環境下で Rails がどのようにテストランナーをラップしているかを把握できます。
#56435 [8-0-stable] Minitest 6 support
マージ日: 2025/12/23 | 作成者: @zzak
- 概要 (1–2文で)
Rails 8.0-stable ブランチに対して、Minitest 6 への対応を行うバックポート PRです。Gemfile の依存関係更新に加え、Minitest 6 での非互換点(autorun、並列実行、ラインフィルタなど)に追随するためのテスト基盤コードの修正が含まれます。
- 変更内容の詳細
2-1. Gemfile / Gemfile.lock: Minitest 6 への更新
GemfileとGemfile.lockで Minitest のバージョンが 6 系に更新。- それに伴う依存解決の変更により、lock ファイルの差分(+6/-11)が発生。
目的:
- Rails 8.0 系でも Minitest 6 を公式にサポートし、最新版の Minitest 環境でテストが動作するようにする。
2-2. ActiveSupport::TestCase 周辺の対応
対象ファイル:
activesupport/lib/active_support/test_case.rbactivesupport/lib/active_support/testing/assertions.rbactivesupport/lib/active_support/testing/autorun.rbactivesupport/lib/active_support/testing/parallelization/worker.rbactivesupport/test/test_case_test.rb
主なポイント:
a. ActiveSupport::TestCase に Minitest 6 対応の設定を追加
ActiveSupport::TestCase 自体が Minitest::Test を拡張する形で、Minitest 6 の API/挙動に合わせた初期化・設定が追加されています。
例(イメージ):
module ActiveSupport
class TestCase < Minitest::Test
# Minitest 6 用のフックや設定
end
endb. 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 の自動実行ロジックが衝突しないように調整。
イメージ:
# 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.rbrailties/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 側で責務を持って解決するコードが増えている可能性があります。
疑似イメージ:
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影響範囲・注意点
テスト環境の Minitest バージョン
- Rails 8.0-stable を使うプロジェクトは、基本的に Minitest 6 を前提とすることになります。
- Gemfile で古い Minitest を固定している場合は、バージョン競合や非互換が起きる可能性があります。
独自 Minitest 拡張の互換性
- プロジェクト側で独自の Minitest プラグインや monkey patch(
Minitest::Test拡張、Minitest.autorunのラップなど)を持っている場合、それらが Minitest 6 の API 変更と衝突しないか要確認です。 - 特に、
autorun周り・カスタムランナー・並列実行・Minitest::Runnableまわりを直接いじっている場合は注意が必要です。
- プロジェクト側で独自の Minitest プラグインや monkey patch(
行番号指定実行 (
bin/rails test ...:line) の挙動変化- 行フィルタの実装がかなり書き換えられているため、
- 以前は動かなかったパターンが動くようになる
- 逆に、非常に特殊なケースで挙動が変わる
可能性があります。
- CI や開発フローで行指定実行を多用している場合は、一度挙動を確認するのが安全です。
- 行フィルタの実装がかなり書き換えられているため、
テスト出力・エラー表示の細かい変化
- Minitest 6 のエラーメッセージフォーマットや集計結果表示が従来と微妙に変わることがあります。
- テスト結果の文字列出力をパースしているようなツール(独自のレポーター、CI 連携スクリプトなど)は影響を受ける可能性があります。
- 参考情報 (あれば)
元 PR(master/main への対応)
- https://github.com/rails/rails/pull/56207
- https://github.com/rails/rails/pull/56385
- https://github.com/rails/rails/pull/56202(※ただし commit
c5c678a6...はこのバックポートでは除外)
Minitest 公式リポジトリ
行番号指定実行のドキュメント(現行 Rails ガイド)
- Testing Rails Applications > Running Tests
bin/rails test test/models/user_test.rb:42などのセクション(Minitest 6 対応後もインタフェース自体は同じですが、内部実装が今回の PR で更新されています)
#56439 [ci skip] Clarify behavior of monday and sunday methods in guides
マージ日: 2025/12/23 | 作成者: @Yuhi-Sato
- 概要 (1-2文で)
このPRは、RailsガイドのDate/ActiveSupport拡張に関するドキュメントを修正し、mondayとsundayメソッドの挙動をより正確に説明したものです。コードの挙動自体は変えず、「常に前の月曜日/次の日曜日」ではなく「同じ日付を返す場合がある」ことを明示しました。
- 変更内容の詳細 (サンプルコード含む)
修正対象
guides/source/active_support_core_extensions.mdDate(あるいはActiveSupport::TimeWithZoneなど)に追加されるmonday,sundayメソッドの説明文が修正されています。
挙動の明確化
PRの説明にある通り:
mondayandsundaymethods in Date class return the same day when called on a Monday or Sunday respectively, not strictly the previous Monday or next Sunday.
つまり、ドキュメント上のイメージが以下のような「常に過去/未来の特定曜日を返すメソッド」になっていたのに対して、それを「現在の日付がその曜日なら、そのまま同じ日を返す」という実際の挙動に合わせて説明し直しています。
実際の挙動のイメージ
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」のような自己返しのケースを説明に追加
といった小さな文言修正であると推測できます。
- 影響範囲・注意点
コードへの影響はなし
変更はガイド文書 (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この仕様をガイドに明記したことで、このようなロジックを組む際の設計判断がしやすくなります。
- 参考情報 (あれば)
- PR本体: https://github.com/rails/rails/pull/56439
- 関連ドキュメント:
- Active Support Core Extensions Guide(
Date/Time拡張)
https://edgeguides.rubyonrails.org/active_support_core_extensions.html
- Active Support Core Extensions Guide(
- Rubyの
Dateにおける曜日ヘルパーは、monday?,sunday?などの述語もあるため、「同じ日を返しうるメソッド」と「必ず前/次を返すロジック」を明確に分けるのが安全です。
#56429 Fixes for default handling in CombinedConfiguration
マージ日: 2025/12/23 | 作成者: @jordan-brough
- 概要 (1-2文で)
CombinedConfigurationまわりの「default」値の扱いに関するバグ修正です。env_configurationとencrypted_configurationの両方で「遅延評価」と「false を正しく扱う」ように挙動が統一されました。
- 変更内容の詳細
背景
CombinedConfiguration は、複数の設定ソース(例: 環境変数 + 暗号化設定ファイル)をまとめて扱うための仕組みです。その中で default: オプション(値が存在しない場合に使うデフォルト値)が正しく扱われていないケースがあり、それを修正しています。
1. env_configuration.rb の default を「遅延評価」に変更
以前:
default:に Proc など「呼び出し可能なオブジェクト」を渡しても、それが「そのままの値」として扱われる、あるいは eager に評価される可能性があり、encrypted_configurationと挙動が揃っていなかった。
今回:
encrypted_configurationと同様に、「値が存在しなかった場合にのみ default を呼び出す」形に修正。defaultが Proc 等の場合は「遅延評価」され、実際に必要になるまで実行されない。
イメージ(擬似コード):
# 環境変数から設定を読む 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が返るように修正。
例:
# 以前の挙動(バグ)
value = config.fetch(:FEATURE_ENABLED, default: false)
# 環境変数 FEATURE_ENABLED が無い場合 → value は nil になっていた
# 修正後
value = config.fetch(:FEATURE_ENABLED, default: false)
# 環境変数が無い場合 → value は falseboolean フラグ用途で default: false を使っていた場合、これまで nil が返っていたのが false になるため、条件分岐での挙動が正しくなります。
3. encrypted_configuration.rb の default コールバックが false を返す場合の修正
以前:
default:に Proc を渡し、その Proc がfalseを返した場合に、返り値としてfalseではなく「Proc オブジェクト自体」が返ってしまうバグがあった。
今回:
- default コールバック(Proc 等)の戻り値が
falseの場合でも、そのfalse自体が正しく設定値として返されるように修正。
例:
config.read(:secret_key_base, default: -> { false })
# 以前の挙動(バグ)
# => #<Proc: ...> が返ることがあった
# 修正後
# => false が返るテストの追加・更新
encrypted_configuration_test.rb- default コールバックが
falseを返すケースのテストが追加/修正。
- default コールバックが
env_configuration_test.rbdefault: falseがfalseを返すこと- default の遅延評価(必要なときのみ呼び出されること) を確認するテストが追加。
- 影響範囲・注意点
対象:
ActiveSupport::EnvConfiguration経由でdefault:オプションを使っている箇所ActiveSupport::EncryptedConfigurationでdefault:に Proc(コールバック)を渡している箇所
互換性上のポイント:
- 以前のバグを前提にしたコードがあると挙動が変わる可能性があります。
default: falseを指定していて「nil が返る」ことを前提にしていた場合、今後はfalseが返るようになる。- default コールバックが
falseを返したときに「Proc 自体が返る」ことを利用していた(通常はありえないが)場合、今後はfalseが返る。
- いずれも「元々の設計意図(boolean を正しく扱う、遅延評価する)」に沿った修正であり、一般的なコードにとってはむしろバグ修正としてプラスに働きます。
- 以前のバグを前提にしたコードがあると挙動が変わる可能性があります。
パフォーマンス:
- default の評価が遅延されることで、重い計算や I/O を default に書いている場合、実際に必要な場合にのみ実行されるようになり、無駄な処理が減ります。
- 参考情報 (あれば)
- 元の
CombinedConfiguration追加 PR: https://github.com/rails/rails/pull/56404 - 本 PR: https://github.com/rails/rails/pull/56429
- 関連クラス:
ActiveSupport::EnvConfigurationActiveSupport::EncryptedConfigurationActiveSupport::CombinedConfiguration
#56431 Add "Missing key: ..." to KeyError message in EncryptedConfiguration
マージ日: 2025/12/22 | 作成者: @jordan-brough
- 概要 (1-2文で)
EncryptedConfiguration でKeyErrorを発生させる際のエラーメッセージに、「Missing key: ...」という具体的なキー名を含めるようにした PR です。既にCombinedConfigurationで行っている挙動と揃えるための、開発者向けの利便性改善です。
- 変更内容の詳細
EncryptedConfiguration の KeyError メッセージ改善
ActiveSupport::EncryptedConfiguration 内で設定値を取得するとき、存在しないキーを参照すると KeyError が発生しますが、そのメッセージを以下のように変更しています。
- 変更前(イメージ)text
key not found: :some_missing_key - 変更後(イメージ)text
Missing key: :some_missing_key
実際には、CombinedConfiguration がすでに以下のような形でメッセージを組み立てていたため、EncryptedConfiguration 側もそれに合わせた形です。
# CombinedConfiguration 側のイメージ
raise KeyError, "Missing key: #{key.inspect}"この PR では、encrypted_configuration.rb 内の KeyError 発生箇所を同様のメッセージフォーマットに変更しています(1行の差分)。
テストの追加・修正
2つのテストファイルが更新されています。
activesupport/test/combined_configuration_test.rbKeyError発生時のメッセージに"Missing key: ..."が含まれることを検証するテストを追加(+12 行)。
activesupport/test/encrypted_configuration_test.rb- EncryptedConfiguration 側でも
"Missing key: ..."形式のメッセージになることを検証するようにテストを修正(+3/-3)。
- EncryptedConfiguration 側でも
これにより、CombinedConfiguration と EncryptedConfiguration の挙動・メッセージフォーマットが揃い、かつ回 regress しにくくなっています。
- 影響範囲・注意点
影響範囲
- EncryptedConfiguration(例:
config/credentials.yml.encなど)から設定値を取得する際、存在しないキーを参照したときのKeyErrorメッセージが変わります。 - CombinedConfiguration はすでに同じ形式なので、新たな挙動変更はほぼ EncryptedConfiguration のみです。
- EncryptedConfiguration(例:
互換性
- 発生する例外の種類は引き続き
KeyErrorであり、アプリケーションコードの rescue ロジックに影響はありません。 - ただし、エラーメッセージの文字列に依存しているテストやロジック(
error.messageを文字列比較しているなど)がある場合は、メッセージ変更により失敗する可能性があります。
- 発生する例外の種類は引き続き
開発者体験
- どのキーが不足しているのかがメッセージに明示されるため、設定ミス(typo や環境ごとの差分)を特定しやすくなります。
- 参考情報 (あれば)
- 本 PR:
- Add "Missing key: ..." to
KeyErrormessage in EncryptedConfiguration (#56431)
- Add "Missing key: ..." to
- 関連 PR:
- CombinedConfiguration を導入した/変更した PR: https://github.com/rails/rails/pull/56404
→ こちらでKeyErrorメッセージに"Missing key: ..."を入れる仕様が導入されており、本 PRはその仕様を EncryptedConfiguration にも適用したものです。
- CombinedConfiguration を導入した/変更した PR: https://github.com/rails/rails/pull/56404
#56432 Fix assert_equal invocations in bug report templates
マージ日: 2025/12/22 | 作成者: @callmesangio
- 概要 (1-2文で)
Rails のバグ報告テンプレート内で使われているassert_equalの引数の順番ミス(actual, expected)を、正しい順番(expected, actual)に修正した PR です。テストコードの書き方のサンプルとして配布されているテンプレートの品質を整えるための、ドキュメント/ガイド系の小さな修正です。
- 変更内容の詳細
対象ファイルは以下の2つです。
guides/bug_report_templates/action_controller.rbguides/bug_report_templates/action_view.rb
これらはいずれも「バグ報告用テンプレート」であり、利用者が Rails のバグを再現するミニマルアプリ/スクリプトを書く際の雛形になっています。
Rails の assert_equal(Minitest ベース)の正しいシグネチャは:
assert_equal(expected, actual, failure_message = nil)ですが、テンプレート内で一部の呼び出しが逆順になっていました:
# 修正前(例)
assert_equal actual_value, expected_valueこれを以下のように修正しています:
# 修正後(正しい順序)
assert_equal expected_value, actual_value※PR 本文には具体的な行の実例はありませんが、「bug report templates における assert_equal 呼び出しの引数順を expected, actual に揃えた」という趣旨なので、上記のような変更が 2 ファイルで 1 箇所ずつ行われています。
この修正により、テンプレートに従って書かれたバグ報告用コードも、Minitest/Rails の慣習に沿った読みやすいテストになります。特に assert_equal の失敗メッセージは expected / actual を前提に出力されるため、順番が逆だとログの意味が分かりにくくなる問題を避けられます。
- 影響範囲・注意点
影響範囲
- 変更は ガイド用テンプレートのみ であり、Rails 本体の挙動やテストランナーの実装には一切影響しません。
- このテンプレートを元に新規でバグ報告用コードを書くユーザーに対して、より正しいテスト記述例が提供されるようになります。
注意点
- すでにテンプレートをコピペして使っている既存のバグ報告スクリプトは自動的には修正されません。もしテンプレート由来の
assert_equal(actual, expected)記述があれば、手動でassert_equal(expected, actual)に直すと、失敗メッセージの意味が分かりやすくなります。 - 新たに Rails のバグ報告テンプレートを参考にする際は、「
assert_equal(expected, actual)が正」と覚えておくと、Minitest や他の Rails ガイドと一貫したスタイルになります。
- すでにテンプレートをコピペして使っている既存のバグ報告スクリプトは自動的には修正されません。もしテンプレート由来の
- 参考情報 (あれば)
- Minitest / Rails の
assert_equalの一般的な使い方:rubyassert_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-2文で)
Railsガイド「tuning_performance_for_deployment.md」に記載されていた jemalloc のリンク先が、アーカイブされた旧リポジトリを指していた問題を修正し、現行の正しいリポジトリへのリンクに差し替えたPRです。ドキュメントのみの更新で、Rails本体のコードや挙動には変更はありません。変更内容の詳細
- 対象ファイル:
guides/source/tuning_performance_for_deployment.md - 変更内容は 1 行だけで、「jemalloc への参照リンク」の URL を修正するものです。
ガイドでは、アプリケーションのパフォーマンスチューニング/デプロイ時の最適化において jemalloc を利用することが推奨されており、その際に参照すべき jemalloc プロジェクトの GitHub リポジトリURLが掲載されています。
元々は「アーカイブ済みの旧リポジトリ」を指していたため、読者が「もうメンテされていないのでは?」と誤解する可能性がありました。
このPRでは、そのリンクを「現在メンテナンスされている(または正しい公式の)jemalloc リポジトリ」へ更新しています。
※実際の差分イメージ(URLのみ変更・擬似例):
- 詳細は jemalloc のリポジトリ https://github.com/old-archived/jemalloc を参照してください。
+ 詳細は jemalloc のリポジトリ https://github.com/jemalloc/jemalloc を参照してください。- 影響範囲・注意点
- 影響範囲:
- Rails ガイドを読む開発者が、最新かつサポートされている jemalloc のリポジトリに直接アクセスできるようになります。
- Rails 本体のコード、API、挙動、パフォーマンスには一切影響ありません(ドキュメントのみの変更)。
- 注意点:
- jemalloc の導入・設定手順そのものはこのPRでは変更されていないため、利用環境や OS ごとのビルド・導入方法は、リンク先の README / ドキュメントを改めて確認する必要があります。
- すでに古いリンクをブックマーク等している場合は、今後は新しいリンクを参照することが推奨されます。
- 参考情報 (あれば)
- 関連Issue:
Fixes #56388- ガイドがアーカイブ済みリポジトリを指していることへの報告・改善要望に対応したものです。
- jemalloc 公式リポジトリ(想定):
- https://github.com/jemalloc/jemalloc
※正確なURLは、マージ済みのガイド (tuning_performance_for_deployment.md) を確認してください。
- https://github.com/jemalloc/jemalloc
#56415 Ensure batched preloaded associations accounts for klass when grouping to avoid issues with STI
マージ日: 2025/12/21 | 作成者: @zzak
- 概要 (1-2文で)
- ActiveRecord の関連プリロード(
includesなど)のバッチ処理において、STI やclass_nameを伴う関連で正しいクラスが使われない問題を修正し、グルーピング時に「レコードのクラス情報」も考慮するようにした PR です。 - これにより、
SpecialAuthor/SpecialBookのような STI / テーブル共有モデルを含む複雑な関連をincludesしたときでも、期待どおりのモデルクラスで関連オブジェクトがロードされます。
- 変更内容の詳細
何が問題だったか
再現スクリプトでは、以下のような構造になっています:
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'
endAuthorとSpecialAuthorは同じauthorsテーブルを共有BookとSpecialBookは同じbooksテーブルを共有Author#origin_authorはSpecialAuthorを返すべき関連SpecialAuthor#booksはSpecialBookを返すべき関連
期待される動作:
result = Author.includes(books: [], origin_author: { books: [] }).last
result.origin_author.books.map(&:class)
# => [SpecialBook] となるべきしかし、バッチプリロードの実装上、関連レコードのグルーピング時に「クラス情報」が考慮されず、Book と SpecialBook が混ざるようなケースで誤ったクラスでインスタンス化される/紐づけられる可能性がありました。
特に、
- 同じテーブルを指す複数のモデル(STI や
self.table_name = ...) class_nameを指定したhas_many/belongs_to- ネストした
includes(origin_author: { books: [] }のような)
といった条件が揃うと、不正なクラスの関連オブジェクトが返ってしまう、というのが #56047 の問題です。
どこをどう直したか
主な修正箇所:activerecord/lib/active_record/associations/preloader/batch.rb
Preloader::Batch は、複数のレコードに対する関連をまとめてプリロードするとき、内部で「どのクエリでどのレコード群を拾うか」をグルーピングしてバッチ化する役割のクラスです。
今回の修正では、このグルーピングキーに「klass(モデルクラス)」を含めるようにしています。
イメージとしては:
- 変更前:
- 「テーブル名・関連名・条件」などだけでまとめてしまい、
Book用とSpecialBook用が同じグループになり得た
- 「テーブル名・関連名・条件」などだけでまとめてしまい、
- 変更後:
- これらに加えて「対象となるモデルクラス」もキーに含めることで、
Book用のバッチSpecialBook用のバッチ
が別々に扱われる
- これらに加えて「対象となるモデルクラス」もキーに含めることで、
結果として、
Author.includes(books: [], origin_author: { books: [] }).lastのようなクエリでも、
Author#books→BookSpecialAuthor#books→SpecialBook
が正しく区別されてプリロードされるようになります。
テストの追加・変更
変更されたテストファイル:
activerecord/test/cases/associations_test.rb- この PR で修正したケース(STI/テーブル共有+
includes+ネストした関連)をカバーするテストが追加されています。
- この PR で修正したケース(STI/テーブル共有+
activerecord/test/models/author.rb- テスト用
Authorモデルに、上記のようなorigin_authorやSpecialAuthor関連定義が追加されていると考えられます。
- テスト用
activerecord/test/schema/schema.rb- テストスキーマに、必要なカラム(例:
origin_author_id)が追加。
- テストスキーマに、必要なカラム(例:
activerecord/test/cases/json_serialization_test.rb- 関連プリロードの挙動が変わったことで JSON シリアライゼーションの期待値に影響が出る箇所を 1 行だけ調整しています(クラスや関連内容が変わったことによる微修正)。
- 影響範囲・注意点
影響範囲
- 対象となるのは ActiveRecord の関連プリロード(特に
includes/preload/eager_loadを経由するプリローダ)のバッチ処理部分 です。 - 主に影響するケース:
- STI または
self.table_name = ...で 同じテーブルを共有する複数モデル class_name:オプションを指定したhas_many/belongs_to- ネストした
includesでこれらを組み合わせている場合
- STI または
これらの組み合わせで「実際には不正なクラスが返っていた」場合は、それが修正されます。
互換性上の注意
- この変更は本来の期待動作(
class_nameなどに従って正しいモデルクラスを返す)に沿う修正です。 - しかし、もし既存コードが「誤っていた以前の挙動」に依存していた場合(例:
SpecialBookではなくBookが返ってくる前提のコード)、- 返るオブジェクトのクラスが変わることで、型チェックやポリモルフィックな処理に影響が出る可能性があります。
- 特に以下を行っているプロジェクトは、アップデート後の動作確認を推奨します:
- STI/テーブル共有モデルで複雑な
includesを多用している result.association.target.map(&:class)のようにクラスを前提としたテストを書いている
- STI/テーブル共有モデルで複雑な
パフォーマンス面では、グルーピングキーに klass 情報が増えただけなので、大きな劣化は想定されませんが、バッチがより細かく分かれることでクエリ回数が「厳密には」変化するパターンはあり得ます(特にこれまで誤ってまとめられていたケース)。
- 参考情報 (あれば)
- 該当 PR:
- 関連 issue:
- 関連コード(参考):
activerecord/lib/active_record/associations/preloader/batch.rb
→ 関連プリロードのバッチング・グルーピングロジックActiveRecord::Associations::Preloader周辺
→includes/preloadなどから呼び出されるプリローダのエントリポイント
#56416 Fix rerun command on CI for railties
マージ日: 2025/12/21 | 作成者: @byroot
- 概要 (1-2文で)
このPRは、Rails のrailtiesにおいて CI 上でrake test TESTOPTS="--rerun …"を利用した際の「失敗テストの再実行」機能が正しく動作するように修正したものです。テストレポーターと Rake タスクの連携を調整し、rerun 用のコマンドが壊れないようにしています。
- 変更内容の詳細
※実際の差分テキストは提示されていないので、ファイル名と行数から推測を交えて解説します。
2-1. railties/Rakefile の修正 (+4)
railties用のテストタスク(おそらくtest/test:unitsなど)で、CI から実行されるときに--rerun用の情報が正しく生成されるように、テストオプション (TESTOPTS) か環境変数の扱いが修正されています。- 実際には次のような処理が追加されている可能性が高いです:
Rails::TestUnit::Reporterが出力する「rerun コマンド」を壊さないよう、Rake 側でテストコマンドの引数を組み立てるロジックを調整- Bundler 経由 (
bundle exec) や engine/railties ディレクトリ固有のパスなど、CI 環境特有の実行パスを考慮
たとえばイメージとしては、以下のような修正が入っていることが多いです(擬似コード):
# 例: 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 #{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)が欠けていないこと、余計な環境依存情報が含まれないことなどを確認。
- CI で使うコマンドライン(おそらく
イメージとしては:
def test_rerun_command_on_ci
reporter = Rails::TestUnit::Reporter.new(...)
# 失敗テストを1個セットアップ
assert_equal expected_command, reporter.rerun_command
endのようなアサーションを増やしているはずです。
- 影響範囲・注意点
- 影響範囲:
railtiesのテストを CI 上で実行しており、失敗テストの rerun コマンド(多くの場合、テスト結果の末尾に表示される)を使っている開発者/メンテナが対象です。- Rails 自体を開発・メンテしている人、もしくは Rails を vendor して同様の test reporter を使っている環境で影響します。
- 期待される挙動の変化:
- 以前は CI 環境で出力された rerun コマンドをそのまま実行しても動かなかった、あるいはファイルパスや実行コマンドが不正だったケースが、今回の修正によりそのままコピペで再実行できるようになります。
- 注意点:
- 独自に
Rails::TestUnit::Reporterを継承・拡張している場合は、今回のコマンド生成ロジック変更と衝突しないか確認したほうがよいです。 - Rakefile 側でテストタスクをオーバーライドしているアプリ/エンジンでは、この PR と同様のパターンを踏襲していないと、rerun が正しく動かないことがあります。
- 独自に
- 参考情報 (あれば)
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-2文で)
ActiveSupport::CombinedConfigurationのコメント内に書かれていたサンプルコードの条件式が、実際には到達しない(無意味な)||を含んでいたため、正しい挙動を反映した形に修正されたドキュメント的変更です。実行コードの挙動そのものではなく、コメント中の例示コードの論理を整えるためのフォローアップ PR です。
- 変更内容の詳細
※ この PR は activesupport/lib/active_support/combined_configuration.rb のコメント中のサンプルコードのみを修正する小さな変更です。実行されるロジックには手を入れていません。
PR 説明の “The "||" would not be reached” から分かるように、コメント中の例では以下のような構造になっていたと考えられます(イメージ):
# 例(修正前・イメージ)
config.some_feature = combined_config[:flag] || default_value
# ただし上の行/前段の処理によって、実際には `|| default_value` に制御が到達しないあるいは、条件分岐そのものが論理的に死んでいるような形:
# 修正前(イメージ)
if condition
do_something
elsif other_condition || fallback_condition
# `fallback_condition` に論理上到達しない
endこの PR では、その「実際には評価されない || の右側」を含む例示をやめ、実際のコードパスを正しく反映した形のサンプルに書き換えています。
実コードとコメントの例が一致するようにし、「読み手がこの書き方を真似すると、意図しない/意味のない条件式を書くことになる」状態を解消するのが目的です。
(具体的な1行差分は、「コメント中の || を含む条件式 → シンプルな条件 or 別の正しい例」に置き換えた、+2/-2 行の修正です。)
- 影響範囲・注意点
- 実際のクラスの挙動(
ActiveSupport::CombinedConfiguration)には変更はありません。 - 影響があるのは コメントを読んでコードを書く人間の理解 のみです。
- 以前のコメントを見て、到達しない
||を含んだ条件式や設定コードを書いていた場合、そのロジックは本来意図したものではない可能性があります。
- 以前のコメントを見て、到達しない
- この PR は前回の PR (#56404) のフォローアップなので、
CombinedConfiguration周辺を利用している場合は、#56404 の内容も含めて読み直すと意図がつかみやすいです。
- 参考情報 (あれば)
- 対象ファイル:
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-2文で)
bin/rails notesコマンドが.cssファイル内のTODO/FIXME/OPTIMIZEなどを正しく検出できていなかったバグを修正した PR です。CSS のコメント形式/* ... */に対応する正規表現へ変更し、期待どおりスタイルシート内のノートを拾えるようにしています。
- 変更内容の詳細
問題点
bin/rails notesは、ソースコード中の以下のようなコメントを拾って一覧表示する機能です:# TODO: ...// FIXME: ...
- しかし CSS ファイルに対しては、JavaScript と同じ
//コメント形式を前提とした正規表現で検索していたため、一般的な CSS コメント形式である/* ... */の中の TODO などが検出されていませんでした。
例: これまでは次のようなコメントが無視されていた:
/* TODO: adjust spacing */
.button {
margin: 10px;
}bin/rails notes を実行しても、この TODO は出力されない状態でした。
修正内容
対象ファイル:
railties/lib/rails/source_annotation_extractor.rbrailties/test/commands/notes_test.rb
CSS 用正規表現の変更
CSS 用のコメント抽出ロジックで使用する正規表現が、CSS のコメント記法に合わせて修正されました。
新しい正規表現(Ruby コード上のイメージ)は以下です:
regexp = /\/\*\s*(#{tag}):?\s*(.*?)(?:\*\/)?$/ポイント:
\/\*- CSS コメント開始
/*を表現。
- CSS コメント開始
\s*- 開始直後の任意の空白を許可。
(#{tag}):?tagはTODO/FIXME/OPTIMIZEなど。- 後ろのコロン
:は任意(TODO: xxxとTODO xxxの両方を許可)。
\s*(.*?)- 実際のコメント内容(ノートの本文)をキャプチャ。
(?:\*\/)?- 行末に
*/があればマッチするが、表示時には含めない(非キャプチャかつ任意)。
- 行末に
$- 行末までを対象とする。
この結果、以下のようなパターンが正しく拾われます:
/* 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 ファイル内の /* ... */ コメントからノートが抽出されることを確認するテストが追加されています。
これにより、今後のリグレッション(後戻りバグ)が防止されます。
- 影響範囲・注意点
- 影響範囲:
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 { ... }
- 参考情報 (あれば)
- 該当ファイルの役割:
railties/lib/rails/source_annotation_extractor.rbは、bin/rails notesで使用される「ソースコードから注釈コメントを抽出する」ユーティリティクラスで、ファイル種別ごとのコメント形式に応じた正規表現を定義しています。
- 関連ドキュメント:
- Rails Guides: Command Line →
rails notes
(この PR 後もコマンドの使い方自体は変わらず、CSS サポートがより正確になった形です。)
- Rails Guides: Command Line →
#56410 Add block support to ActionController::Parameters#merge
マージ日: 2025/12/21 | 作成者: @Saidbek
- 概要 (1-2文で)
ActionController::Parameters#mergeにブロック引数のサポートが追加され、Hash#mergeやParameters#merge!と同じインターフェースで使えるようになりました。これにより、パラメータ同士のマージ時にキー競合の解決ロジックをブロックで記述できます。
- 変更内容の詳細(あればサンプルコードも含めて)
何が変わったか
ActionController::Parameters#mergeが、Hash#mergeと同様にブロックを取れるように実装変更。- すでにブロック対応していた破壊的メソッド
Parameters#merge!との API 不整合が解消。 CHANGELOGにこの仕様追加が記載され、テスト (parameters_permit_test) も追加。
挙動イメージ
従来(このPR前)は、merge にブロックを渡しても無視される/想定通り動かない状態でしたが、この PR 後は以下のように書けます:
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 でも可能になった形です。
- 影響範囲・注意点
互換性
- 既存コードで
mergeにブロックを渡していなければ挙動は変わりません。 - これまで「ブロックを渡しても無視されていた」コードがあれば、今後はブロックが実際に評価されるため、意図せぬ挙動変化が起きる可能性があります(ただし通常、そのようなコードはバグ寄りなので、多くは望ましい変化と考えられます)。
- 既存コードで
Strong Parameters の性質
ActionController::Parametersは Strong Parameters のオブジェクトなので、permit状態(permitted / not permitted)やネスト構造への影響は従来のmergeと同様です。- ブロックはあくまで「キーが重複したときの値をどう決めるか」を制御するものであり、パラメータの許可・非許可判定ロジックを変えるものではありません。
一貫した API
- これで
Hash#merge/Hash#merge!、Parameters#merge/Parameters#merge!すべてが、「非破壊・破壊」と「ブロック対応可否」の点で直感的に一致します。 - ライブラリやアプリ側で
HashとActionController::Parametersを抽象的に扱う場合に、分岐を減らせます。
- これで
- 参考情報 (あれば)
- 該当 PR: https://github.com/rails/rails/pull/56410
- 関連コメント(以前からの要望・背景): https://github.com/rails/rails/pull/45369#issuecomment-1736325181
- 類似仕様: Ruby
Hash#mergeドキュメント
https://docs.ruby-lang.org/ja/latest/method/Hash/i/merge.html
#56404 Add Rails.app.creds to provide combined credentials lookup in ENV and encrypted file
マージ日: 2025/12/20 | 作成者: @dhh
- 概要 (1-2文で)
Rails 8系(?) に、ENV と encrypted credentials を意識せずに一元的に参照できるRails.app.credsが追加されました。これにより、鍵の保管場所を ENV と credentials の間で移行・併用してもアプリケーションコードを変更せずに済む設計になります。
- 変更内容の詳細
新しく追加された概念・クラス
1) Rails.app.creds
- ENV と encrypted credentials をラップした「統合クレデンシャル」インターフェース。
- 検索順序は ENV → Rails.app.credentials。
- API は
require/optionの2つで、credentials と同じ感覚で使えます。
サンプル:
# 必須値: なければ例外 (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 の階層を __(ダブルアンダースコア)でつなげた形式で解決されます:
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を使う。
サンプル:
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の中身に使われている実装。
カスタム構成例:
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 の振る舞いは以下のようなイメージです:
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で統一的に同じ呼び出しスタイルを使えるようになります。
- 影響範囲・注意点
影響範囲
- 新 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_host→DB_HOST:aws, :access_key_id→AWS__ACCESS_KEY_ID
requireは値が見つからないと例外(KeyError想定)になるので、アプリ起動時・設定ロード時に早期に失敗させたい値に用いると安全です。optionは nil や default を許容するため、必須/任意の設計を意識的に分ける必要があります。- 複数バックエンドを組み合わせた場合、先に指定したバックエンドが常に優先されます。優先度の誤設定に注意してください。
- 参考情報 (あれば)
- 新規クラス・API:
Rails.app.creds(ActiveSupport::CombinedConfigurationベース)Rails.app.envs(ActiveSupport::EnvConfigurationベース)ActiveSupport::CombinedConfigurationActiveSupport::EnvConfiguration
- 関連テスト:
activesupport/test/combined_configuration_test.rbactivesupport/test/env_configuration_test.rbactivesupport/test/encrypted_configuration_test.rbrailties/test/application/creds_test.rbrailties/test/application/envs_test.rb
これらのテストコードを読むと、優先順位の詳細やエラー時の挙動、ENV 名の解決ルールなどを具体的に確認できます。
#56409 Remove support for psych < 4
マージ日: 2025/12/20 | 作成者: @byroot
- 概要 (1-2文で)
Psych 4未満(古いYAMLライブラリ)への対応コードをRails本体から削除し、「Psych 4以上のみサポート」に整理したPRです。これによりYAML関連の分岐や互換コードが減り、実装とテストがシンプルになっています。
- 変更内容の詳細
全体方針
- Rails側で「psych < 4」を考慮した条件分岐・ワークアラウンドを削除
- Gem仕様・テスト群を「psych 4が前提」の世界に揃えることで、不要なチェックを廃止
2-1. 依存関係まわり
Gemfile.lockactivesupport/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 endruby# 変更後イメージ(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.rbactiverecord/test/cases/attribute_methods_test.rbactiverecord/test/cases/connection_adapters/schema_cache_test.rbactiverecord/test/cases/json_shared_test_cases.rbactiverecord/test/cases/locking_test.rbactiverecord/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.rbactivemodel/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やオプションの明示指定を削除
イメージ:
# 完全にイメージ
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.rbactivesupport/test/core_ext/time_with_zone_test.rbactivesupport/test/time_zone_test.rb
ここもYAMLシリアライズ(time/durationのYAML表現)やロード結果がPsych 4で安定している前提に書き換え。
例えば:
"!ruby/object:ActiveSupport::TimeWithZone"の表現や内部フィールド名が、Psych 3/4で違っていたことに由来する「バージョン条件付きアサート」を削除、あるいは1パターンに統一していると考えられます。
- 影響範囲・注意点
影響範囲
Railsを最新に上げるアプリケーション全般
- このPRが含まれるRailsバージョン以降では、「Psych 4以上」が前提になります。
- システムのRuby環境でPsych 3系などを使っている場合、Bundlerの解決時にエラーになる、あるいは動作時に非対応な呼び出しが行われる可能性があります。
YAMLを直接扱うコード・独自拡張
- RailsのYAMLまわり(ActiveRecordのYAMLカラム、schema cache、configファイル、暗号化設定など)は心理的に「Psych 4のsafe_load挙動」を前提に安定するため、挙動が僅かに変わるケースがあります。
- ただし、このPR自体は「古いPsychへの互換コード削除」がメインであり、新たな仕様変更ではなく「Rails側の前提バージョンの明確化」に近いです。
テストコード
- Psych 3/4差を吸収していたテストは削除されているので、「CIがPsych 3でたまたま動いていた」ような環境は今後想定されなくなります。
- プロジェクト内でRailsテストを参考にしている場合、YAML表現を比較するような箇所の期待値をPsych 4に合わせる必要があるかもしれません。
注意点 / マイグレーション的観点
RailsをこのPRを含むバージョンにアップデートする場合:
Gemfileで明示的に古いPsychを指定していないか確認- システムにバンドルされているRubyの標準Psychバージョンが古すぎないか確認
- YAMLを直接扱っているアプリケーションコード(特に
YAML.safe_load/Psych.safe_load)で、Railsの変更に合わせて「Psych 4前提のオプション指定」に整理しておくと安全
特に、「permitted_classes」や「aliases: true/false」などを利用している場合、Psych 4での推奨シグネチャを確認しておくとよいです。
- 参考情報 (あれば)
- 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-2文で)
Rails 自身のテストスイートで使っているminitest-mockを「必須依存関係(hard dependency)」に戻す PR です。以前行った「soft dependency 化」の変更をリバートし、Rails テスト実行時にminitest/mockを必ず読み込む形に統一しています。
- 変更内容の詳細
対象ファイル:
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 を前提とする構成に戻っています。
差分イメージ(概念的なもの):
# 変更前(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 に依存するようにしています。
- 影響範囲・注意点
- 影響範囲:
- Rails 本体の「自前テストスイート」に限定されます。
- PR 説明にもある通り、
ActiveSupport::Testing::MethodCallAssertionsは Rails 開発時の内部テストでのみ使われ、一般のアプリケーションコードが直接この「minitest-mock の hard dependency 化」の影響を受けることは基本的にありません。
- gem 利用者への実務的な影響:
- Rails を gem として利用し、自分のアプリ側で
ActiveSupport::Testing::MethodCallAssertionsを直接使っていない限り、動作への変化はほぼありません。 - Rails コアのテストを実行する開発者は、引き続き
minitest-mockをインストールしておく必要があります(=開発環境でbundle exec rake testなどを回す場合に必須)。
- Rails を gem として利用し、自分のアプリ側で
- 注意点:
- もし Rails の内部テストモジュールを流用している特殊なケースがあれば、
minitest/mockが存在しない環境ではテストが落ちる/ロードに失敗する可能性があります。その場合はminitestを通常通り導入すれば問題は解消します。 - 「minitest 自体は使うが
minitest/mockはロードしたくない」といった特殊構成は考慮されなくなります。
- もし Rails の内部テストモジュールを流用している特殊なケースがあれば、
- 参考情報 (あれば)
- 対応するフォローアップ元 PR:
- https://github.com/rails/rails/pull/56207
→minitest-mockを soft dependency にしようとした変更
- https://github.com/rails/rails/pull/56207
- 本 PR:
- Revert されたコミット:
c5c678a67a09b1b83f0f7616290512b48bf2d071
minitest/mock公式ドキュメント:
#53335 Deprecate built-in snekaers adapter
マージ日: 2025/12/19 | 作成者: @dixpac
- 概要 (1-2文で)
Rails の Active Job に含まれていた built-in のsneakersアダプタが非推奨(deprecate)扱いになり、代わりに後継であるkicksgem の利用が推奨されるようになりました。これに伴い、非推奨警告の追加と関連テスト、CHANGELOG の更新が行われています。
- 変更内容の詳細
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 に非推奨警告が追加されています。
おおよそ以下のようなコードが追加されていると考えられます(内容イメージ):
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 メッセージが出るようになっています。
ポイント:
- 「
sneakersはkicksに移行した」という情報を明示 - 今後
kicksgem 側のアダプタに移行するように促す文言
2-3. テストの追加
activejob/test/cases/adapter_test.rb に、sneakers アダプタの非推奨警告を検証するテストが追加されています。
想定されるテスト内容のイメージ:
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 で検証する形になっています。
- 影響範囲・注意点
3-1. 現在 sneakers アダプタを使っているアプリへの影響
config.active_job.queue_adapter = :sneakersなどでsneakersアダプタを指定している場合、Rails の起動時やジョブ実行時に「非推奨です」という deprecation warning がログに出るようになります。- ただし、この PR 時点では「削除」ではなく「非推奨宣言」のみなので、即座に動かなくなるわけではありません。
3-2. 今後の推奨対応
sneakersgem 自体がkicksに移管されているため、将来的なメンテナンス性や互換性を考えると、kicksgem への移行を検討すべきです。- Rails 本体に
kicks用のアダプタが組み込まれるか、もしくは外部 gem として提供されるかは別問題ですが、少なくとも:sneakersに依存した設計や設定をkicksに合わせて見直す- 本番環境での AMQP/RabbitMQ 接続設定を
kicks仕様に合わせて移行 といった対応を事前に検討しておくとスムーズです。
3-3. CI / ログ監視への影響
- deprecation warning をエラー扱いしている CI 設定(例:
RAILS_ENV=testで deprecation を raise する設定)では、テストが落ちる可能性があります。 - そうした場合は一時的に deprecation を許容する設定に変更するか、早期に
kicksへの移行を行う必要があります。
- 参考情報
- PR 本体:
Deprecate built-in snekaers adapter(#53335) - 連動 Issue: https://github.com/rails/rails/issues/52929
sneakersgem の開発移行案内:
https://github.com/jondot/sneakers?tab=readme-ov-file#sneakers-has-a-new-home-and-a-new-name- 新ホーム:
kicksgem:
https://github.com/ruby-amqp/kicks
この PR は「今後 sneakers は Rails/Active Job の一級市民ではなくなる」ことを公式に示す第一段階なので、sneakers を継続利用しているプロジェクトでは、今のうちから kicks への移行計画を立てるのが安全です。
#56207 Bump minitest to V6
マージ日: 2025/12/19 | 作成者: @zenspider
- 概要 (1-2文で)
Rails のテスト周りの依存関係として使っている minitest を 6.0 以上に引き上げ、あわせて minitest-mock を追加し、それに伴って発生したテスト関連の不整合や不具合を修正した PR です。主に ActiveSupport のテスト補助モジュールと並列実行周り、およびガイド用テストヘルパに対する調整が含まれます。
- 変更内容の詳細
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 名やエラークラス、メッセージ仕様などが微妙に変わっているため、それに適合するような修正です。
- 例: 期待されるメソッドが何回呼ばれたか、どの引数で呼ばれたかを検証するような補助メソッド (
おおまかなイメージ(※擬似コードレベルの例):
# 旧来: 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 環境で問題なく動くよう配慮した修正です。
- 影響範囲・注意点
プロジェクト側における影響:
Rails 本体のテスト:
- Rails 自体を開発している場合、
bundle exec rake test等で走るテストは minitest 6 前提になります。 - 自前で minitest の内部 API に依存している(例: 独自のアサーションを minitest のプライベートメソッドに依存して実装している)場合は、5→6 で動かなくなる可能性があります。
- Rails 自体を開発している場合、
Rails を利用しているアプリケーション:
- 通常の Rails アプリは、この PR によって直接的に「アプリコード」が壊れる可能性は低いです。
- ただし、以下のようなケースは注意が必要です:
- Gemfile で
minitestのバージョンを明示ピン止めしている- Rails が 6 系を要求するようになった場合、依存関係の競合が起きる可能性があります。
- minitest 5 系の API を前提にした独自のヘルパーやサポートコードを大量に書いている
- 例:
Minitest::Testの内部実装や、deprecate 済みメソッドに直アクセスしている場合。
- 例:
- Gemfile で
対応方針の目安:
- Rails のテストヘルパや
test_helper.rbでrequire 'minitest/autorun'以外の独自設定をしている場合は、minitest 6 向けに問題なく動作するかを確認すると安心です。 - モックやスタブを Rails 独自ではなく minitest の機能として使っている場合、
minitest-mockが別 gem として扱われることを把握しておくと、CI や開発環境構築でのトラブル防止になります。
- 参考情報 (あれば)
- minitest 6 系の変更点・マイグレーションの参考:
- リポジトリ: https://github.com/minitest/minitest
- CHANGELOG や README に 5→6 の主な変更が記載されています。
- Rails のテスト周りの公式ガイド:
- https://guides.rubyonrails.org/testing.html
(本 PR で使われているguides/test/test_helper.rbの内容も、ここからリンクされるガイドに対応しています。)
- https://guides.rubyonrails.org/testing.html
#56403 Add Rails.app alias for Rails.application
マージ日: 2025/12/19 | 作成者: @dhh
- 概要 (1–2文で)
Rails にRails.applicationの短縮エイリアスとしてRails.appが追加され、コアコード・ジェネレータ・ガイド内の一部参照もそれに合わせて置き換えられました。これにより、Rails.application.credentialsなどの長くなりがちな呼び出しが、Rails.app.credentialsのように簡潔に書けるようになります。
- 変更内容の詳細
2-1. 新しいエイリアスメソッドの追加
railties/lib/rails.rb に以下のような形でエイリアスが追加されています(概念的にはこんな感じ):
module Rails
class << self
# 既存
def application
@application
end
# 新規追加
alias_method :app, :application
end
endこれにより、フレームワーク全体で次のような記述が可能になります。
Rails.application.credentials # 従来
Rails.app.credentials # 新しい書き方2-2. 既存コードの Rails.application → Rails.app への置き換え
複数のファイルで Rails.application を Rails.app に置き換えています。主なものは以下です。
Action Mailbox コントローラ
actionmailbox/app/controllers/action_mailbox/* でアプリケーションの設定・資格情報などを参照している箇所が更新されています。
例(イメージ):
# 変更前
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 がそれぞれ更新されています。
# 変更前
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.ttconfig/environments/production.rb.ttconfig/storage.yml.tt
例:
# config.ru.tt
# 変更前
run Rails.application
# 変更後
run Rails.app# 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.mdguides/source/active_storage_overview.md
例:
# 変更前 (ガイド)
Rails.application.credentials.dig(:mailgun, :api_key)
# 変更後
Rails.app.credentials.dig(:mailgun, :api_key)CHANGELOG
railties/CHANGELOG.md に Rails.app 追加に関する項目が追記されています。
これにより、この変更が公式にパブリック API として意図されていることが示されています。
- 影響範囲・注意点
後方互換性
Rails.applicationは引き続き利用可能であり、今回の変更はあくまで「エイリアス追加」と「内部・テンプレートの利用スタイル変更」です。既存アプリはそのまま動作します。
コードスタイル / ベストプラクティス上の影響
- 今後は Rails 本体やガイドに倣って、
Rails.applicationよりもRails.appを使うのが推奨される方向になると考えられます。 - 特に
credentialsやconfig、routes等と組み合わせた長いチェーンを短く書けるメリットがあります:rubyRails.application.credentials.dig(:aws, :access_key_id) Rails.app.credentials.dig(:aws, :access_key_id)
- 今後は Rails 本体やガイドに倣って、
ライブラリ / ジェム作者への影響
- gem 内で Rails アプリケーションオブジェクトにアクセスする部分は、従来どおり
Rails.applicationでも、新しいRails.appでも動作します。 - ドキュメントや README に掲載しているサンプルコードを更新する場合は、新しい書き方 (
Rails.app) を採用すると、将来的な公式ドキュメントとの一貫性が保ちやすくなります。
- gem 内で Rails アプリケーションオブジェクトにアクセスする部分は、従来どおり
自動生成コードへの影響
- 新規に
rails newで作るアプリや、一部ジェネレータが吐き出すファイルにRails.appが含まれるようになります。 - 古いテンプレートを前提にしたドキュメントや記事との表記ゆれが生じる可能性がありますが、機能的な問題はありません。
- 新規に
- 参考情報
- PR: https://github.com/rails/rails/pull/56403
- 変更概要:
Rails.appはRails.applicationのシンタックスシュガーであり、特にRails.app.credentialsなどの長い構文を読みやすくすることを目的とした追加です。
#56398 Remove duplicate unique index for token migrations
マージ日: 2025/12/18 | 作成者: @zzak
- 概要 (1-2文で)
Rails の migration / model generator が「token」カラムに対して重複した unique index を作ってしまう問題を修正し、生成されるマイグレーションから不要な重複 index を取り除いた PR です。これにより、add_indexとindex: { unique: true }の二重指定などによるエラーや冗長な定義が解消されます。
- 変更内容の詳細
※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 を定義する という形に整理されています。
- すでに
イメージとしては、以前は以下のように「二重」になっていたパターンを:
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 から取り除かれた」ことがわかるようになっています。
- 影響範囲・注意点
- 対象:
rails g model,rails g migration,rails g scaffoldなど、Active Record の migration / model generator 全般- 特に
tokenカラム(例:token:stringやtoken:string:uniq)を含む generator の利用ケース
- 期待される影響:
- 新たに generator を使って作るマイグレーションでは、
token用の unique index が 1 つだけになる。 - これまでのように、migration 実行時に
- 「同名 index の作成に失敗する」
- 「DB 側で一意制約が重複している」 といったエラーが起きづらくなる。
- 新たに generator を使って作るマイグレーションでは、
- 既存アプリへの影響:
- 既に作成済み・マージ済みの古いマイグレーションや DB の index は自動では変更されません。
- もしすでに「token に対して重複 index がある」場合は、手動で古い index を削除するマイグレーションを追加する必要があります。
- マルチ DB / DB ごとの制約:
- PostgreSQL, MySQL, SQLite いずれでも「同じカラムへの同じ unique index 名」の重複は一般にエラーの原因になりやすいため、今回の修正はどの DB を使っていても有益です。
- 参考情報 (あれば)
- 関連 Issue:
- #56038: token 用の重複 unique index に関する報告
- #56131: 同様の不具合あるいは改善要望の追跡
- 影響を確認したい場合のチェックポイント:
db/migrate以下でtokenを追加/作成しているマイグレーションを検索- 同じマイグレーションファイル内に
t.string :token, index: { unique: true }とadd_index :..., :token, unique: trueの両方が無いか確認
- 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-2文で)
ActionController::Parameters#fetch が、存在しないキーに対してブロックを呼ぶ際に「キー自体」を引数として渡すように変更され、Ruby の Hash#fetch と同じインターフェースになりました。Strong Parameters を Hash 互換として扱うコードの挙動が、より一貫したものになります。
- 変更内容の詳細
何が変わったか
従来:
params = ActionController::Parameters.new
params.fetch(:user) { |value| p value }
# => ブロックには「何も渡されない」 (引数が nil になる / そもそも引数を取れない前提で書いていることが多い)この PR 後:
params = ActionController::Parameters.new
params.fetch(:user) { |key| p key }
# => :user がブロック引数 key に渡される (Hash#fetch と同じ)Ruby 標準ライブラリの Hash#fetch は以下のような仕様です:
h = {}
h.fetch(:foo) { |key| "missing: #{key}" }
# => "missing: foo"今回の変更により、ActionController::Parameters#fetch も同様に、キーが存在しない場合にブロックへ「キー」を渡します。
実装面での変更ポイント
※実際のコードは概略ですが、イメージとしては:
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 にも、この動作変更が明記されています。
- 影響範囲・注意点
影響しうるコード
ActionController::Parameters#fetchにブロックを渡しているコードで、
「ブロック引数の数や中身」に依存しているもの。例: これまでは引数なし想定で書いていた場合
rubyparams.fetch(:page) do # 引数を取らない前提 1 end→ このようなコードは、Ruby のブロック引数の仕様上そのままでも動きますが、
「キーが引数として渡されるようになった」という意味で挙動が変わっています。
(ただし引数を定義していないブロックは、渡された引数を単に無視するので、多くの場合は実害なし)Strong Parameters を Hash と同様に扱うライブラリ / アプリケーションコードで、
Hash#fetch 相当の動作を期待していたが、うまく動いていなかったケースが「期待通り」になる。例:
rubydef 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 との一貫性が強化されるメリットが大きい変更です。
- 参考情報 (あれば)
- PR: https://github.com/rails/rails/pull/56400
- Ruby ドキュメント: Hash#fetch
- 対象クラス:
ActionController::Parameters(Strong Parameters)- Rails ガイド: Strong Parameters
#56386 Consolidate use of Ruby versions in templates
マージ日: 2025/12/18 | 作成者: @jeromedalbert
- 概要 (1-2文で)
Rails の各種テンプレート内で使われている Ruby バージョン指定の方法を統一し、「どの値をどの用途で使うか」を整理した PR です。version_manager_ruby_versionとGem.ruby_versionを状況に応じて使い分けることで、テンプレート生成時の Ruby バージョン指定がより一貫性のあるものになりました。
- 変更内容の詳細
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を使うように統一しています。
対象ファイルの一つは以下:
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 関連)
対象:
railties/lib/rails/generators/rails/app/templates/config/deploy.yml.ttこのテンプレートは、config/deploy.yml 生成時に、 Docker 関連の説明コメントとして「RUBY_VERSION ARG を上書きする場合」の例を含んでいます。
ここでの変更内容:
- コメント内の Ruby バージョン表記を、
RUBY_VERSIONDockerARGのデフォルトと同様にGem.ruby_versionを使った値にするよう揃えました。
理由:
- Docker イメージ名(例:
ruby:3.3.1)では、ruby-3.3.1のような「qualified」な形式は使用しません。 - そのため、Dockerfile ではすでに
Gem.ruby_versionを使っており、この deploy.yml のコメントもそれに合わせて「Docker の世界で実際に使う形式」に統一しています。
Dockerfile 側の該当箇所(参照用):
ARG RUBY_VERSION=<%= Gem.ruby_version %>この PR で deploy.yml のコメントでも同じ値を前提とするようにしたため、
- Dockerfile と deploy.yml のサンプル/コメントが矛盾しない
- 利用者が「どのバージョン形式を使えばよいか」で迷いにくくなる
といった効果があります。
- 影響範囲・注意点
対象は 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 標準の流儀と揃えやすくなります。
- 「バージョンマネージャ向け」は
- 参考情報 (あれば)
- 該当メソッド:
version_manager_ruby_version(Rails::Generators::AppBase内)
https://github.com/rails/rails/blob/89dac2ad4d8ae10abd9419eb2bb0caf654e3b21e/railties/lib/rails/generators/app_base.rb#L774
- 関連 PR:
version_manager_ruby_versionを導入した PR: https://github.com/rails/rails/pull/56365
- Dockerfile テンプレート(
Gem.ruby_versionの利用箇所):
#56389 [Bug] Fix input[type=file] accept attribute array value separator as ,
マージ日: 2025/12/18 | 作成者: @bogdan
- 概要 (1-2文で)
file_fieldヘルパで複数指定されたaccept属性値の区切り文字が、仕様どおりカンマ区切り(,)になるように修正したバグフィックスです。これにより、input[type="file"]のaccept属性が HTML 仕様およびブラウザ実装に沿った形で出力されます。
- 変更内容の詳細
何が問題だったか
Rails の file_field ヘルパで accept に配列を渡した場合、これまで値が空白区切りで結合されていました。
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 属性が配列で渡された場合の結合方法を 空白からカンマ(,)に変更しました。
擬似コードイメージ:
# 変更前(イメージ)
accept_value = Array(options[:accept]).join(" ")
# 変更後(イメージ)
accept_value = Array(options[:accept]).join(",")これにより、同じコードは次のように出力されます。
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 も更新され、挙動変更として明示されています。
- 影響範囲・注意点
- 対象となるヘルパ:
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 がある場合、空白区切り前提のロジックがあると挙動が変わる可能性があります(カンマ区切りに対応しているか確認推奨)。
- 参考情報 (あれば)
- MDN:
acceptattribute
https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/accept - 関連ファイル:
actionview/lib/action_view/helpers/tags/file_field.rbactionview/test/template/form_helper_test.rb
#56387 [ci skip] Document top level bin/test
マージ日: 2025/12/17 | 作成者: @jeromedalbert
- 概要 (1-2文で)
Rails リポジトリの最上位ディレクトリに追加されたbin/testの使い方を、コントリビューションガイドに追記したドキュメント専用のPRです。サブディレクトリにcdしなくてもテストを実行できることを公式に説明し、コントリビューターのテスト体験を改善しています。
- 変更内容の詳細
対象ファイル: guides/source/contributing_to_ruby_on_rails.md に約18行の説明が追加されています。主な内容は次の通りです。
a. トップレベル bin/test の紹介
Rails リポジトリのルート直下にある bin/test を使うことで、どのサブプロジェクト(activesupport, activerecord, actionpack, railties など)のテストも、ルートディレクトリから実行できることが明記されました。
従来:
cd activerecord
bin/test test/cases/validations_test.rbドキュメントに追記された新しい推奨方法の例:
# Rails リポジトリのルートから
bin/test activerecord test/cases/validations_test.rbのように、
- 「どのフレームワークか」を第1引数に指定
- それ以降にテストファイルやパターンを指定
という形で使えることが説明されています。
b. 典型的な使用例
ガイドには、以下のような利用パターンが含まれている可能性が高いです(PRの意図と既存ドキュメント構成からの推測):
単一フレームワークの全テストを走らせる:
bashbin/test activesupport特定のテストファイルだけを走らせる:
bashbin/test activesupport test/time_with_zone_test.rbパターン指定・行番号指定(既存の
bin/testのインターフェイスをそのまま転送):bashbin/test actionpack test/controller/request_test.rb:42既存の
bin/testオプション(-nでテスト名フィルタなど)も、そのまま利用可能であることが説明されている可能性があります:bashbin/test activerecord test/cases/validations_test.rb -n test_presence_validation
c. コントリビューションフローとの関連づけ
コントリビューションガイドの「テストの実行方法」もしくはそれに相当するセクションに、このトップレベル bin/test の利用方法が追記されています。
これにより、初めてコントリビュートする開発者でも、
- 「どのディレクトリでどの
bin/testを叩けばいいのか」 - 「複数のコンポーネントのテストをどう整理して実行するのか」
といった迷いが減るような記述になっています。
- 影響範囲・注意点
影響範囲
- コード変更はなく、ガイド文言のみの追加です。
- ただし、トップレベル
bin/testが「公式に」推奨される形になるため、今後のブログ記事や他のドキュメント/チュートリアルでも、このスタイルが紹介されやすくなります。 - Rails コア開発やプラットフォーム(CI 設定、社内向け開発ガイドなど)でのテスト実行手順の標準化に影響する可能性があります。
注意点
- トップレベル
bin/test自体は別PR(#56269)で導入されており、このPRはその存在を説明するだけです。
そのため、実際の挙動は #56269 側の実装に依存します。 - 既存の「各サブディレクトリに
cdしてbin/testを叩く」方法も依然として有効なはずですが、今後はルートのbin/testの使用が説明上のデフォルトになることが想定されます。 - ローカル環境で
bin/testを利用する際は、bundle installやyarn installなど、各コンポーネントが期待する依存関係が満たされている必要があります(この点はコントリビュートガイド全体の前提)。
- トップレベル
- 参考情報 (あれば)
- この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-2文で)
ActionController::Live でスレッド間に共有される「実行コンテキスト(Execution State)」から、特定のキーを除外できる設定が追加されました。これにより、DB の writer/replica 切り替えなど「スレッド間で共有したくないコンテキスト」をアプリ側で制御できるようになります。
- 変更内容の詳細
背景: ActionController::Live と Execution State 共有
ActionController::Live はストリーミング処理などでコントローラのアクションを別スレッドで実行します。このとき、ActiveSupport::IsolatedExecutionState に格納された「実行コンテキスト」がスレッド間で共有されます。
しかし、DB 接続コンテキスト(例: connected_to による writer/replica の切り替え)まで共有されると、次のような問題が起こり得ます:
- メインスレッドでは writer 接続に切り替えたつもりが、Live スレッドでもその状態が引き継がれ、想定外に writer を使い続ける
- もしくは、replica 用の接続が Live スレッドにも引き継がれ、書き込みが失敗する・不整合が起きる など
この PR は、そうした「共有したくないキー」を除外できるようにするものです。
新しい設定項目
アプリケーション全体での設定
config/application.rb 等で、以下のように設定できます:
# config/application.rb
class Application < Rails::Application
# ... 略 ...
config.action_controller.live_streaming_excluded_keys = [:active_record_connected_to_stack]
endlive_streaming_excluded_keysは シンボルの配列 を受け取ります。- ここで指定したキーは、
ActionController::Liveが内部でスレッドを切り替える際に、IsolatedExecutionStateから「コピーしない(共有しない)」対象になります。 - サンプルでは
:active_record_connected_to_stackを除外しています。これは Active Record がconnected_toなどで管理している「どの DB role/接続プールを使っているか」のスタック情報です。
コントローラごとの細かい制御
アプリ全体ではなく、特定の Live コントローラだけで除外したい場合は、コントローラ側で設定できます:
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_keysがActionController::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 には、この設定が正しく読み書きできるかを確認するテストが追加されています。
- 影響範囲・注意点
デフォルトでは、何も除外されない
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 アプリでは意識不要)。
- 参考情報 (あれば)
- 対応 Issue: https://github.com/rails/rails/issues/52906
ActionController::Live と DB 接続(writer/replica 切り替え)の問題についての議論がされています。 - 関連ソース
actionpack/lib/action_controller/metal/live.rblive_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-2文で)
protect_from_forgery prepend: trueを使った場合に、本来問題ないのに「非推奨警告」が誤って出てしまうバグを修正した PR です。verify_authenticity_tokenとverify_request_for_forgery_protectionのコールバック順序を調整し、正しく deprecation 判定が行われるようにしています。
- 変更内容の詳細(サンプルコード含む)
問題の背景
Rails 7.2 系(PR #56369)で後方互換性のために以下 2 つの before_action が登録されるようになりました。
before_action :verify_authenticity_token, :verify_request_for_forgery_protection, optionsprotect_from_forgery prepend: true を使うと、Rails は内部的に prepend_before_action を利用するため、以下のようにコールバックチェーンが構築されます。
1回目の prepend(verify_authenticity_token):
[:verify_authenticity_token]2回目の prepend(verify_request_for_forgery_protection):
[:verify_request_for_forgery_protection, :verify_authenticity_token]結果として実行順序は:
verify_request_for_forgery_protectionverify_authenticity_token
となってしまいます。
一方、deprecation ロジックは「verify_authenticity_token が実行されて @_verify_authenticity_token_ran フラグが立っているかどうか」で判定しています。
しかし実際には verify_request_for_forgery_protection が先に動くため、このフラグが立っておらず、「verify_authenticity_token が呼ばれていない」と誤認して deprecation warning を出してしまう、というのが今回のバグです。
この問題は、以下のようなコードパターンを持つあらゆるコントローラに影響します。
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception, prepend: true
end修正内容
修正では、2 つのコールバックの両方を prepend_before_action で登録し、かつ順序が期待通りになるようにしました。
# 変更後(概念的なイメージ)
prepend_before_action :verify_request_for_forgery_protection, options
prepend_before_action :verify_authenticity_token, optionsprepend_before_action は「後から登録したものが前に挿入される」ため、最終的なチェーンは:
[:verify_authenticity_token, :verify_request_for_forgery_protection]となり、実行順序は:
verify_authenticity_token(ここで@_verify_authenticity_token_ran = true)verify_request_for_forgery_protection(フラグを見て正しく deprecation 判定)
に修正されます。
テストの更新
actionpack/test/controller/request_forgery_protection_test.rb では:
prepend: trueを使うケースのテストが追加/修正され、- ユーザーが独自の before_action / prepend_before_action を定義している場合でも、
Rails が内部で追加する 2 つのコールバックの順序が崩れないこと
が確認されています。
- 影響範囲・注意点
- 対象:
- Rails の
ActionController::RequestForgeryProtectionを使っており、 - コントローラまたは
ApplicationControllerでprotect_from_forgery prepend: trueを指定しているアプリケーション。
- Rails の
- 影響内容:
- これまで「
verify_authenticity_tokenが呼ばれていない」といった趣旨の deprecation warning が誤って出ていたケースで、その警告が出なくなります。 - 実際の CSRF 保護の挙動自体(トークン検証ロジック)は、元々も今も変わりません。変わるのはコールバックの実行順と、それに紐づく deprecation 判定のみです。
- これまで「
- カスタムコールバックとの兼ね合い:
- 自前で
before_action/prepend_before_actionを追加している場合も、
Rails 側の 2 つのコールバックが常に正しい順序で並ぶようにテストされています。 - ただし、
before_actionの順序に依存した繊細なカスタマイズをしている場合は、Rails アップデート後に一度controller._process_action_callbacks.map(&:filter)などで実際の順序を確認するのがおすすめです。
- 自前で
- 参考情報 (あれば)
- 該当 PR: https://github.com/rails/rails/pull/56392
- 関連 PR(後方互換のためのコールバック追加): https://github.com/rails/rails/pull/56369
- 該当コード:
actionpack/lib/action_controller/metal/request_forgery_protection.rb - 典型的な影響例検索:
protect_from_forgery prepend: trueを使う公開リポジトリ
https://github.com/search?q=protect_from_forgery+prepend%3A+true+language%3Aruby&type=code
#56390 [ci skip] Highlight wishlist model changes
マージ日: 2025/12/17 | 作成者: @Tretent
概要 (1-2文で)
このPRは、Railsガイドの「Wishlist」モデルのコード例に対して、他モデルと同様に「変更箇所のハイライト」を付けるドキュメント修正です。アプリケーションコードや挙動には一切影響せず、ガイドの可読性・一貫性を高めるための変更です。変更内容の詳細(あればサンプルコードも含めて)
- 対象ファイル:
guides/source/wishlists.md - 変更行数: +1 / -1(実質的にはコードブロック内のマークアップ修正)
背景として、ガイドのセクション 2.2 で Wishlist モデルに変更を加える流れが説明されているものの、他モデル(例: User, Product など)のコード例のように「どの行が新規・変更行か」がハイライトされていませんでした。
このPRでは、その Wishlist モデルのコードブロックに対して、以下のような「差分ハイライト用マークアップ」を追加・修正しています(イメージ):
-```ruby
+```ruby
class Wishlist < ApplicationRecord
- has_many :items
+ has_many :items # この行をハイライトするような記法に変更
end実際には、Railsガイドが採用している「ガイド内差分表示用の記法」(例: # BEGIN_HIGHLIGHT / # END_HIGHLIGHT、行頭の + / - など)を用いて、Wishlist モデルの「新しく追加された行」が読者に分かりやすく強調されるようにしています。
※正確な記法はガイド内の他モデルの例に合わせて統一されています。
- 影響範囲・注意点
- 実コード、ライブラリ、フレームワークの挙動への影響はありません(完全にドキュメントのみの変更)。
- CHANGELOG の更新も不要と明示されています。
- 影響範囲は Rails Guides の「wishlists」ページ(
guides/source/wishlists.md)に限定され、ガイド閲覧時の見え方が少し改善されるだけです。 - テストへの影響はなく、既存テストもドキュメントの内容に依存していないため、特別な検証は不要です。
- 参考情報 (あれば)
- 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-2文で)
Minitest 6 でtest_orderがrun_orderにリネームされる変更に対応するため、Rails 側でActiveSupport::TestCaseにrun_orderを追加し、Minitest のバージョン差を吸収するブリッジを入れた PR です。Rails 全体でメソッド名を置き換えるのではなく、AS::TC#test_orderを通じてMT::Test#run_orderを呼び出せるようにして互換性を保ちます。
- 変更内容の詳細
背景
- 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 を提供するブリッジを追加します。
ざっくりしたイメージコードは以下のようなものです(※趣旨を説明するための擬似コードです):
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
endPR の説明文のポイント:
- 「Rails 側ですべてをリネームする」のではなく、
AS::TC#test_orderを使い続けつつ、run_orderが必要なときだけブリッジする- かつ「
AS::TCがそれに応答するなら (respond_to?)」という条件つきで、バージョン差に柔軟に対応
要するに:
- Minitest 5 系: 従来どおり
test_orderだけを使っても動く - Minitest 6 系:
run_orderを見にくるコードがあっても、test_orderに委譲されるので壊れない
という二重互換レイヤを ActiveSupport::TestCase に差し込んでいます。
- 影響範囲・注意点
影響範囲
ActiveSupport::TestCaseを継承している Rails のテスト (model / helper / mailer / job など) が対象。- Minitest のバージョンにかかわらず、
test_orderを使ってきたコードはそのまま動く想定です。 - Minitest 6 対応の一環で、今後 Rails 内部も徐々に
test_order→run_orderへ移行しやすくなります。
注意点 / 開発者視点でのポイント
アプリ/gem 側で Minitest に直接触っている場合
- 自前で
run_orderを参照・定義しているコードがある場合、Minitest 6 と Rails の挙動差に注意してください。 - Rails テストクラス (
ActiveSupport::TestCase継承) に対しては、このブリッジによりrun_orderを安全に呼べるようになりますが、素のMinitest::Testを継承しているクラスにはこのブリッジは効きません。
- 自前で
test_orderを前提とした Monkey Patch / メタプログラミングActiveSupport::TestCaseに対してtest_orderをオーバーライドしたり、method_missingでプロキシしているようなコードがある場合、そこにrun_orderが追加されることで意図しない挙動になる可能性があります。- そのような場合は
run_orderにも対応するか、挙動を確認するのがおすすめです。
Rails と Minitest のバージョン組み合わせ
- この PR で Rails 側は MT5 / MT6 の両方をある程度吸収しますが、Minitest 6 の最終仕様 (特に
run_order周り) に追従しているかどうかは、今後の Rails リリースノートと合わせて確認すべきです。 - 特に CI で Minitest のバージョンを固定している場合は、MT6 に上げるタイミングで挙動差をチェックしてください。
- この PR で Rails 側は MT5 / MT6 の両方をある程度吸収しますが、Minitest 6 の最終仕様 (特に
- 参考情報 (あれば)
- Minitest 6 における
test_order→run_order変更は、テストランナー周りの API クリーンアップの一部と考えられます。 - 関連しそうな場所:
- Rails ガイド: Testing Rails (
ActiveSupport::TestCase, テストの実行順序) ActiveSupport::TestCaseソース:activesupport/lib/active_support/test_case.rb
(test_order/run_order定義箇所を確認すると、この PR のブリッジロジックが見つかります)
- Rails ガイド: Testing Rails (
- 今後の Rails / Minitest のアップグレードに備え、アプリ側で「実行順序」を直接いじるコードは最小限にし、
ActiveSupport::TestCaseの提供する API に寄せておくと移行コストを下げられます。
#56380 [ci skip] Fix SQL and console commands for email field
マージ日: 2025/12/16 | 作成者: @Tretent
- 概要 (1–2文で)
Railsガイド「sign_up_and_settings.md」の11.1節で、カラム名の記述ミス(email)を正しいemail_addressに修正したドキュメント専用PRです。実装コードや挙動には一切変更がなく、ガイドの内容と実際のスキーマ定義の不整合を解消しています。
- 変更内容の詳細
- 対象ファイル:
guides/source/sign_up_and_settings.md - 変更点:
emailというカラム名への言及を、実際のスキーマで使われているemail_addressに置き換え。
イメージとしては、以下のようなガイド中の記述が:
# (修正前のイメージ)
$ rails console
> User.find_by(email: "user@example.com")次のように修正されている形です:
# (修正後のイメージ)
$ rails console
> User.find_by(email_address: "user@example.com")また、同様に SQL の例が載っている場合も、email → email_address に合わせて修正されています。
これにより、読者がガイドに従って Rails コンソールや SQL を実行した際に、実際のカラム名と一致するようになります。
- 影響範囲・注意点
- 実装コード・テスト・挙動への影響は一切ありません(ドキュメントのみの変更)。
- 「sign_up_and_settings」ガイドに沿って学習・実行している開発者にとって:
- 以前は
emailを使うと NoMethodError や UnknownAttribute エラーになる可能性がありましたが、ガイドの記述がemail_addressに統一されることで、その混乱を防げます。
- 以前は
- 既に
email_addressカラムを使っているアプリケーション側での対応は不要です(名前の「ドキュメント側の誤記」を直しただけです)。
- 参考情報 (あれば)
- 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-2文で)
Active Record のinheritance.rbとenum.rbから、現在は使われていないrequireが2つ削除された PR です。機能的な挙動は変えずに、不要な依存を整理してコードを軽量化しています。
- 変更内容の詳細
削除された require
対象ファイルと削除内容は以下の2つです。
# activerecord/lib/active_record/inheritance.rb
-require "active_support/core_ext/hash/indifferent_access"# activerecord/lib/active_record/enum.rb
-require "active_support/core_ext/hash/slice"1) inheritance.rb の indifferent_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.rb の slice 削除の経緯
enum定義時の廃止予定だったキーワード引数形式をサポートするために、options.slice!を使う目的でrequire "active_support/core_ext/hash/slice"が導入されていました(commit:0618d2d84a)。- その後、廃止予定だった enum のキーワード引数サポート自体が削除され(commit:
b9226a68bb)、
それに伴いslice!の呼び出しコードも削除されました。 - こちらも
requireだけが残っていたため、今回の PR で削除されています。
簡易イメージ(サンプル的な状況説明)
※ 実際のコードではなく、状況イメージ用の擬似コードです。
# 以前(性能改善前)
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" だけ残っていた# 以前(enum の非推奨キーワード引数サポートあり)
options = args.extract_options!
options.slice!(:_prefix, :_suffix, :_scopes) # こうした処理のために require が必要だった
# 非推奨サポート削除後
# ↑のような options.slice! を行うコード自体が削除済み
# しかし require "active_support/core_ext/hash/slice" が残っていた- 影響範囲・注意点
- Rails の公式コード内部でのみ完結する変更です。
2つのrequireは、どちらもすでに利用箇所が無く、アプリコードから直接 rely されることは通常ありません。 - この PR によって:
- ActiveRecord の 挙動・API・public インターフェースは変化しません。
- 既存の
enum機能や STI(Single Table Inheritance)などの機能に影響はありません。
- もしアプリケーション側が、
「ActiveRecord が読み込まれることでHash#with_indifferent_accessやHash#sliceが必ず使える」
という暗黙の前提に依存していた場合(かなり特殊なケース)、 そのコードは壊れる可能性があります。- その場合は、アプリ側で明示的に:rubyのどちらか/両方を読み込む必要があります。
require "active_support/core_ext/hash/indifferent_access" require "active_support/core_ext/hash/slice"
- その場合は、アプリ側で明示的に:
- ただし、通常の Rails アプリでは
active_support/allまたは必要な core_ext を
どこかでロードしていることがほとんどのため、この影響を受けるケースは稀と思われます。
- 参考情報 (あれば)
- この PR の説明に登場する関連コミット:
inheritance.rbにindifferent_accessを導入したコミット:85afc117566- その後
with_indifferent_access利用を削除してパフォーマンス最適化したコミット:bfc62febac9 enum.rbにsliceを導入したコミット(非推奨の enum キーワード引数サポート用):0618d2d84a- 非推奨の enum キーワード引数サポートと
slice!利用を削除したコミット:b9226a68bb
これらを踏まえると、この PR は「長期間実質的に死んでいた依存関係を整理する掃除(refactoring/cleanup)」に位置づけられる変更です。
#56357 Only emit logs if logger is defined
マージ日: 2025/12/16 | 作成者: @claudiob
- 概要 (1-2文で)
Rails のActiveSupport::EventReporter::LogSubscriberがlogger=nilの場合にNoMethodError (undefined method 'debug?' for nil)を起こしていた問題を、logger が存在する場合のみログを出力するようにして修正した PR です。config.action_view.logger = nilのように一部コンポーネントで明示的に logger を無効化していても、/up などのエンドポイントで例外が出なくなります。
- 変更内容の詳細
問題の発生条件
PR 説明にある手順のとおり、
# 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 にアクセスすると、以下のような例外が発生していました。
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 が、logger が nil のケースを考慮せずに logger.debug? のような呼び出しを行っていたためです。
実際の変更内容
変更ファイルは 2 つです。
activesupport/lib/active_support/event_reporter/log_subscriber.rbactivesupport/test/event_reporter/log_subscriber_test.rb
log_subscriber.rb 側では、「logger が定義されている場合だけログを emit する」という条件分岐が追加されました。実際のコード断片は PR 本文には出ていませんが、イメージとしては次のようなガードが入った形です。
def emit(...)
return unless logger # ← このように logger の存在チェックを追加
# 既存のログ出力処理(例: logger.debug? で判定してから出力)
endあるいは、debug? 呼び出しの前で安全にナビゲーションしている可能性もありますが、趣旨としては「logger が nil のときはそもそもログを発生させない」という修正です。
それに合わせて、activesupport/test/event_reporter/log_subscriber_test.rb にテストが追加されています。テスト内容は、おそらく次の 2 パターンをカバーしていると考えられます。
loggerが設定されている場合に、これまで通りログが出力されることlogger = nilの場合でも#emitの呼び出しが例外を投げずにスルーされること
テストの追加行数が 12 行なので、簡潔なケースが 1〜2 個増えた程度の小さな変更です。
- 影響範囲・注意点
影響範囲
ActiveSupport::EventReporter/LogSubscriberを通じたログ出力全般に影響しますが、「logger を明示的に nil にしているケースで、これまでクラッシュしていたところが静かに何もしない」ようになるだけなので、後方互換性はほぼ問題ありません。- 特に、
config.action_view.logger = nilのようにサブコンポーネントごとに logger をオフにする設定をしているアプリで、/up などのヘルスチェックエンドポイントアクセス時の例外が解消します。
注意点
- これまでは「バグとして例外になっていた」ケースが、「ログが出ないだけで正常に処理が進む」ようになります。もし
logger=nilを意図していなかった場合(設定ミスなど)は、例外による早期発見ができなくなるので、production.rbなどでの logger の設定は改めて確認した方が安全です。 loggerを nil にしたいのではなく「静かにしたい」だけなら、レベルを:fatalに上げるなどの選択肢もありますが、この PR によりnilでも安全に動作するため、そのままでもクラッシュはしません。
- これまでは「バグとして例外になっていた」ケースが、「ログが出ないだけで正常に処理が進む」ようになります。もし
- 参考情報 (あれば)
- 元の問題の文脈・議論:
https://github.com/rails/rails/pull/56213#issuecomment-3642535238 - 対象クラス:
ActiveSupport::EventReporter::LogSubscriber
(activesupport/lib/active_support/event_reporter/log_subscriber.rb)
#56202 Prepping rails for minitest 6
マージ日: 2025/12/16 | 作成者: @zenspider
- 概要 (1-2文で)
Rails のテスト周りを、次期 minitest 6 でも問題なく動作するように調整した PR です。minitest 5 との後方互換性を維持しつつ、Rails 側の拡張・フック・並列実行・ラインフィルタリングなどを minitest 6 の仕様に合わせて整備しています。
- 変更内容の詳細
※実際の 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 endRails のアサーションが 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 特有のクリーンアップなど endminitest 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) の取り扱い
- worker プロセス/スレッド側での
具体例(擬似コード):
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 endminitest 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.runnablesやMinitest::Test.runnables)に合わせて、どのテストメソッドがどの行にあるかを解決するロジックを更新。 - 正規表現ベースだったフィルタを、行番号とメソッド定義位置のマッピングにするなど、より堅牢な実装へ。
rails test test/models/user_test.rb:10-20のような範囲指定や複数指定に対する処理の改善。
- minitest 6 のテストディスカバリ API(例:
具体的なイメージ(擬似コード):
rubymodule 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 endminitest 6 でテスト名の扱いやクラス探索の順序が変わっても、
rails testの UX を維持するための変更と言えます。
- 影響範囲・注意点
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 が変わる可能性があります。
- 参考情報 (あれば)
- 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–2文で)
File.atomic_writeの一時ファイル生成ロジックが整理され、SecureRandom を用いて衝突を事実上不可能にしつつ、万一の競合はEXCLフラグ付きオープンで防ぐようになりました。あわせて、ディレクトリごとのパーミッション推測ロジックが削除され、「必要ならブロック内で自分で chmod/chown する」方針に簡素化されています。
- 変更内容の詳細
2-1. File.atomic_write の衝突回避ロジックの刷新
従来の File.atomic_write は、同名ファイルの存在チェック+リトライ、といった形で一時ファイル名の衝突を回避していましたが、この PR で次のように変わりました。
- 一時ファイル名の生成に
SecureRandomを使用- 例:
basename-<secure_random_hex>のような形で、事実上衝突しない名前を生成
- 例:
- ファイルオープン時に
EXCLフラグ(File::EXCL)を利用File::CREAT | File::EXCLの組み合わせでオープンし、万が一同名ファイルが既に存在する場合は OS レベルでエラーにする- これにより「存在チェック → 作成」の間に他プロセスがファイルを作る TOCTTOU レースを避けられる
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 ブロック内で次のようなコードを書くことが推奨されるケースがあります:
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 側で「それっぽいパーミッション」になるよう努力していましたが、今後は「どのようなパーミッションにしたいかは利用側の責任」という整理になります。
- 影響範囲・注意点
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 によるユニーク名生成のため、実際にこのケースに遭遇することはまずありません。
- 参考情報 (あれば)
- 対応する Issue/PR:
- 修正対象として言及されている PR: https://github.com/rails/rails/pull/56367
- 本 PR: https://github.com/rails/rails/pull/56368
- 関連する 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-2文で)
このPRは、Rails 7.2 でverify_authenticity_tokenがverify_request_for_forgery_protectionに改名された後も、既存コードで広く使われているskip_before_action :verify_authenticity_tokenが引き続き動くようにしつつ、非推奨として扱うための仕組みを追加したものです。skip_before_action :verify_authenticity_tokenで forgery protection をオフにした場合、今後はデprecation警告が出るようになります。
- 変更内容の詳細
背景
- 前PR (#56350) で
verify_authenticity_tokenコールバックがverify_request_for_forgery_protectionにリネームされた。 - ただし現実には、ほとんどのアプリが以下のように書いて forgery protection を無効化している:ruby
skip_before_action :verify_authenticity_token - 名前が変わっただけで既存のこの書き方を壊すと、膨大な既存アプリに影響が出るため、挙動は維持しつつ「非推奨」として誘導したい。
実装方針
PR本文を踏まえると、実装の狙いは次の通りです。
protect_from_forgery実行時に、「verify_authenticity_tokenが実行された」というフラグを内部的に立てるようにする。- 実際にリクエストを検証するタイミングで、そのフラグを確認する。
- フラグが立っていない場合は、「
verify_authenticity_tokenが本来実行されるはずだったが、どこかでスキップされた」と判断する。 - そのスキップが
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を使ってほしい」と警告が出る。
- 内部的には「
想定されるコード例と今後の推奨書き方
従来よく見られるコード:
class WebhooksController < ApplicationController
skip_before_action :verify_authenticity_token
def create
# ...
end
endこのPR適用後も動作自体は変わりませんが、ログ等に deprecation warning が出るようになります。
今後は以下のように書き換えるのが推奨されます:
class WebhooksController < ApplicationController
skip_forgery_protection
def create
# ...
end
end変更点のファイルレベルでの概要:
action_controller/metal/request_forgery_protection.rb- forgery protection の内部処理に、
- 「
verify_authenticity_tokenが実行されたかどうか」を記録する仕組み - それを検査して deprecation warning を出すロジック
を追加。
- 「
- forgery protection の内部処理に、
action_controller/base.rb- 上記の挙動を踏まえた軽微な修正(ほぼフックの差し替えレベル)。
request_forgery_protection_test.rbskip_before_action :verify_authenticity_tokenを使った場合に、- 挙動が今まで通り forgery protection をスキップすること
- かつ deprecation warning が出ること
を確認するテストを追加。
- 影響範囲・注意点
- 既存の
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自体の動作には直接の変更はなく、「名前の変更」と「旧名経由のスキップ方法の非推奨化」が主な変更点です。
- 参考情報 (あれば)
- このPRでの前提となる変更:
- #56350:
verify_authenticity_token→verify_request_for_forgery_protectionへのリネーム
- #56350:
- 関連するAPI:
protect_from_forgeryskip_forgery_protectionskip_before_action/before_action(旧before_filter)
#56365 Include MRI prerelease string in generated .ruby-version
マージ日: 2025/12/15 | 作成者: @jeromedalbert
- 概要 (1-2文で)
Rails のrails newが生成する.ruby-versionについて、MRI のプレリリース版(例:4.0.0-preview2)を使っている場合に、プレリリースのサフィックス(-preview2など)を含めて正しく出力するように修正した PR です。これにより、asdfやmiseなどの Ruby バージョンマネージャーでエラーになりにくくなります。
- 変更内容の詳細
何が問題だったか
- これまで:
- MRI プレリリース版の Ruby(例:
ruby 4.0.0preview2)でrails newを実行すると、生成される.ruby-versionには4.0.0しか書かれず、-preview2が落ちていた。 - その結果、
asdfやmiseなどが「4.0.0 が見つからない」といった扱いになり、インストール済みのプレリリース版4.0.0-preview2を認識できないケースがあった。
- MRI プレリリース版の Ruby(例:
- 一方で、TruffleRuby や JRuby など MRI 以外の実装では、
RUBY_ENGINE_VERSIONがもともと23.0.0-preview1のようにサフィックス込みで返るため、この問題は発生していなかった。
どのように直したか
変更された主な箇所は以下です。
railties/lib/rails/generators/app_base.rbrailties/lib/rails/generators/rails/app/templates/ruby-version.ttrailties/test/generators/generator_test.rb
.ruby-version のテンプレート変更
ruby-version.tt では、Ruby のバージョン文字列を生成するときに、MRI かそうでないかで扱いを分岐するようになりました。
概念的には以下のような挙動になります(実際のコードはもう少し抽象化されています):
# 擬似コードイメージ
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 実装 + バージョンマネージャの現実的な利用パターンが網羅的にテストされています。
- 影響範囲・注意点
影響範囲
- 新規に
rails newを実行したときに生成される.ruby-versionの内容が、MRI プレリリース版利用時にのみ変わります。 - 既存プロジェクトの
.ruby-versionには影響しません(自動変換などはなし)。 - MRI 以外(JRuby, TruffleRuby など)は、従来通り
RUBY_ENGINE_VERSIONベースで書かれる挙動のままです。
- 新規に
期待できる改善
- MRI プレリリース版を使っている環境で:
.ruby-versionが4.0.0-preview2のように完全一致の値を持つようになり、asdf,miseなどのバージョンマネージャが、インストール済みのプレリリースバージョンと正しくマッチしやすくなります。
- これにより、「
.ruby-versionには 4.0.0 と書いてあるが、インストールされているのは 4.0.0-preview2 なので見つからない」といった齟齬が減ります。
- MRI プレリリース版を使っている環境で:
注意点
- MRI プレリリース版をあえて
4.0.0として扱いたい、というニッチな運用をしている場合は、今後.ruby-versionに-previewXが付与されるため、ツール側の設定や CI などに影響が出る可能性があります。 - ただし、一般的にはプレリリースとリリースを区別して扱いたいケースが大半なので、多くの環境では望ましい挙動変更といえます。
- この変更は Rails の内部実装 (
AppBaseのバージョン抽出ロジック) に依存しており、MRI 側のRUBY_DESCRIPTION等フォーマットが将来的に大きく変わると、プレリリース判定ロジックの追随が必要になる可能性があります。
- MRI プレリリース版をあえて
- 参考情報 (あれば)
- 対応 Issue: https://github.com/rails/rails/issues/55644
- この挙動は、
rbenvやrvmだけでなく、asdf,miseといったマルチランタイムマネージャでの動作検証に基づいてテストされているため、Ruby 4.0 系プレリリースを試す際にも安心してrails newを使えるようになります。
#56363 Fix typo in Active Job callbacks test
マージ日: 2025/12/15 | 作成者: @shivamsinghchahar
概要 (1-2文で)
Active Job のコールバックに関するテスト名に含まれていたタイプミス(swhen)を修正する、テストコードのみの非常に小さな修正です。挙動や API には一切変更がありません。変更内容の詳細
- 対象ファイル:
activejob/test/cases/callbacks_test.rb - 変更内容: テストメソッド名から不要な文字列
swhenを取り除くリネームのみ。
イメージとしては、以下のような変更です(実際の名前は PR 本文からは断定できませんが、ニュアンスとして):
# 変更前
def test_callbacks_run_swhen_job_is_performed
# ...
end
# 変更後
def test_callbacks_run_when_job_is_performed
# ...
end- ロジックやアサーション内容、テスト対象のコードには一切手を入れておらず、「テスト名の typo 修正」という一点に限定されています。
- コミットメッセージや PR 説明からも、他の副次的な変更がないことが明示されています。
- 影響範囲・注意点
影響範囲
- Rails 本体の実行時挙動には影響なし。
- Active Job の API/挙動/パフォーマンスも一切変化なし。
- 影響するのは「テストコード上のメソッド名」のみです。
注意点
- Rails 本体の CI を前提とした他のコード(例えば「特定のテストメソッド名をピンポイントで実行するスクリプト」など)がもし存在する場合、そのメソッド名に依存していると影響を受ける可能性はあります。ただし、通常の利用では問題になりません。
- 公開 API の変更や仕様変更ではないため、CHANGELOG の更新も不要と判断されています。
- 参考情報 (あれば)
- 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-2文で)
Active Job のテストヘルパー内にある文言の文法ミス("it's" と "its" の誤用)を修正した PR です。機能や挙動には一切変更がなく、コメント/メッセージの英語表現のみが改善されています。
- 変更内容の詳細
- 対象ファイル:
activejob/lib/active_job/test_helper.rb - 変更点: 英文の possessive(所有格)の誤りを修正
"it's"("it is" の短縮形)と書かれていた箇所を、
所有格"its"に修正しています。
実際には次のようなごく小さな変更です(イメージ例):
# 修正前(例)
# This method resets the job queue and it's state.
#
# 修正後(例)
# This method resets the job queue and its state.※PR 説明文から判断すると、実コードではなくコメントやドキュメント的な文言の修正であり、ロジックやインターフェイスは一切変更されていません。
- 影響範囲・注意点
影響範囲:
- Active Job のテストヘルパーを利用するアプリケーションの挙動には影響しません。
- API シグネチャ、挙動、テスト結果は従来と完全に同じです。
- ドキュメントやコメントを読む際の英語表現が正しくなったのみです。
注意点:
- バージョンアップ時にこの PR によるマイグレーションやコード修正は不要です。
- CI をスキップする指定(
[ci-skip])がタイトルにあり、純粋にドキュメントレベルの修正であることが明示されています。
- 参考情報 (あれば)
- PR タイトル:
[ci-skip] Fix typo in Active Job test helper - PR 番号: #56364
- 修正の背景:
- "it's"(= it is)ではなく、所有格 "its" が文法的に正しいため、その誤用を修正したと PR 説明に明記されています。
- Rails のドキュメント・コントリビューションガイド:
#56360 [ci skip][docs] Fix :nodoc: placement in ActiveRecord::Transactions
マージ日: 2025/12/15 | 作成者: @Yuhi-Sato
- 概要 (1-2文で)
ActiveRecord::Transactions 内の:nodoc:の付け方を修正し、APIドキュメントに本来載るべきメソッドが非表示になっていた問題を直した PR です。あわせて、内部用のwith_transaction_returning_statusを公式に「内部メソッド」としてドキュメントから隠すようにしています。
- 変更内容の詳細
背景
- PR #18102 で、内部用の定数を API ドキュメントから隠すために
:nodoc:が導入された。 - しかし、その
:nodoc:の書き方(適用範囲)が広すぎたため、- 対象にしたかった定数だけでなく、
- その後ろに定義されるメソッドまで API ドキュメントから除外されてしまっていた。
- また、issue #26656 で指摘されていた
with_transaction_returning_statusは内部メソッドであり、公開 API に含めるべきではないことが、メンテナによって確認済み。
この PR はこの2点を整理して、「何が public API か」という境界をドキュメントレベルで正しく表現する修正です。
実際の修正内容
ファイル: activerecord/lib/active_record/transactions.rb
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:が効く形にしています。- 「
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のような形になっていると考えられます(実装スタイル次第)。
- 影響範囲・注意点
ランタイムの挙動への影響
- 変更は
:nodoc:(ドキュメント生成用のメタ情報)に限定されており、Ruby の実行時動作には影響しません。 - メソッドや定数のシグネチャ・挙動は変わっておらず、既存アプリケーションのコードが壊れることはありません。
- 変更は
ドキュメント/API 仕様面の影響
- ActiveRecord::Transactions の API ドキュメントに、本来表示されるべき公開メソッドが正しく表示されるようになります。
ACTIONS定数は内部向けとしてドキュメントから隠されます。with_transaction_returning_statusは「内部メソッド」として公式に非公開扱いとなり、今後の変更互換性が保証されない位置づけが明確になります。
開発者が意識すべき点
- もしアプリ側で
with_transaction_returning_statusを直接呼び出している場合は、非公開 API に依存している状態です。この PR をきっかけに、将来の Rails バージョンでこのメソッドが変更・削除される可能性を考慮すべきです。 - トランザクション処理は、基本的には
transactionやsave!/update!など、ドキュメントに記載された公開メソッドだけを使うのが安全です。
- もしアプリ側で
- 参考情報 (あれば)
- この PR で言及されている過去の議論:
- PR #18102: 内部定数を隠すために
:nodoc:が追加された PR - Issue #26656:
with_transaction_returning_statusを公開 API に含めるべきでないとした議論
- PR #18102: 内部定数を隠すために
:nodoc:は Rails 独自ではなく、Ruby のドキュメントツール(RDoc / YARD)における「ドキュメント生成対象外」指定の記法です。- クラス/モジュール/メソッド/定数などに付与でき、そのスコープの取り方によって「どこまでを非公開扱いにするか」が変わるため、今回のように配置ミスがドキュメントに影響することがあります。
#56361 Add controller action source location to routes inspector
マージ日: 2025/12/14 | 作成者: @guilleiguaran
- 概要 (1-2文で)
rails routes --expandedとルーティングエラーページで、各コントローラアクションが「どのファイルの何行目で定義されているか」を表示できるようになりました。RAILS_EDITOR/EDITORが設定されている場合、ルーティングエラーページからワンクリックで該当アクションのソースをエディタで開けます。
- 変更内容の詳細
2-1. RouteWrapper#action_source_location の追加
ActionDispatch::Routing::RoutesInspector::RouteWrapper に、アクションの定義場所を取得するメソッドが追加されています。
内部的には次のようなイメージで実装されています(概念的なサンプル):
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」が追加されました。
イメージ:
$ 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.erbrails routes --expandedのテーブルヘッダに "Action Location" 列を追加。
actionpack/lib/action_dispatch/middleware/templates/routes/_route.html.erb- 各ルート行にアクション位置情報を表示。
- ルーティングエラーページで ✏️ リンクを描画するロジックを追加。
actionpack/test/dispatch/routing/inspector_test.rbaction_source_locationが期待どおり動作することを確認するテストを多数追加。- 名前空間付きコントローラ、Rack アプリ、存在しないアクションなどのケースもカバー。
railties/test/commands/routes_test.rbrails routes --expandedの出力に "Action Location" が含まれることを確認するテストを追加。
- 影響範囲・注意点
主な影響範囲
rails routes --expandedの出力形式が増える(列が一つ増える)- このコマンド出力をパースしているツール/スクリプトがある場合は要注意。
- ルーティングエラー画面の HTML が変わる
- 自前でテンプレートを差し替えている場合、今回の変更と差分が出る可能性あり。
パフォーマンス
source_locationを得るためにコントローラクラスをconstantizeしinstance_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ではないものは、アクション位置が表示されないだけで、従来どおり利用できます。
- 参考情報 (あれば)
- 関連ファイル:
actionpack/lib/action_dispatch/routing/inspector.rbactionpack/lib/action_dispatch/middleware/templates/routes/
- エディタ連携の一般的な設定例(Rails ガイドでも紹介されている形式):
- VS Code:
export RAILS_EDITOR="code --goto %{file}:%{line}" - RubyMine:
export RAILS_EDITOR="rubymine %{file}:%{line}"
- VS Code:
- この PR により、「どこでそのアクションが定義されているか」を Rails が教えてくれるようになるため、ルーティング周りのデバッグ・既存コードの追跡がかなり楽になります。
#56359 Active Support: remove stale deprecation assertions from TimeWithZone YAML tests
マージ日: 2025/12/13 | 作成者: @afurm
- 概要 (1-2文で)
ActiveSupport::TimeWithZoneの YAML シリアライズに関するテストから、既に無効となった非推奨警告(assert_not_deprecated)のアサーションと TODO コメントが削除され、テストがシンプルに YAML 出力そのものを検証する形に整理されています。挙動変更はなく、テストコードのクリーンアップのみです。
- 変更内容の詳細(あればサンプルコードも含めて)
対象ファイル:
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変更後のイメージ:
rubyassert_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が通ることが確認されています。
- もともと
- 影響範囲・注意点
ランタイム挙動:
- アプリケーションコードには一切変更がなく、
ActiveSupport::TimeWithZoneの YAML シリアライズ挙動自体は従来どおりです。 - 非推奨警告の有無もすでに現状どおりで、このPRで新たに non-deprecated になったわけではありません。「既に出ていない警告に対するテスト用のラッパ削除」という位置付けです。
- アプリケーションコードには一切変更がなく、
テスト・開発者への影響:
- YAML 形式の期待値比較がそのまま残っているため、将来 YAML 出力フォーマットが変われば従来どおりテストが落ちて検知できます。
- 逆に「非推奨警告が出ないこと」を明示的にテストする仕組みはなくなりますが、そもそも該当ケースは非推奨扱いではなくなっており、テスト意図としても不要という判断です。
- CI などに対しても影響はなく、テストのノイズが減るのみです。
Rails バージョンとの関係:
- TODO コメントに記載されていた「Rails 7.1 で削除予定」のものを、現行バージョン(7.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-2文で)
PostgreSQL アダプタに、複数テーブルのシーケンスを一括でリセットできるreset_column_sequences!が追加され、特にフィクスチャ読み込み時などに多数テーブルがある環境でのパフォーマンス改善を狙った変更です。あわせて抽象アダプタ/各DBアダプタの reset 関連メソッドの整理が行われています。
- 変更内容の詳細
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 内部実装を簡略化したサンプル):
# 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! などに対するテストが追加されています。
これにより、複数テーブルを対象としたリセット処理が想定通り動くことを担保しています。
- 影響範囲・注意点
- 対象 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 ロード時間短縮が見込まれます。
- 参考情報 (あれば)
- 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-2文で)
config.action_controller.forgery_protection_verification_strategyで設定したデフォルト値が、コントローラ側のprotect_from_forgeryに正しく反映されず、常に:header_onlyがフォールバックとして使われていた問題を修正するPRです。これにより、configで指定した検証戦略(例::header_or_legacy_token)が期待通りデフォルトとして利用されるようになります。
- 変更内容の詳細
何が問題だったか
アプリ側で以下のように設定しても:
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 に揃えた、という内容です。
ざっくりしたイメージコード
(実際のコードとは多少簡略化したイメージです)
def protect_from_forgery(options = {})
options[:with] ||= Rails.configuration.action_controller.forgery_protection_verification_strategy || :header_only
# ...
endこのように「config にあるならそれを使う」が保証されるようになりました。
- 影響範囲・注意点
影響を受けるケース
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 が期待通り検証されるか、確認しておくと安全です。
- これまで「設定しているつもりだったが、実際には
- 参考情報 (あれば)
- 関連PR: https://github.com/rails/rails/pull/56350
- そのPRでの議論: https://github.com/rails/rails/pull/56350/changes#r2615865992
- 該当モジュール:
actionpack/lib/action_controller/metal/request_forgery_protection.rb
- テスト:
actionpack/test/controller/request_forgery_protection_test.rbに、- 設定値を変えたときの
protect_from_forgeryの挙動
を検証するテストが追加・修正されています。
- 設定値を変えたときの
#56346 Update with_lock docs to mention yielded transaction
マージ日: 2025/12/12 | 作成者: @Saidbek
- 概要 (1-2文で)
ActiveRecord::Base#with_lockが「現在のトランザクションオブジェクトをブロックに渡す」ようになった (#56334) ことを、APIドキュメントとガイドに反映した PR です。これにより、利用者がwith_lock内でトランザクションのコールバックなどを明示的に登録できることがドキュメント上も分かるようになりました。
- 変更内容の詳細
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.md の with_lock の解説部分が更新され、サンプルコードが「トランザクションをブロック引数で受け取る」形になっています。おおよそ以下のような内容が追加・変更されています(イメージ):
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のブロックは、単にロック下での処理を書く場所」というだけでなく、「そのロックを担うトランザクションオブジェクト自体を直接扱える場所」であることを示しています。
- 影響範囲・注意点
- 本 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など)の互換性や将来変更に注意が必要です。
- 参考情報 (あれば)
- この PR の背景となる挙動変更:
- PR #56334:
with_lockが現在のトランザクションを yield するようにした変更
- PR #56334:
- 関連ドキュメント:
- Rails Guides – Active Record Querying – Locking (
with_lockセクション) ActiveRecord::Base#with_lockの API ドキュメント (Edge / 該当バージョンの RDoc)
- Rails Guides – Active Record Querying – Locking (
#56355 Active Support notifications for CSRF warnings
マージ日: 2025/12/12 | 作成者: @jeremy
- 概要 (1-2文で)
このPRは、CSRF関連の警告・ブロック処理を「直接ログ出力」から Active Support Notifications を用いた「イベント駆動のログ・フック」に変更し、CSRFイベントを購読・拡張できるようにしたものです。新しい通知イベントが追加され、StructuredEventSubscriber によってログ用の構造化イベントへ変換されるようになりました。
- 変更内容の詳細
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 イベントとして発火されるように置き換わっています。
通知の使い方(購読例)
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通知など
endCSRFの発生状況を、ログに限らずメトリクス送信・アラート・監査ログなど任意の処理に接続しやすくなっています。
2-2. StructuredEventSubscriber との連携
action_controller/structured_event_subscriber.rb に CSRF イベント対応が追加されました。
StructuredEventSubscriberが上記の Notification イベントを受け取り、action_controller.*名前空間の「構造化イベント」に変換LogSubscriber(action_controller/log_subscriber.rb)が扱いやすいフォーマットに整形
- これにより、既存の「アクション実行ログ」などと同じフォーマット/ストリームで CSRF に関するログが扱えるようになります。
LogSubscriber 側では、新たに CSRF 向けのハンドラメソッドが追加されています(例示イメージ):
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 関連の処理は
- ActionController 側で「何が起きたか」のイベントを発火
- StructuredEventSubscriber がそれを捕捉し、構造化イベント/ログフォーマットに変換
- LogSubscriber やカスタム Subscriber が必要なログ・処理を行う
という流れになります。
テスト (request_forgery_protection_test.rb) も、直接ログメッセージを期待する形から、通知イベント発火/StructuredEventSubscriber 経由のログを期待する形に更新されています。
影響範囲・注意点
CSRF関連ログに直接依存している場合の注意
- 以前の「生ログメッセージ」にパース依存していた場合(例: 正規表現で CSRF ログ行だけ抜き出すなど)、メッセージの文言変更や構造化ログ化の影響を受ける可能性があります。
- 今後はなるべく
ActiveSupport::Notifications.subscribeによるイベント購読に切り替えると安全です。
カスタムミドルウェア・監査基盤との連携がしやすくなる
- CSRF ブロックを検知してメトリクス送信・アラート発報・IP ブロック・ユーザー通知などを行うロジックを「Rails の標準機能にフックする形」で実装しやすくなりました。
- 例えば
csrf_request_blocked.action_controllerにフックして、WAF や SIEM へのイベント送信を行うことができます。
パフォーマンス上の影響はごく小さい想定
- ActiveSupport::Notifications はもともと Rails 内部で幅広く利用されている仕組みであり、CSRF 関連の処理もその一部に乗った形です。標準的な利用では特別なパフォーマンス劣化はほぼありません。
- ただし、購読側で重い処理(同期I/O, 外部API呼び出しなど)を行うとリクエストレイテンシに影響が出る可能性があるため、非同期処理(ActiveJob など)と組み合わせることが推奨されます。
ログフォーマットの変化を監視・運用している場合
- 既存のロガー設定、ログ集約/解析ツール(Datadog, Splunk, OpenSearch, Cloud Logging など)のフィルタやパーサが「CSRF ログ」を前提としている場合は、動作確認が必要です。
- 新しく
action_controller.*系の構造化イベントとして出力されるため、フィールドベースのパースがやりやすくなる一方、旧来の生テキスト前提のルールは調整が必要になるかもしれません。
- 参考情報 (あれば)
- 対応 PR: https://github.com/rails/rails/pull/56355
- 関連議論: https://github.com/rails/rails/pull/56350 およびそのレビューコメント(
#discussion_r2615540783) - ActiveSupport::Notifications ドキュメント(英語):
https://api.rubyonrails.org/classes/ActiveSupport/Notifications.html - ActionController::RequestForgeryProtection ドキュメント(英語):
https://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html
この変更により、CSRF に関する挙動をより柔軟・構造化された形で監視・拡張できるようになっているため、監査ログやセキュリティ基盤との統合を検討している場合に特に有用です。
#56350 Use a modern approach for cross-site request forgery protection
マージ日: 2025/12/12 | 作成者: @rosa
- 概要 (1-2文で)
Rails の CSRF 保護を「Sec-Fetch-Siteヘッダを使ったモダン方式」がデフォルトとなるように刷新し、従来の authenticity token 方式はレガシー環境向けのフォールバックとして扱う変更です。あわせて、正当なクロスサイトアクセスを許可するtrusted_originsオプションなどが追加され、将来的に CSRF トークンを廃止していく足がかりになっています。
- 変更内容の詳細
2.1 新しい CSRF 検証アプローチ
protect_from_forgery に using オプションが追加され、CSRF 検証方式を選択できるようになりました。
using: :header_onlySec-Fetch-Siteヘッダのみを使って CSRF 判定を行う。- Rails 8.2 のデフォルトになる想定。
using: :header_or_legacy_token- 原則
Sec-Fetch-Siteで判定し、判定できない場合に限って従来の authenticity token チェックにフォールバックする。 - レガシーブラウザや proxy の都合で
Sec-Fetch-Siteが使えないアプリ向けの移行パス。
- 原則
使用例:
class ApplicationController < ActionController::Base
# 例: 当面はトークンも使いつつ移行したい場合
protect_from_forgery using: :header_or_legacy_token, with: :exception
endheader_or_legacy_token を使っていて、実際にトークンへのフォールバックが発生した場合にはログに警告が出るようになっており、どのリクエストがまだモダン方式に移行できていないかを把握できます。
2.2 Sec-Fetch-Site による判定のポリシー
この PR で導入される新方式では、Sec-Fetch-Site ヘッダを非常に保守的に解釈します。非 GET/HEAD の「ブラウザ由来」のリクエストを想定した挙動です。
- 許可される値
same-originsame-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」を許可するための設定が追加されました。
class ApplicationController < ActionController::Base
protect_from_forgery using: :header_only,
trusted_origins: %w[ https://accounts.google.com ]
endSec-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についての記述が追加。
- CSRF 保護の説明がアップデートされ、新しい
config/initializers/new_framework_defaults_8_2.rb.tt- Rails 8.2 で新デフォルト(
header_only)へ移行するための初期化オプションが追加。アプリ作成時・アップグレード時にここを編集して挙動を切り替えられる。
- Rails 8.2 で新デフォルト(
- テスト (
request_forgery_protection_test.rb)- 新しいパス(
header_only,header_or_legacy_token,trusted_origins)を網羅する大量のテストが追加されており、本機能がかなり慎重にカバーされていることがわかります。
- 新しいパス(
- 影響範囲・注意点
(1) Rails 8.2 以降のデフォルト変更
- デフォルトが
using: :header_onlyになるため、以下のようなケースで非互換が出る可能性があります:- レガシーブラウザ(
Sec-Fetch-Site非対応)からの非 GET/HEAD リクエスト - 中間 proxy / FW が
Sec-Fetch-*系ヘッダを削除している構成 - 正当な Cross-Site POST(OAuth, サードパーティ連携など)を
trusted_originsで明示していない場合
- レガシーブラウザ(
対処方針:
不安がある場合は、まずは以下のように「フォールバックあり」の設定で運用開始するのが現実的です:
rubyprotect_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
などには直接の影響は少ないはずです。
- API モード (
- ただし、「ブラウザからの HTML + JSON を混在させているアプリ」で、CSRF 保護対象の JSON エンドポイントがある場合は、
Sec-Fetch-Siteが前提になることで挙動が変わる可能性があります。
(3) サブドメインをまたぐ運用
- 現時点の実装では
same-siteも許可するため、app.example.comからadmin.example.comへのリクエストなど、サブドメイン間は「同一サイト」として扱われ、CSRF 的には許可されます。
- PR で提起されているオープンクエスチョンとして、「
same-siteを許さずsame-originのみを許可できるようにした方が良いか」があります。- 将来的にここが設定可能になると、サブドメイン間攻撃を避けるためにより厳格なポリシーを選べるようになる見込みです。
- 現時点ではそこまでの設定は入っていないため、サブドメイン分離でセキュリティ境界を引いているアプリは、Origin チェック・クッキー分離などと組み合わせて慎重に設計する必要があります。
- 参考情報
PR 内で言及されている参考資料と、理解に役立つポイントです。
- MDN:
Sec-Fetch-Site
https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Sec-Fetch-Site- 各値 (
same-origin,same-site,cross-site,none) の意味が整理されています。
- 各値 (
- web.dev: Fetch Metadata
https://web.dev/articles/fetch-metadata- Fetch Metadata ヘッダ群(
Sec-Fetch-Site,Sec-Fetch-Mode等)を組み合わせた保護戦略の背景が解説されています。
- Fetch Metadata ヘッダ群(
- Go issue: https://github.com/golang/go/issues/73626
- ブログ: https://www.alexedwards.net/blog/preventing-csrf-in-go
- Go での実装例ですが、ヘッダ中心の CSRF 対策パターンを理解するのに有用です。
この PR は、「CSRF トークンに強く依存する旧来スタイル」から、「ブラウザ標準の Fetch Metadata に基づく CSRF 防御」への移行を Rails レベルで促進するものなので、中長期的に Rails アプリの CSRF 戦略を見直すきっかけになる変更と言えます。
#56354 [ci skip][docs] PARALLEL_WORKERS threshold bypass behavior
マージ日: 2025/12/12 | 作成者: @justinko
- 概要 (1-2文で)
Rails のテスト並列実行機能で使われるPARALLEL_WORKERS環境変数について、「しきい値(threshold)よりテスト件数が少ない場合でも、PARALLEL_WORKERSを明示指定すれば並列実行を強制できる」という挙動を、ガイドと API ドキュメントに明記した PR です。コード上の変更はごく小さく、主にドキュメントの明文化が目的です。
- 変更内容の詳細
背景となる機能
Rails のテスト並列実行は通常、以下のように設定します。
# test/test_helper.rb など
class ActiveSupport::TestCase
parallelize(workers: :number_of_processors)
endRails 7.1 以降では、テスト総数が少ない場合に「オーバーヘッドの方が大きくなる」ことを避けるため、**一定件数未満のテストでは自動的に並列化をスキップする「しきい値」**が導入されています。
この PR(とリンク先の #56352)で整理されたのは、このしきい値を「環境変数 PARALLEL_WORKERS の明示指定でバイパスできる」という挙動です。
コード変更(ActiveSupport::TestCase)
activesupport/lib/active_support/test_case.rb の変更は +3 / -1 行と小さいですが、おおよそ次のような意図を持っています(疑似コードイメージ):
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 を無視して強制並列化
具体的な利用イメージ:
# 通常: テスト数が threshold 未満ならシリアル実行される可能性がある
bin/rails test
# しきい値を無視して常に 4 ワーカーで並列実行したい
PARALLEL_WORKERS=4 bin/rails test
# CI 上で CPU コア数に合わせて並列数を明示的に制御
PARALLEL_WORKERS=$(nproc) bin/rails testAPI ドキュメントの変更
ActiveSupport::TestCase.parallelize の API ドキュメントに、環境変数との関係が追記されています(要約):
parallelize(workers: ...)はENV["PARALLEL_WORKERS"]によってオーバーライドされる- テストの並列化にはしきい値があり、通常はテスト数が少ないと並列化を行わないが、
PARALLEL_WORKERSを指定したときは このしきい値をスキップして並列化 される
- 影響範囲・注意点
すでに
PARALLEL_WORKERSを使っている環境- これまで通り「指定したワーカー数で必ず並列実行」されることが、仕様として明文化された形です。
- テスト件数が少なくても、CI などで
PARALLEL_WORKERSを設定していれば並列実行されるので、環境によってはオーバーヘッドが増える可能性があります。
PARALLEL_WORKERSを使っていない環境- 挙動は従来と同じく、テスト数や threshold に応じて自動判定されます。
- 「小さな test suite で並列化したい」場合は、
PARALLEL_WORKERSを指定すれば threshold を無効化できると理解しておくとよいです。
設定戦略の指針
- ローカル開発:
- テスト数が少ないプロジェクトでは
PARALLEL_WORKERSをあえて指定せず、threshold の自動判定に任せると速度面で有利なことがあります。
- テスト数が少ないプロジェクトでは
- CI:
- 実行時間のブレを減らしたい場合には、
PARALLEL_WORKERSを固定値(または CPU コア数)に明示設定しておくと、threshold に左右されず安定した挙動になります。
- 実行時間のブレを減らしたい場合には、
- ローカル開発:
- 参考情報 (あれば)
- この PR で参照されている議論・実装:
- プレビューされたガイド:
- API ドキュメント:
ActiveSupport::TestCase.parallelize
https://9696f494.rails-docs-preview.pages.dev/api/classes/ActiveSupport/TestCase.html#method-c-parallelize
#56353 Make delegate and delegate_missing_to work in BasicObject subclasses
マージ日: 2025/12/12 | 作成者: @rafaelfranca
- 概要 (1-2文で)
BasicObjectを継承したクラスでもdelegate/delegate_missing_toが正しく動くように、例外の発生元や例外クラス参照を::Kernel/ ルート名前空間経由に修正した PR です。これにより、BasicObjectサブクラスでdelegate_missing_toを使った際にSystemStackError(無限再帰)が発生していた問題が解消されます。
- 変更内容の詳細
何が問題だったか
BasicObject には raise メソッドや NoMethodError などの定数がありません。
一方、delegate / delegate_missing_to が生成するメソッド内では例外を投げたり、例外クラスを参照したりします。
例えば(あくまでイメージ・簡略化):
class MyObject < BasicObject
extend ActiveSupport::Concern
extend ActiveSupport::Delegation
delegate_missing_to :@target
endこのとき、method_missing 経由で DelegationError などを投げようとして、
raiseが存在しない(BasicObjectにはない)NoMethodErrorやDelegationErrorなどの定数解決が正しく行えない
結果として、method_missing 内でさらに method_missing が呼ばれるような無限再帰が発生し、SystemStackError になる、という挙動になっていました。
PR での対処
この PR では、activesupport/lib/active_support/delegation.rb 内で生成されるコードを次のように修正しています(実際のコードはもっと複雑ですが、ポイントだけ抜粋イメージ):
1. 例外発生時は ::Kernel.raise を使うように修正
修正前(イメージ):
raise DelegationError, "Message"修正後(イメージ):
::Kernel.raise ::DelegationError, "Message"もしくは NoMethodError などを投げる箇所も同様に:
::Kernel.raise ::NoMethodError, "undefined method ..."これにより、
BasicObjectにはraiseがなくても、::Kernel.raiseを呼ぶので例外を正しく発生できるDelegationError/NoMethodErrorなどの定数も::でルート名前空間から解決され、BasicObjectに依存しない
という状態になります。
2. 例外クラスをすべてルート名前空間で参照
NoMethodError, DelegationError などの例外クラス参照を、
::NoMethodError
::DelegationErrorのように、先頭に :: をつけてルート名前空間に固定しました。
BasicObject サブクラスでは Kernel などを include していないことも多く、そのクラスのコンテキストで定数解決させると失敗する可能性があるため、それを避けています。
3. テストの追加
activesupport/test/core_ext/module_test.rb にテストが追加されています。
ここでは、BasicObject を継承したクラスで delegate / delegate_missing_to を利用し、期待どおり動作することを確認するテストが加えられています。
イメージ的には:
class BasicProxy < BasicObject
extend ::ActiveSupport::Delegation
def initialize(target)
@target = target
end
delegate_missing_to :@target
end
obj = BasicProxy.new("string")
obj.upcase # => "STRING" が返り、例外も無限再帰も起きないのような使い方を想定したテストです。
- 影響範囲・注意点
影響を受ける場面
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 以降のバージョンで挙動を確認した方が安全です。
- もしアプリ側で
- 参考情報 (あれば)
- PR 本体: https://github.com/rails/rails/pull/56353
- 関連する Rails 機能:
Module#delegate/Module#delegate_missing_to(ActiveSupport::Delegation)
BasicObjectとKernelの関係: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-2文で)
Rails のRails::BacktraceCleanerが、アプリケーションルート直下のディレクトリ(※vendor を除く)にあるファイルのスタックトレースを「クリーン」しないように変更されました。これにより、./scriptや./packsなど独自ディレクトリに置いたスクリプトやユーティリティの呼び出し元が backtrace にきちんと表示されるようになります。
- 変更内容の詳細
背景・課題
- 多くのチームが、一時的・補助的なスクリプトを
./script,./scripts,./packsなどアプリケーションルート直下の任意ディレクトリに置き、bin/rails runner ./script/foo.rbのように実行している。 - デバッグ時に
BACKTRACE=1を付けてクエリの発行元を追いたいが、Rails.backtrace_cleanerは「フレームを減らして見やすくする」ために、アプリケーションコードとみなしていないパスをフィルタリングしてしまう。 - その結果、本当に知りたい
./script/xxx.rbなどユーザ定義ディレクトリのフレームが backtrace から消え、代わりに gem 内部 (activerecord/lib/…など) だけが見える状態になっていた。
問題例
# ./script/verbose_logs.rb
User.where(id: 1).load現在の main ブランチでは:
$ bundle exec rails runner ./script/verbose_logs.rb
User Load (1.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1BACKTRACE=1 を付けても:
$ 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 行と小さく、既存の挙動を崩さない形での拡張)。
変更後の挙動例
同じスクリプト:
# ./script/verbose_logs.rb
User.where(id: 1).loadPR 適用後は:
$ 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 など) のフレームは、従来どおり適度にクリーンされる。
- 影響範囲・注意点
影響を受けるケース
- 以下のような 「アプリケーションルート直下のカスタムディレクトリ」にコードを置いているプロジェクト:
./script,./scripts,./packs,./tools,./tasksなど
- それらを
bin/rails runnerで実行したり、アプリケーションから require して使っている場合に、ログ・エラー・クエリソースの backtrace に当該ファイルがより多く表示されるようになります。
とくにメリットが大きいのは:
- データパッチや一時的スクリプトを migration ではなく
./scriptに置いているチーム - モジュラーモノリス構成で
./packsにドメイン別パッケージを切っているチーム
注意点
- vendor 直下のコードは、従来どおり backtrace クリーン対象 です。
- vendor 以下を自前コードとして積極的に使っていて「vendor も backtrace に常に出したい」というニーズがある場合は、引き続き
Rails.backtrace_cleanerのカスタマイズが必要です。
- vendor 以下を自前コードとして積極的に使っていて「vendor も backtrace に常に出したい」というニーズがある場合は、引き続き
- 表示されるフレーム数が若干増える可能性がありますが、増えるのは基本的に「自分たちが書いたコード」のみなので、デバッグ上の恩恵のほうが大きいはずです。
ActiveSupport::BacktraceCleanerの API 自体は変わっていないため、既存で backtrace_cleaner を独自にカスタマイズしている場合も、衝突はほぼ起きにくい変更です。
- 参考情報 (あれば)
- 対象 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-2文で)
このPRは、Rails 起動時にミドルウェアスタックを構築する際、不要にActionDispatch::Requestをロードしてしまう「リクエスト関連のロードリーク」を解消するものです。ActionDispatch::Requestを eager load せず、autoload + load hook を活用することで、ブート時の不要な処理を避けています。
- 変更内容の詳細
主なポイントは「ActionDispatch::Request を明示的に require しないようにした」ことです。
変更1: Flash ミドルウェアでのリクエストパッチ方法の変更
action_dispatch/middleware/flash.rb で、フラッシュ用の機能を ActionDispatch::Request にパッチする際に、これまでは ActionDispatch::Request を直接ロードする形になっていましたが、それをやめて「autoload が ActionDispatch::Request を読み込んだタイミングでパッチする」ように修正しています。
イメージとしては、以下のような方針転換です(実際のコードは簡略化例):
# 以前のイメージ(例)
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::Request の require が1行削除されています。
# 削除されたイメージ
require "action_dispatch/request"ActionDispatch::Request は Rails 内で autoload されるクラスであり、ここで明示的に require する必要がないため、ブート時の不要なロードを避ける目的で削除されています。
CHANGELOG の更新
actionpack/CHANGELOG.md に、この挙動変更(ブート時の不要な request ロードをやめた)が記載され、利用者が挙動変化・最適化を把握できるようにされています。
- 影響範囲・注意点
影響範囲
ActionDispatch::FlashミドルウェアActionDispatch::Session::AbstractStoreを継承するセッションストア実装ActionDispatch::Requestに対してActiveSupport.on_load(:action_dispatch_request)フックを使って拡張しているコード
パフォーマンス・リソース観点
- Rails アプリの起動時(特にミドルウェアスタック構築時)に
ActionDispatch::Requestがロードされなくなるため、- 起動時間がわずかに短縮される可能性
action_dispatch_requestの load hook に重い処理を仕込んでいるアプリでは、その処理が「初期化時」ではなく「実際にリクエストが必要になったタイミング」まで遅延される
が期待されます。
- Rails アプリの起動時(特にミドルウェアスタック構築時)に
互換性・注意点
- 公開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
- 公開APIとしては
- 参考情報 (あれば)
- 元になった議論: 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-2文で)
Rails の guides 用テスト (guides/bug_report_template_test) がbin/testでは動くのにrake testでは実行されていなかった問題を修正した PR です。あわせて、guides のテスト用 Rake タスク定義を、フレームワーク本体のテストタスクと同じスタイルに整理しています。
- 変更内容の詳細
問題の背景
bin/testで実行した場合はテストが動くのに、rake testではテストが 0 件になる状態だった。- 原因は autorun がロードされていない ためで、
rake test側では Minitest の自動実行が働かず、テストが実行されていなかった。
guides/test/test_helper.rb の変更
test_helper に autorun 読み込みを追加していると考えられます:
# guides/test/test_helper.rb
require "minitest/autorun" # ← このような行が追加されているこれにより、rake test 経由でテストをロードした場合でも、Minitest が自動的に実行されるようになります。
guides/Rakefile の変更
guides 用の Rake タスク定義を、railties/Rakefile など他のコンポーネントと同じパターンにそろえています。おおよそ以下のような方向の変更です:
Rake::TestTaskの使い方を、本体側と同じスタイルに修正- テストファイルのパターン指定や load path 設定を整理
- 余計な独自処理をなくし、他コンポーネントと一貫性を持たせる
例として、他コンポーネントのテストタスクに近い形:
# 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 件」状態を避けられます。
- 影響範囲・注意点
- 影響範囲は Rails guides のテスト (特に bug_report_template_test) に限定されます。アプリケーションの通常のテスト (
bin/rails test,rake test) への影響はほぼありません。 - guides のテストを
rake testで回している開発者・CI 環境では、これまで 0 件だったテストが 正しく実行されるようになる ため、失敗テストが顕在化する可能性があります。 minitest/autorunの二重ロードなどによる副作用は一般的には問題になりませんが、独自に Minitest 実行フローをいじっている場合は念のため挙動を確認するとよいです。- guides 下の Rake タスク定義をカスタマイズしている場合は、今回の変更との差分を見て、必要に応じて自分のプロジェクト側もタスク定義を整理する価値があります。
- 参考情報 (あれば)
- PR 本体: https://github.com/rails/rails/pull/56347
- 類似のテストタスク定義 (railties/Rakefile):
https://github.com/rails/rails/blob/4f4e0acaf558f6adf7df3ac23a51beb470463901/railties/Rakefile#L166-L173 - この問題への言及: https://github.com/rails/rails/pull/55987#issuecomment-3641860716
#56252 [ci skip] Improve docs about system test generation
マージ日: 2025/12/11 | 作成者: @callmesangio
- 概要 (1-2文で)
このPRは、Railsのシステムテスト関連ドキュメント(Testing GuideおよびAPIドキュメント)の内容を、現在の挙動に合わせて改善・修正したものです。特に「system test は自動生成されない」「application_system_test_case.rbはデフォルトでは作られない」という点を明確にしています。
- 変更内容の詳細
テストガイド (guides/source/testing.md) の修正
(1) 「Test Setup」セクションの ls -F test 出力を実際の構成に合わせて更新
rails new直後などでls -F testを実行したときに表示される内容が、現行のRailsのディレクトリ構成と一致するようにコードブロックを修正しています。- 具体的には「system test 関連のファイルやディレクトリ」が、デフォルトでは存在しない状態を反映した出力になったと考えられます。
例(イメージ・実際のPRでは具体的な中身が更新されている):
$ ls -F test
controllers/
models/
test_helper.rbのように、system/ や application_system_test_case.rb が最初からは存在しないことを示す方向に修正。
(2) 「Test Directories」セクションの文章を再構成
- 「system test 関連の内容(ディレクトリ・基底クラスなど)は、自動で用意されるのではなく“明示的に生成する必要がある”」ことを強調するように文言が書き換えられました。
- これにより、以下のような誤解を避けます:
- 新規アプリ作成時に
test/systemやapplication_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.rbとtest/systemディレクトリが同時に生成されることなどが、正確な形で示されるようになります。
例(イメージ):
$ 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を定義する際の推奨パス/ファイル名」の説明更新
- 「
コード自体のロジック変更ではなく、説明文の整合性合わせと見てよいです。
- 影響範囲・注意点
ランタイム挙動への影響はほぼ無し
- 変更はガイドとコメント中心で、実際のテスト実行やシステムテストの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内容に沿って表現を修正すると混乱が減ります。
- 「
- 参考情報 (あれば)
フォローアップ対象となったPR:
- #55743
- #56272
これらで導入/変更されたsystem test関連仕様に対して、説明の抜けや古い記述を補正する目的のPRです。
実際にsystem testを有効化する際の実務的ポイント:
- 新規プロジェクトでsystem testを使いたい場合、まずは:bashなどを一度実行して、
bin/rails generate system_test sampletest/application_system_test_case.rbとtest/systemを生成してから他のsystem testを追加していくとスムーズです。
- 新規プロジェクトでsystem testを使いたい場合、まずは:
#55294 [RF-DOCS][ci skip] Update Threading and Code Execution Guide
マージ日: 2025/12/11 | 作成者: @OughtPuts
- 概要 (1-2文で)
このPRは Rails ガイド「Threading and Code Execution (スレッドとコード実行)」を大幅にアップデートし、非同期 Active Record や Isolated Execution State、CurrentAttributesなど、現行 Rails のスレッド関連機能を整理・追記したドキュメント改善です。ガイドの表現・構成も見直され、より読みやすく実践的な内容になっています。
- 変更内容の詳細
※コードの挙動を変える PR ではなく、ガイドの内容・構成を更新するものです。
2-1. ガイド全体の書き換え・構成変更
- 文言を平易にしつつ、スレッドや並行実行に慣れていない Rails 開発者でも理解しやすいように説明をリライト。
- セクション構成の見直しにより、
- 「どこまでが Rails が面倒を見る部分か」
- 「アプリ側が注意すべきスレッド安全性や状態管理」 が順序立てて理解しやすくなるように再構成。
- 以前付いていた「work in progress (作業中)」ラベルを削除し、正式なガイドとして位置づけ。
2-2. 非同期 Active Record に関する情報の追加
Rails 7 以降で強化されている「非同期なデータベース操作」について、スレッド&実行モデルの観点から説明が追加されています。
典型的には以下のようなポイントが整理されていると考えられます:
ActiveRecord::Baseやクエリメソッドの非同期 API(例:load_async)がどのようなスレッドで動くか- その非同期処理が Rails のエグゼキューションコンテキスト (Current attributes, I18n, timezone など) を引き継ぐのかどうか
- 非同期クエリを使うときに「スレッド安全性」や「接続プール」にどのような影響があるか
イメージしやすいサンプル:
# コントローラ内など
def index
# 非同期にロード
@users = User.where(active: true).load_async
# ここでは他の処理を先に進める
do_some_heavy_logic
# ビューで @users を参照したタイミングで結果が利用可能になる
endガイドでは、こうした非同期処理が裏側でどのようなスレッドを使うか、アプリ開発者は何に気を付けるべきか、が説明されているはずです。
2-3. ラップされたアプリケーションコードの例の追加
「コードがどのように『ラップされて』実行されるか」の具体例が増えています。
これは、Rails が各種ミドルウェアやエグゼキューションラッパーを通してアプリケーションコードを実行する構造を解説するものです。
典型的には以下のような点がサンプルコード付きで整理されます:
Rails.application.executor.wrapなどで実行コンテキストを管理する仕組み- スレッドプール上でコードを実行する際に「必ず Executor で wrap すべき」という推奨パターン
サンプルイメージ:
Rails.application.executor.wrap do
# このブロックの中は:
# - CurrentAttributes
# - I18n.locale
# - Time.zone
# などが正しく設定・リセットされる
UserMailer.welcome(user).deliver_later
endあるいは、独自スレッドを立てる場合のパターン:
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 との統合により、各スレッド・各実行単位で値が正しくセット・クリアされる
サンプルイメージ:
# 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" ラベルに相当する記述が削除され、このガイドが正式な完成度のドキュメントとして扱われるようになりました。- これにより、利用者は「未完成の情報かもしれない」という懸念なしに参照しやすくなります。
- 影響範囲・注意点
実行コードには一切変更なし
変更はすべてguides/source配下であり、Rails 本体の挙動・API・パフォーマンスには影響しません。正しい使い方がより明示される
- 非同期 Active Record、独自スレッド、バックグラウンド処理などで「Executor で wrap する」「
CurrentAttributesを使う」といった推奨パターンがドキュメントとして明確になるため、今後の新規コードでは「暗黙の Thread-local 依存」や「グローバル状態のリーク」を避けやすくなります。 - 既存アプリで独自にスレッドを使っている場合、このガイドを読むと「やるべき初期化やクリーンアップ」がわかり、リファクタリングの指針になります。
- 非同期 Active Record、独自スレッド、バックグラウンド処理などで「Executor で wrap する」「
ドキュメントとコードベースの整合性
- ガイドは最新の Rails 実装 (特に Executor / Isolated Execution State /
CurrentAttributes/ 非同期 Active Record) を前提に書かれていると考えられるため、古い Rails バージョンでは一部 API が存在しない/挙動が異なる可能性があります。 - 自分のアプリの Rails バージョンとガイドの対象バージョン (edge / stable) を確認して読む必要があります。
- ガイドは最新の Rails 実装 (特に Executor / Isolated Execution State /
- 参考情報 (あれば)
- Rails Guides – Threading and Code Execution (更新対象ガイド)
https://edgeguides.rubyonrails.org/threading_and_code_execution.html - ActiveSupport::CurrentAttributes
https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html - Rails Executor / Isolated Execution State に関する設計 (英語ですが、関連する Issue/PR がよく議論ソースになります)
- https://github.com/rails/rails/pull/33780 (Executor の改善系の例)
この PR 自体はドキュメント変更のみですが、非同期処理・マルチスレッドを使うアプリでは一度読み通しておくと設計・デバッグに役立つ内容です。
#56345 Add SVG renderer
マージ日: 2025/12/11 | 作成者: @thiagoyoussef
- 概要 (1-2文で)
Rails にformat.svg { render svg: ... }という形で利用できる SVG レンダラーが追加され、コントローラのrespond_toで SVG を第一級のレスポンス形式として扱えるようになりました。これに伴い、MIME type 定義とテスト、CHANGELOG が更新されています。
- 変更内容の詳細
2-1. SVG レンダラーの追加
actionpack/lib/action_controller/metal/renderers.rb に SVG 用のレンダラーが追加されています。
使い方は PR のサンプルのとおりです。
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_jsonやto_xmlと同じパターン)。respond_toブロック内でformat.svgを使えるようになり、Accept: image/svg+xmlを受け取った場合のコンテンツネゴシエーション対象になる。
内部的には以下のようなことが行われていると考えられます(疑似コード):
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 行のみですが、以下のいずれか(または同等)が追加/修正されています。
Mime::Type.register "image/svg+xml", :svgこれにより:
respond_to :html, :svgでsvgを指定できる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:が使える」旨の記述
これにより、利用者がリリースノートからこの機能を把握できるようになっています。
- 影響範囲・注意点
影響範囲
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を再定義する
- 参考情報 (あれば)
- 元実装: Basecamp/fizzy からの抽出
https://github.com/basecamp/fizzy/commit/b8e93c4f8a6b87866ce3e0b50dc7645f0d926219 - 類似 API:
- JSON:
render json: @record - XML:
render xml: @record - 今回追加:
render svg: @object(to_svgに委譲)
- JSON:
#56329 [ci skip][docs] Fix RDoc markup for DATE_FORMATS constant references
マージ日: 2025/12/11 | 作成者: @Yuhi-Sato
- 概要 (1-2文で)
このPRは、DATE_FORMATS定数への言及に対して RDoc 用のマークアップ(+DATE_FORMATS+)を付けることで、日付・日時関連のドキュメントコメントを整備・統一したものです。コードの挙動には一切変更はなく、ドキュメント生成時の表示改善のみを目的としています。
- 変更内容の詳細
対象は ActiveSupport の日付・時刻拡張のドキュメントコメントで、以下の3ファイルです。
activesupport/lib/active_support/core_ext/date/conversions.rbactivesupport/lib/active_support/core_ext/date_time/conversions.rbactivesupport/lib/active_support/core_ext/time/conversions.rb
それぞれのファイルで、RDocコメント内の「DATE_FORMATS 定数」への参照を、RDocの定数マークアップに修正しています。
例(イメージ、実際の対象コメントを簡略化):
修正前:
# You can add your own formats to the DATE_FORMATS hash.修正後:
# You can add your own formats to the +DATE_FORMATS+ hash.RDoc では +定数名+ という形で記述することで、HTML生成時に等幅フォント&コードとして扱われ、他の場所ですでに使われていた表記(+DATE_FORMATS+)と揃うようになります。
このPRは、Date, DateTime, Time の各 conversions 拡張で同様のコメント表現を統一するものです。
- 影響範囲・注意点
影響範囲
- ドキュメントコメントのみの変更であり、Rubyコード(挙動・API・署名)には一切影響しません。
- 生成される RDoc/HTML ドキュメント上での見た目・スタイルが変わる(
DATE_FORMATSがコードとして強調される)だけです。 - 既存アプリケーションやテストへの影響はありません。
注意点
DATE_FORMATS自体の仕様や利用方法(例:Time::DATE_FORMATS[:short]を追加するなど)は何も変わっていません。ドキュメントの見栄えと一貫性の改善のみです。- Rails内の他の箇所(
ActiveRecord::Integration,ActiveSupport::TimeWithZoneなど)ですでに使われていた表現に合わせた統一なので、新しい書き方を導入したわけではありません。
- 参考情報 (あれば)
- 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-2文で)
Rails 8.1 以降で、カスタムロケール使用時にpluralizeヘルパーが英語 (:en) へのフォールバックを正しく参照できなくなっていた不具合を修正する PR です。ActiveSupport::Inflector::Inflections.instance_or_fallbackがフォールバックチェーン内の:enを正しく扱うように変更されています。
- 変更内容の詳細(あればサンプルコードも含めて)
問題の背景
- Rails 8.1 でのパフォーマンス最適化(コミット
2ab34cd)により、英語の Inflections は- 以前:
@__instance__ - 以後:
@__en_instance__に保存されるようになりました。
- 以前:
- しかし、
Inflections.instance_or_fallback(locale)はフォールバックチェーンを辿る際に@__instance__しか見ていなかったため、- 例: フォールバックチェーンが
[:custom, :en]のとき :enを探しに行っても@__en_instance__を見に行かない- 結果として、「
custom→en→ …」というフォールバックにおける英語の規則が見つからない
- 例: フォールバックチェーンが
- これにより、
pluralizeヘルパーがカスタムロケールで英語規則へフォールバックすべき場面でも、正しく動作しないケースが発生していました(Issue #56339)。
修正内容
activesupport/lib/active_support/inflector/inflections.rb に 1 行の修正が入り、instance_or_fallback がフォールバック処理中に :en を見つけた場合、@__en_instance__ を参照するように変更されています。
擬似コードイメージ(実際のコードを読みやすく意訳):
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 が正しく使われることを確認
イメージ:
I18n.available_locales = [:custom, :en]
I18n.fallbacks.map(custom: [:custom, :en])
assert_equal "posts", "post".pluralize(locale: :custom)
# custom で定義がなくても、en の規則で "posts" になることを確認- 影響範囲・注意点
- 影響を受けるのは以下のようなケースです:
- Rails 8.1 以降
- カスタムロケール(例:
:ja,:customなど)を使っている - そのロケールのフォールバックに
:enが含まれている(例:[:ja, :en]) - カスタムロケール側には該当する Inflections 規則がないが、
:enにはある
- この修正により、
- 以前のバージョン(8.1 導入前)と同じように、フォールバックとしての英語 Inflections が再び機能する
- 既存の英語ロケールの挙動は変わらない
- パフォーマンス最適化(
@__en_instance__を分ける)自体は維持されたまま、参照だけが正しくなった形
- 既に 8.1 の挙動に合わせて「英語に頼らないようカスタム Inflections を追加した」プロジェクトがあっても、そのルールは引き続き優先的に使われるため、後方互換性上の問題は小さいと考えられます。
- 参考情報 (あれば)
- 対応 Issue: Fixes #56339
(pluralizeがカスタムロケールで英語フォールバックを無視する不具合) - 影響箇所:
ActiveSupport::Inflector::Inflections.instance_or_fallbackpluralizeヘルパーやActiveSupport::Inflectorをロケール付きで利用するコード全般
- 関連コミット:
2ab34cd
英語 Inflections を@__en_instance__に分離するパフォーマンス最適化が元原因。
#56335 Analyze attachments before validation
マージ日: 2025/12/10 | 作成者: @jeremy
- 概要 (1–2文で)
Active Storage の添付ファイル(画像・動画等)のメタデータ(幅・高さ・再生時間など)を「モデルのバリデーション前」に取得できるようになり、avatar.metadata[:width]などを使ったサーバーサイドのバリデーションが素直に書けるようになりました。あわせて、メタデータ解析のタイミングを:immediately / :later / :lazilyから選べる新オプションが導入されています。
- 変更内容の詳細
2-1. バリデーション前にメタデータが利用可能に
これまで Active Storage の metadata は、アップロード後に非同期で解析されたり、最初のアクセス時に解析されたりとタイミングが不定で、「バリデーション時点ではまだ width/height が入っていない」問題がありました。
このPRでは、(ローカルIO経由のアップロードに対して)バリデーション実行前にメタデータ解析を行うように変更され、次のようなコードが安全に書けます:
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
endvalidate が走る時点で avatar.metadata に幅・高さなどが入っていることを前提にできるようになりました(後述の例外: 直接アップロード時)。
2-2. メタデータ解析タイミングの設定オプション analyze:
添付関連の関連定義 (has_one_attached / has_many_attached) で、解析タイミングを指定できるオプションが追加されています。
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. 全体デフォルト設定の追加
アプリケーションレベルでデフォルトを指定する設定が追加されています:
# 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::Modelでanalyzeオプションを受け取り、関連リフレクション経由で添付ごとの設定として保持。- engine および configuration クラス (
Rails::Application::Configuration) にconfig.active_storage.analyzeを追加。 - ガイド (
active_storage_overview.md,configuring.md) に新オプションと推奨の使い方を追記。 - テスト一式の追加・更新により、新挙動とオプション組み合わせの検証が行われている。
- 影響範囲・注意点
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
- 全体設定:
- メタデータ未設定状態を前提としていた既存コードがある場合(「初回アクセス時に解析されるはず」という前提)も挙動が変わるため、依存関係がないか確認した方が安全です。
- 参考情報 (あれば)
- 変更の中心となるクラス/ファイル:
activestorage/app/models/active_storage/attachment.rbactivestorage/lib/active_storage/attached/model.rbactivestorage/lib/active_storage/attached/changes/create_one.rbrailties/lib/rails/application/configuration.rbconfig/initializers/new_framework_defaults_8_2.rbテンプレート
- ドキュメント:
guides/source/active_storage_overview.md- メタデータ解析と
analyzeオプションの使い方が追記。
- メタデータ解析と
guides/source/configuring.mdconfig.active_storage.analyzeについての説明が追加。
- 運用設計の目安:
- サーバーサイドのメタデータバリデーションを重視 →
:immediately - レスポンス速度・バックグラウンド処理優先 →
:later - 完全に手動制御したい、独自ジョブで解析したい →
:lazily
- サーバーサイドのメタデータバリデーションを重視 →
#56017 Add Rails::CodeStatistics#register_extension to register file extensions for rails stats
マージ日: 2025/12/10 | 作成者: @taketo1113
- 概要 (1-2文で)
rails statsが対象とするファイル拡張子を、安全に追加できるRails::CodeStatistics.register_extensionが新設されました。これにより、将来のデフォルト拡張子の変更と競合せずに、独自や gem 由来の拡張子を統計対象に含められます。
- 変更内容の詳細
背景整理
以前の 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 に拡張子を登録するためのメソッドが追加されました。
Rails::CodeStatistics.register_extension("slim")ポイント:
- 「デフォルト PATTERN + 自分の拡張子」という形で、既定の挙動を保ったまま拡張子を増やせる。
- 内部的には、
register_extensionで登録された拡張子をpatternに反映する形になっており、Rails 側がデフォルトの PATTERN を将来変更しても、それに追随できます。 - 既存の
Rails::CodeStatistics.patternの直接上書きは 引き続きサポートされる(互換性維持)。
テスト・実装の方向性
railties/lib/rails/code_statistics.rbにregister_extension実装を追加。railties/test/code_statistics_test.rbに、登録した拡張子がrails stats対象になることを確認するテストが追加。railties/CHANGELOG.mdに、この新機能のエントリが追記されています。
※PR 本文から読み取れる範囲では、register_extension は「文字列の拡張子名(例: "slim")」を受け取り、pattern に組み込む仕組みになっています。
- 影響範囲・注意点
アプリ側:
これまで
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の初期化時などに:rubyRails::CodeStatistics.register_extension("slim")を呼べば、他の gem やアプリの設定を壊さずに自分の拡張子を
rails statsに加えられます。以前のように
Rails::CodeStatistics.pattern =を gem 側で定義してしまうと、アプリ側・他の gem 側の上書きと競合するので、今後はregister_extension一択が望ましいです。
互換性:
patternの直接上書きは残っているため、Rails 8.1.0 で既に導入されているコードも壊れません。- 将来的に
patternを非推奨にする可能性は示唆されていますが、現時点ではそのような変更は行われていません。
- 参考情報 (あれば)
- 本 PR:
- Add
Rails::CodeStatistics#register_extensionto register file extensions forrails stats(#56017)
- Add
- 関連 PR:
Rails::CodeStatistics.patternによる PATTERN 上書きを導入した PR: https://github.com/rails/rails/pull/55734
#56323 Ensure generated yml files do not have extra blank lines
マージ日: 2025/12/10 | 作成者: @jeromedalbert
- 概要 (1-2文で)
Rails 8.1.1 で生成されるdeploy.ymlとdatabase.ymlに紛れ込んでいた不要な空行を削除し、今後同様の空行が紛れ込まないようにテストを追加した PR です。見た目の調整・品質向上が目的で、挙動や仕様の変更はありません。
- 変更内容の詳細
対象ファイル
railties/lib/rails/generators/rails/app/templates/config/deploy.yml.ttrailties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml.ttrailties/test/generators/app_generator_test.rb
テンプレートから余計な空行を削除
deploy.yml テンプレートと sqlite3 用 database.yml テンプレート (*.yml.tt) の末尾または途中にあった「意味のない空行」が 1 行ずつ削除されています。
イメージとしては次のような差分です(あくまで構造イメージ):
# 修正前 (例)
default: &default
adapter: sqlite3
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
# ここに空行があった
development:
<<: *default
database: storage/development.sqlite3# 修正後 (例)
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.ymlとconfig/database.ymlを読み込み - 不要な連続空行や末尾の空行が含まれていないか確認
といった検証を行うことで、テンプレート修正時にうっかり空行を挿入してしまう問題を自動テストで検出できるようにしています。
- 影響範囲・注意点
影響範囲
- Rails アプリ生成時 (
rails new) に出力されるconfig/deploy.ymlとconfig/database.ymlのフォーマットのみが変わります。 - 設定値やキー構造、アプリケーションの挙動には一切影響しません。
- 既存アプリには影響せず、新規生成またはジェネレータを通じて生成されるファイルにのみ影響があります。
- Rails アプリ生成時 (
注意点
- YAML としてのパース結果は変わらないため、デプロイツールや DB 接続には影響ありません。
- ただし、CI などで「生成ファイルとテンプレートの厳密比較(空白や空行も含めた差分チェック)」をしている場合には、
deploy.yml/database.ymlの生成結果が 1 行分短くなるなどの違いが出る可能性があります。 - PR の性質上、CHANGELOG は更新されていません(ユーザーに見える挙動変更ではなく軽微なフォーマット修正のため)。
- 参考情報 (あれば)
この PR が対処している背景となる PR:
deploy.ymlの空行発生元:
https://github.com/rails/rails/pull/55646database.ymlの空行発生元:
https://github.com/rails/rails/pull/50463- 同様の「空行掃除」をした過去の PR:
https://github.com/rails/rails/pull/55940
これらの変更の副作用でテンプレート中に余分な空行が入り込み、本 PR と追加されたテストで再発防止を図っています。
#56327 Active Storage: eager analysis and local file processing for immediate variants
マージ日: 2025/12/10 | 作成者: @jeremy
- 概要 (1-2文で)
Active Storage でprocess: :immediatelyを指定したバリアント付き添付ファイルについて、バリデーション時にメタデータ解析(analysis)を即時実行し、かつアップロード直後のローカルファイルを直接使って解析・変換を行うようにした PR です。これにより、画像サイズなどのメタデータを使ったカスタムバリデーションがやりやすくなり、無駄な再ダウンロードも減ります。
- 変更内容の詳細
2-1. 「process: :immediately」なバリアントの挙動変更
これまで:
- Active Storage の添付時に
variantを定義していても、- 画像の analysis(width/height などのメタデータ取得)
- variant の生成(リサイズなど)
- は基本的に遅延実行 (on-demand) で、初回アクセス時に行われることが多かった。
今回の変更:
process: :immediatelyオプションを指定したバリアントを持つ添付については、- レコードのバリデーション段階で blob の analysis を実行
- その結果得られたメタデータを、同じバリデーション中のカスタム検証で利用可能
- 例: モデル側で画像サイズをチェックしたいケース
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しただけのケース(この場合は従来どおりストレージから取得)
- すでに存在する Blob を
イメージコード(概念的な流れ):
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 全体としては内部実装の変更が主ですが、挙動として開発者が感じる点は次のとおりです。
ActiveStorage::Attachment:- attach 処理時に、アップロード IO を保持して analysis/variant 処理に渡すための変更。
process: :immediatelyな variant を検知して、バリデーション中に analysis を走らせるロジックが追加。
ActiveStorage::Blob/Analyzable:analyze実行時に、「もし uploadable io が指定されていれば、それを使って解析」する経路が追加。- そうでない場合は従来どおりサービス(S3 など)からダウンロード。
ActiveStorage::Variant/VariantWithRecord:- 変換時に「uploadable io を活用する」経路を追加。
- バリアント生成時に余計なダウンロードが発生しないようにする。
テスト:
Attachmentのテストに 100 行以上追加されており、- バリデーション時に analysis が行われること、
- uploadable io を使って処理していること、
- などが確認されている。
- 代表表現コントローラテストや integration テストも微調整され、今回の挙動を前提にしたテストになっている。
- 影響範囲・注意点
3-1. 想定されるメリット
- カスタムバリデーションが書きやすくなる:
process: :immediatelyな variant を定義しておけば、画像の width/height といった metadata をバリデーション時に安全に使える。
- パフォーマンス向上/無駄な通信削減:
- アップロード直後の処理(analysis・variant生成)でストレージからの再ダウンロードが不要になり、特に S3 などリモートストレージのときに効く。
- レイテンシ削減:
- ユーザーが初めて画像を表示するタイミングで重い処理を行わず、事前に済ませたい場合に
process: :immediatelyが機能しやすくなる。
- ユーザーが初めて画像を表示するタイミングで重い処理を行わず、事前に済ませたい場合に
3-2. 既存コードへの影響と注意点
process: :immediatelyを使っている or 使おうとしているプロジェクト:- この PR により、「即時処理」としての意味がより明確になり、バリデーション段階での解析が行われます。
- すでに image metadata を使ったバリデーションをしている場合、
- これまで「初回アクセスまで metadata が nil だった」ケースで、今後は metadata が入っている可能性が増えるため、動作が「改善される」方向の変更。
- ただし、バリデーション中に画像解析が走るということは、
- バリデーションのレスポンスタイムが若干伸びる可能性があります(画像サイズ・ネットワーク環境にもよる)。
- 特に大量の添付を一度に処理するフォームでは注意。
既存 Blob を attach するケース:
# 既に存在する blob を別モデルに付け直すようなケース
post.image.attach(existing_blob)- この場合は、「uploadable io」が存在しないため、従来どおりストレージからダウンロードして analysis/variant 処理を行います。
- この PR の「ローカルファイル利用による最適化」は適用されない点に注意。
- ストレージ負荷・コスト観点:
- この PR による最適化は「アップロード直後」の処理に限られるため、
- ユーザーが後から別の画面で初回アクセスする場合など、従来どおりダウンロードが発生するケースも当然残ります。
- とはいえ、「アップロード直後に即座に処理する」設計のアプリ(たとえば投稿完了時に全バリアントを生成しておく)では、ストレージからの無駄な読み出しが削減され、トラフィックとコストに効きます。
- バリデーション・コールバック順序への影響:
- analysis は「バリデーション中」に走るため、
before_validationなどのコールバック内で attachment にアクセスしたときの挙動は、以前と若干タイミングが変わる可能性があります。- ただし基本的には「より早く metadata が使えるようになった」だけなので、副作用は限定的と思われます。
- 参考情報 (あれば)
- 元 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-2文で)
Rails 7.2 系ブランチ(7-2-stable)に対して、本来取り込むべきだった PR #56275 をバックポートし、Bundler 4.0.0 でテストが落ちる問題に対応する修正です。
railties のジェネレーター用テストヘルパーを調整し、7-2-stable でも CI が安定して通るようにしています。
- 変更内容の詳細
- 対象ファイル:
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 ランタイムの挙動ではなく、「テスト環境のセットアップ方法」をいじる種類の変更です。
- 影響範囲・注意点
影響範囲:
railtiesの「ジェネレーターのテスト」を実行する際の挙動に限定されます。- アプリケーション本体のランタイムコードや、実際のジェネレーターの生成結果の挙動への直接的な変更はありません。
- 主な影響は Rails 自身の CI・開発者が Rails を checkout してテストスイートを走らせるケースです。
注意点:
- Bundler 4.0.0 環境で 7-2-stable ブランチのテストを回していた場合、今回の修正によりテストが通るようになるはずです。
- バックポート時に
#53647とのコンフリクトを手作業で解消しているため、テスト結果の確認が重要になりますが、この PR 自体は CI を通過してマージされている前提です。 - アプリケーション側で特別な対応が必要になるような破壊的変更は含まれていません。
- 参考情報 (あれば)
- 元 Issue: https://github.com/rails/rails/issues/56271
- 元 PR(バックポート元): https://github.com/rails/rails/pull/56275
- コンフリクトが発生した PR: https://github.com/rails/rails/pull/53647
- CI 失敗の例(Buildkite): https://buildkite.com/rails/rails/builds/124364
#56336 Split dbs_test.rb to whittle away at CI bottlenecks
マージ日: 2025/12/9 | 作成者: @jeremy
- 概要 (1-2文で)
railties/test/application/rake/dbs_test.rbが CI の大きなボトルネックになっていたため、テストの見積り時間を現実に合わせて更新しつつ、テスト構成を整理・分割して並列化しやすくした PR です。重複していた ERB YAML テストを統合したうえで、DB 関連のテストを4つのファイルに分割し、CI のタイムアウトと全体時間の削減を狙っています。
- 変更内容の詳細
パフォーマンス問題と背景
- 問題となっていたファイル:
railties/test/application/rake/dbs_test.rb - CI の挙動:
- テスト時間の見積もりが 82秒 と登録されていたが、実際は 20分以上 実行されるケースがある
- テスト構造:
- 49個の isolated test があり、それぞれが
- プロセス fork
- フルな Rails アプリ構築
- 1ファイル内にまとまっているため、CI の「ファイル単位」の並列化が効かず、1ワーカーで全て処理していた
- 49個の isolated test があり、それぞれが
このため、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 にすべて詰め込まれていたテストを、役割ごとに分割しています。
新しく作成されたファイル:
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 特有の処理確認
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 動作
railties/test/application/rake/dbs_setup_test.rb(+301行)- DB セットアップ全般(
db:setup,db:reset,db:migrateなど)に関するテストを分離 - 例:
- 初回セットアップ・リセット時の挙動
- 既存DB・既存スキーマがある場合の挙動
- seed 実行の確認 (
db:seed,db:setup内での seed 実行など)
- DB セットアップ全般(
railties/test/application/rake/dbs_test.rb(+102/-745)- 元の肥大化したファイルから大幅に削減(745行削除、102行に縮小)
- より「共通・ベースライン的な DB rake テスト」だけが残されている構成に近い
ポイント:
- 1つの巨大ファイルに49個の isolated テストがあった状態から、
- 複数のテストファイルに論理的に分割
- CI は「ファイル単位」でテストジョブを分散できるため、4つのワーカーで並列実行可能 になる
+802 / -746という統計からも、主に「リファクタ的な移動・分割」であり、テストカバレッジ自体は維持・整理されていると考えられます。
- 影響範囲・注意点
影響範囲(主にテスト・CI 周り)
railtiesの rake DB テスト構成が大きく整理・分割されているため、- CI 設定で特定ファイルを直接指定していた場合(例:
ruby -Itest railties/test/application/rake/dbs_test.rbのようなジョブ)は、新ファイル名に追随する必要があります。 - ただし一般的な
bundle exec rake testやbin/testベースの実行であれば、そのまま動作します。
- CI 設定で特定ファイルを直接指定していた場合(例:
- テスト内容自体は基本的に移動・統合であり、仕様レベルの挙動変更は行っていない想定です。
テスト時間の見積もり変更
- Rakefile での duration estimate の変更により、CI の「仕事配分」が変わります。
- これにより、テスト全体のワーカーごとの時間バランスが変わるため、
- 一部の CI 実行時間が短くなり
- 別のワーカーに負荷が寄る場合がありますが、
- トータルとしては「より均等」かつ「タイムアウトしにくい」構成を目指しています。
ローカル開発への影響
- ローカルで
railtiesの rake DB 周りのテストをいじる開発者は、- どのファイルにどの系統のテストを書くかを整理しやすくなっています
- Postgres 固有 →
dbs_postgresql_test.rb - スキーマ系 →
dbs_schema_test.rb - セットアップ/リセット系 →
dbs_setup_test.rb - その他・共通 →
dbs_test.rb
- Postgres 固有 →
- どのファイルにどの系統のテストを書くかを整理しやすくなっています
- テストファイルの責務が明確になったため、今後のテスト追加・メンテナンスもやりやすくなります。
- ローカルで
- 参考情報 (あれば)
- 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-2文で)
Rails の並列テスト実行時に、テストをワーカーへ決定論的(再現可能)に割り当てる仕組みが入り、「同じ--seedとワーカー数なら同じワーカーが同じ順番でテストを実行できる」ようになりました。
加えて、オプションwork_stealing: trueによってアイドルワーカーが他のワーカーからテストを奪う「ワークスティーリング」を有効化できるようになり、実行時間の平準化と再現性のトレードオフを選べます。
- 変更内容の詳細
全体像
これまでの Rails 並列テストは「どのワーカーがどのテストを実行するか」があまりコントロールされておらず、同じ --seed を指定してもテスト間依存によるフレーク(並列時のみ落ちるテスト)が再現しにくい問題がありました。
この PR では:
- テスト -> ワーカーの割り当てを「ラウンドロビン + 決定論的」に変更
- 任意で「ワークスティーリング」をオンにできるようにして、実行時間を平準化できるようにする
- これらを支えるために新しい
TestDistributorとThreadPoolExecutorを導入 - ドキュメントとテストを整備
という変更が入っています。
主要な技術的変更点
1. 決定論的なテスト割り当て (TestDistributor)
新ファイル:activesupport/lib/active_support/testing/parallelization/test_distributor.rb
役割:
- 並列実行する「テストキュー(テストファイル/テストケースの一覧)」を管理
- ワーカーへのテスト配分を「ラウンドロビン + 決定論的」に行う
- オプションで「ワークスティーリング」を提供
ポイント:
- 「同じ seed」「同じワーカー数」であれば、テストが常に同じワーカー順・テスト順で実行される
- これにより、テスト間依存が原因の失敗(例えば「Aが先に走るか後に走るかでBの結果が変わる」)を、再現しやすくなります
ラウンドロビンのイメージ:
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.rbactivesupport/lib/active_support/testing/parallelization/worker.rbactivesupport/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 名はコードに依存しますが、概念として):
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.rbactivesupport/test/parallelization/server_test.rbactivesupport/test/parallelization/test_distributor_test.rbactivesupport/test/parallelization/thread_pool_executor_test.rb
主な検証内容:
- 同じ seed・ワーカー数で同じ配分になること(決定論的であること)
- work_stealing の有無で挙動が変わること
- スレッドプールエグゼキュータが正しくタスクを処理し、完了すること
- 影響範囲・注意点
影響範囲
- Rails で
ActiveSupport::TestCase.parallelizeによる並列テストを使っているプロジェクト全般- 特に CI 上で
PARALLEL_WORKERSを使っているケースや、自前でワーカー数を設定しているプロジェクト
- 特に CI 上で
- テスト間依存によるフレークテストがあるプロジェクトでは、失敗パターンの再現性が上がる(=バグを見つけやすくなる)
実行時間への影響
- PR 説明上は、「ワークスティーリング有無・変更前後で統計的に有意な差はまだ見えていない(分散が大きい)」という評価
- とはいえ、極端に遅いテストが偏っている場合、
work_stealing: trueの方が平均実行時間・ばらつきは改善する可能性がある
注意点・トレードオフ
再現性 vs 実行時間
- フレークテストをデバッグするフェーズ:
work_stealing: false推奨--seedとワーカー数を固定して再実行すると、同じワーカー順・実行順で走らせやすい
- 普段の CI での高速化・安定化:
work_stealing: trueを検討(プロジェクトの特性と計測次第)
- フレークテストをデバッグするフェーズ:
seed とワーカー数の固定
- 再現性を高めるには、CI などで
--seedとワーカー数をログに残しておくとよいです - 過去ログから seed とワーカー数を再利用してローカルで再実行すれば、「どのワーカーがどのテストを実行して落ちたか」をかなりの精度で再現できます(work_stealing 無効時)
- 再現性を高めるには、CI などで
ログ・レポート形式の変化
- 「並列でのテスト失敗レポートの扱い」が変わっているため、CI ログの並び方が従来と若干変わる可能性があります
- 「各ワーカーからの並列レポート」は削除されているため、以前のログフォーマット前提の解析ツールなどがある場合は要確認
- 参考情報 (あれば)
パフォーマンス検証に使われたリポジトリ/PR:
https://github.com/basecamp/fizzy/pull/2037並列テストに関する Rails ガイド(PR で更新された章):
guides/source/testing.md内の「Parallel Testing」セクション(Rails 本家ガイドサイトの Testing ガイド)
#56330 Enable debug events by default
マージ日: 2025/12/9 | 作成者: @gmcgibbon
- 概要 (1-2文で)
Railsの新しいイベントレポーター(log subscriber 周り)で、デフォルトでは出なくなっていた「debug イベント」を、再びデフォルトで有効にする変更です。特にアプリケーション初期化前(テストやフレームワーク単体利用時)でも debug イベントが出力されるようになります。
- 変更内容の詳細
2-1. イベントレポーターのデフォルト動作変更
ActiveSupport::EventReporter の初期設定が変更され、デフォルトで debug モードが有効になります。
変更イメージ(擬似コード):
# 変更前 (イメージ)
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 イベントをデフォルト有効にした」旨が追記。
- 影響範囲・注意点
3-1. ログ・イベント出力量の増加
- これまで:
- 新しい log subscriber / event reporter 導入以降、デフォルトでは debug イベントが出ない状態だった
- これから:
- 何も設定していない場合、debug イベントが出るようになる
そのため:
- テスト環境やコンソール上で、イベント出力(ログやイベントサブスクライバ経由の出力)が増える可能性があります。
- デバッグ目的ではメリットが大きい一方で、
- 大量のイベントが発生する処理
- もともとログ量を抑えていたシナリオ
では、出力量が増えることに注意が必要です。
3-2. ActiveSupport 単体利用・ライブラリ作者への影響
ActiveSupport や Rails のイベント仕組みを使ってログやメトリクスを構築しているライブラリは:
- デフォルトで debug イベントが流れる前提になるため、
- 不要であれば
EventReporterや subscriber 側で debug イベントをフィルタする - 本番環境向けライブラリであれば、利用者にログレベルのチューニングを案内する といった対応を検討したほうがよいかもしれません。
- 不要であれば
3-3. セキュリティ・情報漏えい観点
PR の意図として、
アプリケーションが初期化されていない状況 = テストや単体利用を想定
この場合、debug イベントを「秘匿すべき」とはみなさない
という割り切りがあります。
- 本番環境で Rails アプリとして動いている場合は、
- 通常は Rails のロガー設定や環境ごとの設定でログレベルを調整する想定
- 一方で、もし初期化前の状態で本番相当の処理をしている特殊な構成があれば、
- 想定より詳細な debug 情報が出力される可能性があるため、設計を見直した方が安全です。
- 参考情報 (あれば)
- 該当 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-2文で)
ActiveRecord::Base#with_lockが、ブロック引数として「現在のトランザクションオブジェクト」を受け取れるように拡張されました。これにより、with_lock内でafter_commitなどのトランザクションフックを、より扱いやすい形で利用できます。
- 変更内容の詳細(あればサンプルコードも含めて)
何が変わったか
従来の with_lock:
person.with_lock do
# 悲観ロックされた状態での処理
endこの PR 以降は、ブロック引数としてトランザクションオブジェクトが渡されます:
person.with_lock do |transaction|
transaction.after_commit { puts "hello" }
# その他の処理
end内部的には、ActiveRecord::Base#with_lock が開始するトランザクションを、そのままブロックに yield するように変更されています。
これは既に ActiveRecord::Base.transaction に導入されている挙動 (transaction do |tx| ... end で tx.after_commit などが使える) と同様のパターンを、with_lock にも適用したものです。
コード上の変更ポイント(概略)
activerecord/lib/active_record/locking/pessimistic.rbwith_lock内のtransactionブロックで、これまでyieldのみだった箇所を「トランザクションオブジェクトを引数に渡してyield」するように変更。
activerecord/test/cases/locking_test.rbwith_lockにブロック引数を渡したときにafter_commitなどが正常に動作することを確認するテストが追加。
activerecord/CHANGELOG.md- 新機能として
with_lockがトランザクションを yield することが明記。
- 新機能として
※正確なメソッドシグネチャは PR 本体側の実装に依存しますが、意図としては以下のようなイメージです:
# 疑似コードイメージ
def with_lock(...)
transaction(requires_new: true) do |tx|
lock!
yield tx
end
end- 影響範囲・注意点
既存コードへの影響
後方互換性
- 既存の
with_lockの呼び出し方法(ブロック引数なし)はそのまま有効で、挙動も基本的に変わりません:rubyperson.with_lock do # これまで通り動く end - ブロックの arity(引数の数)が 0 の場合でも Ruby のブロック仕様上問題なく動作します(余計な引数は無視される)。
- 既存の
新機能としての利用
with_lock内でトランザクションフックを直接呼びたい場面で、モデルのクラスメソッド経由などを使わずに、transaction引数から直接after_commit等を呼べます。rubyperson.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をネストするようなケースや、transactionとwith_lockを組み合わせるケースでは、どのレベルのトランザクションに対してafter_commitが紐づくかの理解が必要になります(これは本 PR 特有というより、transactionを yield するパターン全般の注意点です)。
- 参考情報 (あれば)
- 類似機能の 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-2文で)
Rails の Active Record に含まれるコメント内のスペルミス「exisiting」を正しい「existing」に修正した PR です。コードロジックの変更は一切なく、挙動への影響はありません。変更内容の詳細(あればサンプルコードも含めて)
対象ファイル:
activerecord/lib/active_record/encryption/encryptor.rbactiverecord/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 つのコメント行で誤字が見つかり、それを修正したという位置付けです。
- 影響範囲・注意点
- コードコメントのみの修正であり、実行パス・API・DB スキーマ・挙動には一切影響しません。
- テスト (
activerecord/test/models/dats.rb) のコメントも対象ですが、テストの内容や結果に影響はありません。 - バージョンアップやパッチ適用時に、この変更に起因するマイグレーション・リリースノート上の特別な対応は不要です。
- ドキュメント生成やソースコードブラウジング時(IDE でのコメント表示など)において、より読みやすく正確な説明になるという程度の改善です。
- 参考情報 (あれば)
- 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-2文で)
Active Storage のテスト実行時に、非推奨オプション:preprocessedに関する deprecation warning が大量に出る問題を、テスト側でサイレンスするPRです。Rails 9.0 での削除予定APIに関する警告を抑制し、テスト出力をノイズレスにしています。
- 変更内容の詳細
※差分は activestorage/test/test_helper.rb のみ(+24/-18)。
主なポイントは以下です。
Active Storage のテストで発生する、以下のような deprecation warning を抑制する設定をテストヘルパーに追加。
textDEPRECATION 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に入るパターンです(イメージ):rubyActiveSupport::Deprecation.behavior = :stderr ActiveSupport::Deprecation.silenced = true # または ActiveSupport::Deprecation.disallowed_warnings += [ /The :preprocessed option is deprecated/, ]実際のコードはPR側の実装に依存しますが、
- Rails 本体のテストでのみ有効
- 本体機能(本番利用の deprecation 動作)は変えない
という方向で、ActiveStorage のテスト補助コードにのみ変更が入っています。
あくまで「テスト出力の静音化」が目的であり、
:preprocessed→process: :later / :lazilyへの置き換え自体を行うものではありません(Active Storage の内部実装置き換えではない)。
- 影響範囲・注意点
影響範囲
- Rails リポジトリ内の Active Storage テストスイートのみ。
- Rails を利用するアプリケーションコードや、アプリ側テストの挙動には影響しません。
- CIのログなどで、該当 deprecation が出なくなることでログノイズが減ります。
注意点
- このPRは「テストで警告を隠す」だけであり、
:preprocessed自体が将来削除される事実は変わりません。 - アプリケーション側で
preprocessed: true/falseを使っている場合は、引き続き自分のアプリで deprecation warning を確認し、
推奨どおりprocess: :later/process: :lazilyへの移行が必要です。 - コアのテストが警告をサイレンスしているため、「Rails 本体のテストが deprecation を検知してくれる」ことは期待できなくなります。
ただし、これは「移行ガイドで正式に案内済みの deprecation であり、Rails 本体テストではノイズとして扱う」という判断によるものと考えられます。
- このPRは「テストで警告を隠す」だけであり、
- 参考情報 (あれば)
- 該当 deprecation のメッセージ:
preprocessed: true→process: :laterpreprocessed: false→process: :lazily
- 将来のバージョン:
- Rails 9.0 で
:preprocessedオプションは削除予定。
- Rails 9.0 で
- 実アプリでの修正例: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-2文で)
config.action_controller.action_on_open_redirect = :notifyを有効にしているアプリで、オープンリダイレクト検知時に自動で「構造化ログイベント」が発行されるようにした PR です。ActiveSupport::Notifications の購読を自前で書かなくても、標準の Structured Event Subscriber 経由でオープンリダイレクトの情報を取得できます。
- 変更内容の詳細
背景
- 先行PR (#55496) で「オープンリダイレクト検知」と
:notifyの動作が導入済み。 - これまでは
:notifyを使っても、「通知は飛ぶが、構造化ログとして扱うには独自 subscriber 実装が必要」という状態だった。 - この PR で、Rails 標準の
ActionController::StructuredEventSubscriberがopen_redirect通知を処理するようになり、「設定するだけで構造化ログ」が出るようになる。
具体的な変更点
ActionController::StructuredEventSubscriberにopen_redirect用メソッドを追加actionpack/lib/action_controller/structured_event_subscriber.rbに、open_redirectイベントを処理するメソッドが追加。- イベント名は ActiveSupport::Notifications の
open_redirect.action_controllerに対応している想定です。 - メソッドの中で、構造化ログとして必要な情報を組み立てます。PR 説明から分かるポイント:
stack_traceには「スタックトレースの最後の 1 行」を使用- これは既存の
#rescue_from_callbackの出力形式と揃えるため - (将来的には「最後の数行」や「フィルタ済みトレース」など、よりリッチな情報を提供する拡張余地あり)
- これは既存の
擬似コードイメージ(実際のコードイメージ用・正確なキー名はリポジトリを参照してください):
rubymodule 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に似た仕組み)からオープンリダイレクトが一貫した形式で出力されます。Redirecting モジュール側の微調整
actionpack/lib/action_controller/metal/redirecting.rbは +1/-1 の微修正のみ。- 内容的には、
open_redirect通知の発火ロジック(または payload)に対する細かな調整で、StructuredEventSubscriber が扱いやすい形に合わせた可能性が高いです(メソッド名・イベント名・payload のキー揃え等)。
CHANGELOG の更新
actionpack/CHANGELOG.mdに、この新機能(オープンリダイレクト向け structured event サポート)が追加されたことが追記されています。- actionpack の利用者向けに、機能の追加が公式にアナウンスされた形です。
テスト追加
actionpack/test/controller/structured_event_subscriber_test.rbに 20 行程度のテストを追加。- 主な観点:
open_redirect通知が structured event として処理されることstack_trace等、構造化された payload が期待通りであること
- これにより、今後のリファクタリングや拡張時にもこの振る舞いが壊れないよう担保されています。
- 影響範囲・注意点
影響範囲
- 対象:
ActionControllerのオープンリダイレクト保護を有効化し、かつconfig.action_controller.action_on_open_redirect = :notify
を指定しているアプリケーション。
- 影響内容:
- 上記設定のアプリでは、オープンリダイレクト検知時に自動的に構造化ログイベントが出力される。
- 既に独自で
open_redirect.action_controllerを購読しログを出していた場合、- Rails 標準の structured event と併せて 2 重にログが出る可能性があります。
注意点・移行上のポイント
すでに独自の subscriber で構造化ログを実装している場合:
- 標準 StructuredEventSubscriber に合わせて統一することで、
- ログ形式の一貫性
- メンテナンスコストの削減 が期待できます。
- 不要であれば、自前の subscriber を削る・または標準出力側をフィルタすることを検討してください。
- 標準 StructuredEventSubscriber に合わせて統一することで、
stack_traceが「最後の 1 行のみ」である点:- デフォルトでは詳細なスタックトレースが欲しい場合には情報が足りない可能性があります。
- よりリッチなスタック情報が必要なら:
- 独自 subscriber を追加で実装し、payload から
exceptionを参照して自前でトレースを組み立てる - あるいは将来的な Rails 側の拡張(「最後の X 行」など)が入るのを待つ を検討してください。
- 独自 subscriber を追加で実装し、payload から
ログ出力基盤との統合:
- structured event のキー設計は他の
ActionController::StructuredEventSubscriberが出すイベントと整合しているはずなので、- 既に JSON ログや Observability 基盤(Datadog, New Relic, OpenTelemetry 等)に統合している場合、
- 同じストリーム上に「open_redirect」イベントが自然に流れ込むようになります。
- 監視・アラート設定で「open_redirect」のイベント名やタグをトリガーとして使うのが簡単になります。
- structured event のキー設計は他の
- 参考情報 (あれば)
先行PR:
- https://github.com/rails/rails/pull/55496
オープンリダイレクトに関する:notifyの仕組み自体の導入 PR。
- https://github.com/rails/rails/pull/55496
対応するクラス / モジュール:
ActionController::RedirectingActionController::StructuredEventSubscriber
Rails の Structured Logging / Structured Event 周り:
- Rails 7.1+ 以降で強化されている structured logging 基盤の一部で、
ActionController::StructuredEventSubscriberはコントローラ関連イベントを構造化してログに流すための標準 subscriber です。
本 PR により、その対象イベントに「open_redirect」も加わった形になります。
- Rails 7.1+ 以降で強化されている structured logging 基盤の一部で、
#56296 Remove unnecessary defined? check in Active Job instrumentation
マージ日: 2025/12/8 | 作成者: @shivamsinghchahar
- 概要 (1-2文で)
Active Job のインストルメンテーション処理内で、インスタンス変数へのアクセス前に行っていたdefined?チェックを削除し、よりシンプルな条件分岐に変更した PR です。Ruby 2.7 サポート終了により、未定義変数アクセスに関する警告回避コードが不要になったことが背景です。
- 変更内容の詳細
対象ファイル: activejob/lib/active_job/instrumentation.rb
変更は 1 行のロジック差し替えのみです。
変更前
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 以前では、未定義のインスタンス変数アクセスで警告が出ることへの配慮としてこうしたパターンがよく使われていました。
変更後
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 は適切に設定される設計になっていることが前提になっています。)
- 影響範囲・注意点
- 対象: Active Job のインストルメンテーション(
"#{operation}.active_job"の通知を送る際の payload 生成)部分のみ。 - 主な影響:
payload[:aborted]の値が「@_halted_callback_hook_calledの実際の値」から「条件成立時は常にtrue」に変わりました。- ロジックとしては「中断したかどうか」のフラグであれば、
true/nilの二値で十分なので、意味的な後方互換性は高いです。 - もし外部コードが
payload[:aborted]にtrue/false以外の値(例えばシンボルや詳細な状態)を期待していた場合は注意が必要ですが、そのような利用は想定されていないはずです。
- ロジックとしては「中断したかどうか」のフラグであれば、
defined?を使わなくなったことで、Ruby のバージョン依存の警告回避コードが減り、読みやすさ・保守性が向上しています。
- パフォーマンス影響:
- 実質的に誤差レベルですが、
defined?の呼び出しがなくなるため、極めてわずかに軽くなります。
- 実質的に誤差レベルですが、
- テスト:
- PR 上では新規テスト追加は行われていませんが、挙動としてはごく限定的な変更であり、既存テストでカバーされている前提です。
- 参考情報 (あれば)
- 類似の変更:
- PR 説明にもある通り、
https://github.com/rails/rails/pull/50597で同様の「Ruby 2.7 サポート終了に伴うdefined?除去」的なリファクタリングが行われています。
- PR 説明にもある通り、
- 背景:
- Rails 7.2 以降で Ruby 3.0+ が前提となり、古い Ruby 向けの互換コードや警告回避コードが徐々に削除されている流れの一環です。
#56311 Deprecate PreviewImageJob
マージ日: 2025/12/8 | 作成者: @p8
- 概要 (1-2文で)
Active Storage のActiveStorage::PreviewImageJobを非推奨にしつつ、内部実装を現状の API に追従させる変更です。Rails 本体からは既に呼ばれていないものの、ドキュメント済みでパブリック API であるため、削除ではなく deprecate という形で扱っています。
- 変更内容の詳細
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 などを防ぐ。
概要イメージ(あくまで構造イメージの擬似コード):
# 変更前 (イメージ)
class ActiveStorage::PreviewImageJob < ActiveStorage::BaseJob
def perform(blob)
preprocessed(blob) do |variant|
# ...
end
end
end# 変更後 (イメージ)
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 として既存利用がある可能性を考慮し、最低限の振る舞いをテストで担保。
- 影響範囲・注意点
既存のアプリケーションで
ActiveStorage::PreviewImageJobを使っている場合- クラスはまだ存在するため直ちに壊れません。
- ただし非推奨になったため、今後の Rails メジャーバージョンで削除される可能性が高いです。
- 直接呼んでいる場合は、代替手段(自前の Job で blob/attachment の preview 生成を行うなど)を検討してください。
- 継承して独自 Job を作っている場合も、ベースクラスを自前のクラスに差し替えるなどの移行を考えた方がよいです。
preprocessedに依存した独自実装がある場合- この PR の対象は Rails 本体内の利用ですが、もしアプリ側で
preprocessedを呼び出しているコードが残っている場合は、すでに別の PR (#51951) の時点で壊れている可能性があります。 - 同様の方針で、
preprocessedがしていた処理を直接自前で記述する形に移行する必要があります。
- この PR の対象は Rails 本体内の利用ですが、もしアプリ側で
テストやドキュメント
- ドキュメントで紹介されていたクラスに非推奨ラベルが付くことで、将来的な削除に向けたシグナルが明確になります。
- ライブラリや gem で
PreviewImageJobを利用・継承している場合は、非推奨である旨を README 等に記載しつつ、代替設計へ移行することが望まれます。
- 参考情報 (あれば)
- この変更は、以前の変更 (#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–2文で)
GitHub Actions 用の CI テンプレートで利用しているactions/checkoutのバージョンをv5からv6に更新した PRです。これにより、新しく生成した Rails アプリで GitHub に push した直後に Dependabot から「v6 に上げてください」という PR が飛んでくる状況を解消します。
- 変更内容の詳細
何を変えたか
主な変更は2種類です。
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
- 以下のワークフロー内の
新規アプリ/プラグイン作成時に生成される 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のバージョンをv5→v6に更新。
- Rails アプリ生成 (
サンプル (生成される CI のイメージ)
変更前(例):
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: ruby/setup-ruby@vX
with:
ruby-version: 3.3
# ...以下省略変更後(例):
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 を防ぐのが目的。
- 影響範囲・注意点
新規で生成する Rails アプリ・プラグイン
- この PR が含まれた Rails バージョン以降で
rails new/rails plugin newをし、--github ciオプションを付けた場合、CI テンプレートは最初からactions/checkout@v6を使うようになります。 - そのため、GitHub 上で Dependabot が即座に「checkout のバージョンを上げる PR」を出してくることはなくなります。
- この PR が含まれた Rails バージョン以降で
既存の Rails アプリへの影響
- すでに生成済みのアプリの CI 設定は自動では変わりません。
- 既存プロジェクトで
actions/checkout@v5を使っている場合は、手動で@v6に上げるか、Dependabot の PR をマージすれば同等の状態になります。
互換性面のリスク
actions/checkoutのメジャーバージョンアップでは挙動やデフォルト設定が変わる場合がありますが、この PR 自体は Rails テンプレート上のバージョン番号を追従させただけで、Rails のコードやテスト内容には影響しません。- 既存の GitHub Actions のベストプラクティスに合わせた更新なので、一般的には問題なく受け入れられる変更です。
- 参考情報 (あれば)
- 対応している Issue:
Fixes #56320 - 変更対象となった GitHub Actions の公式アクション:
actions/checkout- リポジトリ: https://github.com/actions/checkout
- v5 → v6 への変更点は上記リポジトリのリリースノートを参照。
#56324 Require uri instead of uri/generic
マージ日: 2025/12/8 | 作成者: @jeremyevans
- 概要 (1-2文で)
Rails内でuri/genericを直接requireしていた箇所を、標準的なrequire "uri"に変更することで、URIをautoloadしている環境でもrequire "rails"が失敗しないようにする修正です。Ruby 標準ライブラリのuriの使い方を正しくし、他ライブラリとの相互運用性を改善するバグフィックスです。
- 変更内容の詳細
何が問題だったか
Ruby の uri ライブラリは、本来は次のようにトップレベルで読み込むことが前提になっています。
require "uri"しかし Active Support の一部では、内部実装ファイルを直接指定していました。
# 以前
require "uri/generic"通常はこれでも動くことが多いのですが、以下のように URI を autoload している環境では問題が発生します。
autoload :URI, "uri"
require "rails"この状況で require "uri/generic" が走ると、uri ライブラリ内部の読み込み順と定数定義の前提(URI::Generic が定義済みであることなど)が崩れ、uninitialized constant URI::Generic という NameError が発生します。PR のログにある通り、uri/file.rb が Generic を参照した時点で未定義になってしまうためです。
具体的な変更箇所
変更ファイルは 2 つで、どちらも require 行の置き換えのみです。
activesupport/lib/active_support/core_ext/object/json.rbactivesupport/lib/active_support/message_pack/extensions.rb
それぞれで、
require "uri/generic"となっていた箇所が、
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 とも整合するようになります。
- 影響範囲・注意点
影響範囲
直接的な影響箇所
- Active Support の
Object#to_jsonなど JSON 拡張 (core_ext/object/json.rb)- MessagePack 拡張 (
message_pack/extensions.rb) で使用している URI 関連機能の読み込み方法。
- Active Support の
間接的に恩恵を受けるケース
- 他ライブラリが
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 しているだけ」なので、一般的には安全な変更と見なせます。
- 参考情報 (あれば)
- 該当 PR: https://github.com/rails/rails/pull/56324
- 関連しうる Ruby 標準ライブラリ:
- 問題の再現例(PR 説明より):
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-2文で)
Action Mailer Basicsガイド内のサンプルコードで使われていたemailカラム名を、Rails標準の認証ジェネレータで使われているemail_addressに統一するドキュメント修正です。コード本体の挙動変更や機能追加はなく、ガイドの記述のみが変わっています。
- 変更内容の詳細
対象ファイル
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 の入門ドキュメント
が同じカラム名を前提に説明されるようになり、チュートリアルをそのまま写経しても整合性が取れるようになります。
- Rails 7.2 以降に入った組み込みの認証ジェネレータ
- 影響範囲・注意点
影響範囲
- 実コード・ライブラリの挙動には影響なし(ドキュメントのみの変更)。
- 新しく Action Mailer Basics を読みながら開発する人が、認証ジェネレータと同じ
email_addressを自然に使うようになります。
注意点(既存プロジェクトとの関係)
- 既存アプリで
emailカラムを使っている場合、このPRによってアプリが壊れることはありませんが、ガイドのコード例とは名前が異なる状態になります。 - 既に
bin/rails generate authenticationを使ってemail_addressカラムを持つUserモデルを作っている場合は、この PR 後のガイドのほうが素直にコピペで動きます。 - どちらのカラム名を使うかはアプリの設計次第ですが、Rails公式の最新フローに合わせるなら新規では
email_addressに寄せるのが無難です。
- 既存アプリで
- 参考情報 (あれば)
- PR: https://github.com/rails/rails/pull/56310
- 関連ディスカッション: https://github.com/rails/rails/discussions/54980
- 認証ジェネレータについての背景は、Rails Guides や
bin/rails generate authentication --helpで確認できます。
#56303 Suppress unnecessary output in railties tests
マージ日: 2025/12/7 | 作成者: @yahonda
- 概要 (1-2文で)
Rails の railties テスト実行時に、HTTP リクエストによる例外ログや RuboCop の警告メッセージなど、テスト結果と直接関係しない冗長な出力をquietlyで抑制する変更です。これにより、テスト出力がシンプルになり、失敗箇所の把握がしやすくなります。
- 変更内容の詳細
全体方針
- 対象は railties 配下の一部テスト。
- 「テストの目的は満たすが、標準出力には不要な情報」が出ていた箇所を
quietlyブロックでラップ。 - テストの成否・挙動は変えずに、コンソール出力だけを静かにするためのリファクタリング。
2-1. HTTP リクエスト関連テストの出力抑制
対象ファイル(例):
railties/test/application/mailer_previews_test.rbrailties/test/application/middleware/exceptions_test.rbrailties/test/application/routing_test.rbrailties/test/application/sprockets_assets_test.rb
これらのテストでは、意図的に 404 / 500 などのエラーを発生させて挙動を確認するため、以下のような ActionController::RoutingError などのスタックトレースやログがテスト中に出力されていました。
変更前のイメージ:
# テスト実行中に
[UUID] ActionController::RoutingError (No route matches [GET] "/rails/mailers"):
[UUID]変更後は、同じテストを実行しても、このようなログは出力されず、テスト名と "." のみが表示されます。
実際の適用イメージ(簡略化):
# 変更前(擬似コード)
get "/rails/mailers"
assert_response :not_found
# 変更後(擬似コード)
quietly do
get "/rails/mailers"
end
assert_response :not_foundquietly は内部で $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 が設定されていない」旨のメッセージがすべてテスト出力に流れていました:
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 設定に関する情報」であり、テストの成功・失敗とは無関係なノイズです。
変更後イメージ(擬似コード):
# 変更前
run_rubocop(generated_app_path)
# 変更後
quietly do
run_rubocop(generated_app_path)
endこの結果、テスト出力には . のみが表示され、RuboCop の冗長なバージョン/設定関連のメッセージは表示されなくなります。
- 影響範囲・注意点
影響範囲
- 影響するのは railties の一部テストの「コンソール出力のみ」で、アプリ本体や本番挙動には一切影響しません。
- テストのアサーション内容やカバレッジも変わらない想定です(HTTP レスポンスの検証などはそのまま)。
利点
- テストログが大幅に読みやすくなり、テスト失敗時の本質的なメッセージが見つけやすくなります。
- CI ログが短くなり、ログ閲覧コストや保存容量の削減にも寄与します。
注意点
quietlyによって、トラブルシュート時に参考になる可能性があるログも一緒に抑制されるケースがあります。- ただし、ここで抑制しているのは主に「意図的に起こしているエラー」や RuboCop のバージョン通知であり、テスト失敗時にはアサーションエラー等は通常通り表示されます。
- テストをデバッグする際に HTTP ログが必要な場合は、該当テストから一時的に
quietlyを外すか、ローカルで patch を当て直して実行する必要が出るかもしれません。
- 参考情報 (あれば)
- PR: https://github.com/rails/rails/pull/56303
- RuboCop の新規 Cop 周りの挙動: https://docs.rubocop.org/rubocop/versioning.html
- 「新しい Cop が追加されたが
.rubocop.ymlで設定されていない」場合に出る警告メッセージが今回抑制対象。
- 「新しい Cop が追加されたが
- Rails テストでの
quietly利用は既存パターンであり、ログノイズを減らす目的でしばしば使われています。
#56316 ActionView Strict Locals: Adds support for case when closing parenthesis is on next line
マージ日: 2025/12/7 | 作成者: @shivabhusal
- 概要 (1–2文で)
Action View の「Strict Locals」機能で、locals:コメントのカッコ閉じ)が次の行に書かれている場合でも正しくパースできるようにした PR です。これにより、複数行に整形された locals 宣言のバリエーションが追加でサポートされます。
- 変更内容の詳細
背景のおさらい
Strict Locals は、テンプレート先頭のコメントで受け取るローカル変数とデフォルト値を宣言する仕組みです(ERB だとこんな感じ):
<%# locals: (arg_1:, arg_2: nil, arg_3: []) -%>直近の PR #56270 で、これが複数行にまたがるケースがサポートされました:
<%# locals: (arg_1:,
arg_2: nil,
arg_3: []) -%>ただし、閉じカッコ ) を別行にそろえて書くこのスタイルは未対応でした:
<%# locals: (
arg_1:,
arg_2: nil,
arg_3: []
) -%>この PR は、この「閉じカッコだけ次の行」パターンを parser が認識できるようにする小さな修正です。
実際の変更点
変更ファイルは 2 つだけです。
actionview/lib/action_view/template.rb- Strict Locals コメントをパースする正規表現 or 行処理ロジックを 1 行だけ修正。
- 具体的には、
locals: (で始まるコメントから)までを複数行にわたって読み取る際に、
「引数行」と「閉じカッコ行」が明確に分かれているケースも含めるようになっています。
イメージとしては、こんなパターンが新たに許可されるようになった、と考えてよいです:
erb<%# locals: ( arg_1:, arg_2: nil, arg_3: [] ) -%>actionview/test/template/template_test.rb- 上記のパターンが正しく解釈されることを保証するテストが 13 行分追加。
- テストでは、Strict Locals コメントで宣言した
arg_1,arg_2,arg_3が期待通りに利用できるかを確認しています。
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: ( から改行をはさみ、最後に単独行で ) を書いてもパースされます。
- 影響範囲・注意点
対象:
- Action View の Strict Locals 機能を利用している ERB / Slim /(おそらく)Haml テンプレート。
- 特に「整形のために
locals:コメント内の閉じカッコを次の行に出す」コーディングスタイルを採っている場合に恩恵があります。
互換性:
- 既存の 1 行 / 複数行の locals 宣言はそのまま動作します。
- 追加で 1 パターンのフォーマットが許容されただけの変更なので、後方互換性への影響はほぼありません。
Haml について:
- 明示的なテストはされていませんが、Action View 側のテンプレートパースロジック変更なので、同じコメント形式であれば Haml でも同様に動作するはず、という前提です。
- プロジェクトで Haml + Strict Locals を使っている場合は、念のためこのフォーマットのコメントを 1 つ用意して CI/手動で確認しておくと安全です。
注意点:
- あくまで
locals:コメントのフォーマット解釈の改善であり、Strict Locals の意味論(必須/任意、デフォルト値など)には変更はありません。 - コメントの開始と終了(例:
<%# ... -%>や/# ...)のシンタックスは、各テンプレートエンジンのルールに従う必要があります。
- あくまで
- 参考情報 (あれば)
- 対応した元の issue/PR:
- Strict Locals の複数行サポートを追加した PR: https://github.com/rails/rails/pull/56270
- Strict Locals 全般の利用イメージ:
- Rails Guides にはまだ詳細がない場合がありますが、
ActionView::Templateの実装およびテスト (actionview/test/template/template_test.rb) が現時点での仕様のソースとなります。
- Rails Guides にはまだ詳細がない場合がありますが、
#56304 Fix dumping materialized views indexes
マージ日: 2025/12/6 | 作成者: @fatkodima
- 概要 (1-2文で)
このPRは、Active Record の schema_dumper が「マテリアライズドビューに付いているインデックス」を正しくダンプできていなかった問題 (#56301) を修正するものです。PostgreSQL などで materialized view を使う際に、schema.rbへのインデックス出力が正しく行われるようになります。
- 変更内容の詳細
※ 実際の diff は 2 行追加・2 行削除のみと非常に小さいですが、主なポイントは「インデックスをダンプする対象に materialized view を含めるようにした」ことです。
activerecord/lib/active_record/schema_dumper.rb 内で、インデックスを列挙する処理は通常「テーブル一覧」に対して行われますが、これが materialized view のインデックスを拾えていませんでした。
イメージとしては、次のような修正が行われています(疑似コードレベル):
# 修正前(イメージ)
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 で:
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 にも次のようなインデックス定義が含まれるようになります:
create_view "popular_articles", materialized: true, sql_definition: <<-SQL
...
SQL
add_index "popular_articles", ["views"], name: "index_popular_articles_on_views"- 影響範囲・注意点
- 影響を受けるのは「schema.rb を使っている」かつ「materialized view にインデックスを定義している」アプリケーションです。
- これまで:
- materialized view 上のインデックスが
schema.rbに出力されず、 db:schema:loadしてもインデックスが再現されない
という不具合があった場合、この修正により解消されます。
- materialized view 上のインデックスが
- 変更は schema dump 時の出力内容のみであり、既存DBには直接変更を加えません。
ただし、Rails をこの修正を含むバージョンに上げてからdb:schema:dumpすると、schema.rbに新たにインデックス定義が追加されるため、git diffが増える可能性があります。 - SQL スキーマ (
structure.sql) を使っている場合は、本修正の直接的な影響はほぼありません(pg_dumpがそのまま出力するため)。
- 参考情報 (あれば)
- PR: https://github.com/rails/rails/pull/56304
- 関連 issue: Fixes #56301(materialized view の index が schema dump に含まれない問題)
- Rails ガイド(schema.rb と structure.sql の違い):
https://guides.rubyonrails.org/active_record_migrations.html#schema-dumping-and-you
#56297 Wrap console command with an executor by default
マージ日: 2025/12/6 | 作成者: @zzak
- 概要 (1-2文で)
Rails コンソール (bin/rails console) が、デフォルトでRails.application.executorにラップされた状態で起動するようになりました。
runner と同様に、-wまたは--skip_executorオプションでこの挙動を無効化できます。
- 変更内容の詳細
2-1. コンソールが Executor でラップされるように
これまで rails console は、基本的に「素の」プロセス上で Rails 環境をロードしていましたが、この PR により、コンソールの実行処理が Rails.application.executor でラップされるようになりました。
イメージとしては、以下のようなことが行われるようになったと考えられます(実際の実装イメージ):
# 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
endRails.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 に包まれない状態でコンソールを起動します。
# 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 をバージョンアップする際の互換性確認ポイントとして記録されました。
影響範囲・注意点
コンソールでのコード実行が、本番リクエストに近いコンテキストになる
Currentを使ったコンテキスト依存のコードや、Executor フック (ActiveSupport::Executor) を利用しているコードが、コンソールでもより本番に近い挙動になります。- これまで「コンソールだと動くが、本番だと動かない/逆に本番ではフックが効くがコンソールでは効かない」といった差がある場合、それが解消・もしくは現れ方が変わる可能性があります。
Executor フックに副作用がある場合は要確認
Rails.application.executor.to_run/to_completeなどで重い処理やログ出力などをしている場合、コンソール起動・終了時にもそれらが実行されるようになります。- コンソール起動が重く感じるようになった場合は、フックの中身を見直すか、コンソールだけ
--skip_executorを使う運用も検討できます。
既存ツール・スクリプトへの影響
rails consoleをラップして独自にスレッドローカルやグローバル状態を扱っているツールがあると、Executor の導入で状態がリセットされるタイミングが変わることがあります。- 互換性を確保したい場合は、ツール側で
rails console --skip_executorを使うか、Executor 前提の設計に寄せる必要があります。
runner との挙動統一
- 既に runner で
-w/--skip_executorを使っていたプロジェクトは、「コンソールも同じ思想で使える」ようになります。 - コンソールと runner で Executor の有無を揃えておくと、デバッグ時の挙動差分が減らせます。
- 既に runner で
- 参考情報 (あれば)
- 関連 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-2文で)
ERB の「strict locals」用マジックコメントが複数行に渡る場合にローカル変数解析が失敗していたバグを修正した PR です。locals:コメントを複数行に分割して書いても、正しく必須/オプションローカル変数が解釈されるようになります。
- 変更内容の詳細(あればサンプルコードも含めて)
何が問題だったか
_partial.html.erb 内で strict locals を次のように 1 行で書く場合は動作していました:
<%# locals: (arg_1:, arg_2: nil, arg_3: []) %>しかし、可読性のために次のように複数行に分割すると:
<%# 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.rb の STRICT_LOCALS_REGEX が以下のように変更されました。
# 変更前
STRICT_LOCALS_REGEX = /\#\s+locals:\s+\((.*)\)/
# 変更後
STRICT_LOCALS_REGEX = /\#\s+locals:\s+\((.*?)\)/mポイント:
mフラグの追加- Ruby の正規表現では、デフォルトでは
.は改行にマッチしません。 /.../mとすることで.が改行も含めてマッチするようになります。- これにより
locals: ( ... )の中身が複数行でもパース可能になります。
- Ruby の正規表現では、デフォルトでは
.*を.*?(非貪欲)に変更.*は「できるだけ多くマッチ」するため、テンプレート中に複数の)があると最後の)まで巻き込んでしまう可能性があります。.*?とすることで「できるだけ少なくマッチ」となり、最初に現れる)で正しく止まるようになります。
テストの追加
actionview/test/template/template_test.rb に multiline の locals コメントをカバーするテストが追加されています。
テスト内容は PR 説明のスクリプトとほぼ同等で、以下の 2 パターンを検証します:
- 単一行の strict locals コメントで partial を render するケース(既存挙動の確認)
- 複数行の strict locals コメントで partial を render するケース(今回修正対象)
期待値:
arg_1はlocalsで渡した"First"が表示されるarg_2はデフォルトnilなので空文字として表示されるarg_3はデフォルト[]なので"[]"と表示される
- 影響範囲・注意点
- 影響範囲
- Action View の ERB テンプレートで、strict locals マジックコメント(
<%# locals: (...) %>)を利用している部分に限定されます。 - 特に、マジックコメントを改行して書いている or これから可読性のために改行したい partial で恩恵があります。
- Action View の ERB テンプレートで、strict locals マジックコメント(
- 既存コードへの影響
- これまで 1 行で書いていた strict locals コメントの挙動に変更はありません。
- 正規表現が非貪欲(
.*?)になったことで、逆に「誤って広くマッチし過ぎていた」ケースがあれば、より正確にマッチするようになりますが、通常の use case では問題にならないと考えられます。
- 注意点
- マジックコメント自体のシンタックスは従来通りで、
# locals: (arg_1:, arg_2: nil)の形式を守る必要があります。 - コメント内のカッコの対応が崩れている(
(に対応する)がないなど)場合は、正規表現も正しくマッチできないため、引き続き注意が必要です。
- マジックコメント自体のシンタックスは従来通りで、
- 参考情報 (あれば)
- 対象 Issue: Fixes #56239
- 修正ファイル:
actionview/lib/action_view/template.rbactionview/test/template/template_test.rb
- 機能としての「strict locals」について(参考用イメージ):
- partial 冒頭にerbのように書くと、その partial は
<%# locals: (title:, content: nil) %>titleを必須、contentをオプションとして受け取ることを Action View がチェックする仕組みです。 - 今回の修正により、これをerbのような複数行スタイルでも安全に使えるようになります。
<%# locals: ( title:, content: nil ) %>
- partial 冒頭に
#56300 Fix typos in JavaScript guide
マージ日: 2025/12/6 | 作成者: @Saidbek
- 概要 (1-2文で)
Rails のガイド「Working with JavaScript in Rails」に含まれていた誤り・不整形な箇所を修正した PR です。主にドキュメントの整形改善と、非同期 JavaScript のコード例の誤り修正が行われています。
- 変更内容の詳細
変更ファイル:
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 ガイドでよく出る形は以下のようなコードです:
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()などを行う- 文法エラーにならないように
;やカンマ、括弧などを整える
といった点が直されているはずです。
- 影響範囲・注意点
- 影響範囲は ドキュメントのみ であり、Rails 本体の挙動や API には一切変更がありません。
- ただし、以前のガイドの async サンプルコードをそのまま使っていた場合:
- コードが動かなかったり、
- たまたま動いていても「Promise を正しく待っていない」「エラーハンドリングが不十分」といった問題を含んでいる可能性があります。
そのため、JavaScript ガイドの async コード例を参照している箇所があれば、新しいガイド版と差分を見て、自分のコードも修正する価値があります。
- テストや CHANGELOG の更新は不要な種類の変更(ドキュメント修正)として扱われており、実際にテスト追加や CHANGELOG 変更は行われていません。
- 参考情報 (あれば)
- Rails ガイド: Working with JavaScript in Rails (edge / 最新版)
https://edgeguides.rubyonrails.org/working_with_javascript_in_rails.html - ドキュメント貢献ガイド:
https://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation
この PR は、上記ガイドの質を高め、特に async/await を使った例を正しく理解・利用できるようにするための細かな改善です。
#56208 Fix bug when txn isolation would not reset or when requires_new is set
マージ日: 2025/12/6 | 作成者: @kirs
- 概要 (1-2文で)
このPRは、トランザクションの分離レベル (isolation level) を一時的に変更した後に元に戻らないバグと、requires_new: trueなネストトランザクションで isolation 指定が正しく扱われないバグを修正しています。Active Record のトランザクション分離レベルを多用するアプリケーション(例: Shopify)のワークフローに直接影響する問題へのピンポイント修正です。
- 変更内容の詳細
※実際のコード全文は省略しますが、PR説明と diff 情報から復元できる挙動レベルで解説します。
背景: #55549 での変更漏れ
#55549 で導入された「トランザクションの isolation レベルをブロック単位で変更する」機能において:
- ブロック実行後に
current_transaction.isolationを元の値に戻していなかった requires_new: trueのネストトランザクション時に isolation 指定を正しく扱っていなかった
という2点のバグが発生していました。
修正ポイント1: isolation レベルのリセット
ActiveRecord::ConnectionAdapters::DatabaseStatements#transaction(あるいはそれを内部で呼び出すヘルパー)の中で、
「トランザクション開始前の isolation」を退避し、ブロック終了後に必ず元の isolation を復元するように修正されています。
イメージとしては、以下のような処理になります:
def transaction(**options, &block)
# もともとの isolation を保存
previous_isolation = current_transaction.isolation
# options[:isolation] が指定されていれば、その isolation でトランザクション開始
# ...
yield
ensure
# ブロック終了後に isolation を元の値に戻す
current_transaction.isolation = previous_isolation
endこれにより、以下のようなケースで isolation が「後続のトランザクションにも引き継がれてしまう」という誤動作が解消されます。
# 例: これまでは tx2 も :serializable のままになってしまうバグがあった
User.transaction(isolation: :serializable) do
# ここだけ serializable を想定
...
end
User.transaction do
# 本来はデフォルトの isolation (DB の設定) を期待
...
endPR後は、tx1 の isolation 指定は tx1 のスコープだけに閉じるようになります。
修正ポイント2: requires_new: true での isolation の扱い
requires_new: true を指定したネストトランザクションに isolation を渡した場合のハンドリングも修正されています。
典型例:
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では、
requires_newの inner トランザクションについても isolation が正しく設定される- inner の終了後に、outer トランザクションの isolation に確実に戻す
というロジックが追加されています。
テストの追加
activerecord/test/cases/transaction_isolation_test.rb に8行のテストが追加されており、少なくとも以下のようなシナリオをカバーしていると考えられます:
- isolation を指定したトランザクションの後、isolation 未指定のトランザクションを開始すると元の isolation に戻っていること
requires_new: trueのネストトランザクションで isolation を指定しても、外側の isolation は変化しないこと
これにより、前述の2つのバグが再発しないことを保証しています。
- 影響範囲・注意点
影響範囲
- Active Record のトランザクション API を使い、特に
isolation:オプション・requires_new: trueを併用しているコードが対象です。 transactionを素朴にtransaction do ... endとだけ使っているアプリケーションへの影響はほぼありません(isolation を明示指定していなければ振る舞いは DB デフォルトのまま)。
アプリケーション側で変わる可能性がある挙動
「意図せず高い isolation が引き継がれていた」ケースが正常化される
以前は、あるブロックでisolation: :serializableを指定すると、その後のトランザクションでも serializable が継続してしまっていた可能性があります。
これを前提にしていた(=バグ依存)コードがあると、以降のトランザクションの isolation が「元に戻る」ことで挙動が変わります。requires_newネスト時の isolation がより直感的になるrequires_new付きの中で isolation を変えると、その効果は inner トランザクションの中だけに留まり、outer には漏れなくなります。
これにより、想定していた isolation 境界(outer: read committed / inner: serializable など)が、実装通りに機能するようになります。
注意点
- 「特定の処理以降、アプリ全体のトランザクション isolation が変わる」という挙動に気づかずに依存していた場合、パフォーマンス特性やデッドロック発生率が変わる可能性があります。
isolation を明示指定している部分があれば、その前後のトランザクションの挙動を確認した方が安全です。 - マルチDB構成やカスタム connection handler を使っている場合も、
current_transaction単位で isolation がきちんとスコープされるようになるため、 isolation まわりの暗黙的な前提がないかを確認してください。
- 参考情報 (あれば)
- このPRで修正している元の変更:
- 同じ著者による、より大きな関連 PR(今回よりスコープが広いもの):
- 対象ファイル:
activerecord/lib/active_record/connection_adapters/abstract/database_statements.rbactiverecord/test/cases/transaction_isolation_test.rbactiverecord/CHANGELOG.md(バグ修正として明記)
このPRは、小さなコード変更で isolation のスコーピングを正す「バグフィックス」であり、API 仕様の変更というよりは、ドキュメントや直感に沿った挙動への修正、と考えると理解しやすいです。
#56299 Fix typos in command line guide
マージ日: 2025/12/5 | 作成者: @Saidbek
概要 (1-2文で)
Rails ガイド「Command Line」のドキュメント中の軽微なタイポ(重複語・スペース抜け・重複行)を修正した PR です。コードや挙動の変更はなく、文章表現のみの修正です。変更内容の詳細
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
- 修正前(イメージ)
- 例:
「The
bin/rails」のように "The" とバッククォートの間のスペースが抜けていた箇所を修正- 修正前:
The\bin/rails`` - 修正後:
The \bin/rails``
- 修正前:
いずれもガイドの可読性・整合性を高めるための、純粋な文章レベルの修正です。
- 影響範囲・注意点
- 対象はドキュメントガイドのみであり、Rails 本体のコード、挙動、API には一切影響がありません。
- コマンドの使い方や意味は変わっておらず、既存アプリケーションやツールチェーンへの影響もありません。
- ドキュメント参照時に、より正確・自然な英語で読めるようになります。
- 参考情報 (あれば)
- PR: https://github.com/rails/rails/pull/56299
- 関連ガイド: 「Command Line」(Command Line Tools and bin/rails に関する公式ガイド)
#56213 Respect config.log_level to emit debug events
マージ日: 2025/12/5 | 作成者: @claudiob
- 概要 (1-2文で)
Rails の「デバッグイベントを出すかどうか」の判定条件を、環境名(development かどうか)ではなくconfig.log_levelに基づくように変更する PR です。これにより、RAILS_LOG_LEVEL=debugを設定すれば test や production でもデバッグイベントを出力できるようになります。
- 変更内容の詳細
変更点の本質
以前の PR #55657 では、「開発環境 (development) のときだけデバッグイベントを有効にする」という実装になっていました。この PR では、その条件を:
Rails.env.development?から:
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. 環境変数で指定(推奨されている使い方)
RAILS_LOG_LEVEL=debug bin/rails serverRAILS_LOG_LEVEL は Rails が config.log_level に反映するので、この PR により debug イベントが有効になります。
2. 設定ファイルで直接指定
config/environments/production.rb:
Rails.application.configure do
config.log_level = :debug
endこれにより production でも development 同様にデバッグイベントが出力されます。
- 影響範囲・注意点
環境に依存しない挙動になる
以前は「development だけ特別扱い」でしたが、今後は「ログレベルが debug かどうか」で決まります。- test / production で
config.log_level = :debugにしているプロジェクトでは、これまで出ていなかったデバッグイベントが新たに出る可能性があります。
- test / production で
デフォルト設定では互換性維持
生成直後の Rails アプリの典型的な設定では:- development:
log_level = :debug→ デバッグイベント「あり」(従来通り) - test: 通常は
:debug→ この PR により test でも debug イベントが出るようになっている可能性がある(以前は「development 限定」だった場合との差分) - production:
log_level = :info→ デバッグイベント「なし」(従来通り)
test 環境での挙動は、#55657 の実装内容や各プロジェクトの
config.log_level設定に依存します。
もし test でログが多すぎると感じる場合は、config/environments/test.rbのconfig.log_levelを:info以上に上げることで抑制できます。- development:
本番で debug を有効にする際の注意
- ログ量が非常に増える可能性があるため、ストレージ・ログローテーション・ログ集約基盤(例: ELK、Loki など)の負荷に注意が必要です。
- デバッグログに機微情報が含まれる場合、コンプライアンス上のリスクも増えます。production で
:debugを使う場合は、ログ内容の棚卸し・マスキングを検討してください。
- 参考情報 (あれば)
- この PR: https://github.com/rails/rails/pull/56213
- 元になった PR: https://github.com/rails/rails/pull/55657
- ログレベルの公式ドキュメント:
https://guides.rubyonrails.org/debugging_rails_applications.html#log-levels - 関連するユースケースの議論:
https://github.com/rails/rails/pull/55900#issuecomment-3445390380
#51238 Extract ActionText::Editor base class and ActionText::TrixEditor adapter
マージ日: 2025/12/5 | 作成者: @seanpdoyle
- 概要 (1–2文で)
- Action Text にエディタ共通の基底クラス
ActionText::Editorと、Trix 向けアダプタActionText::TrixEditorを導入し、「Trix 前提の API」を整理・非推奨化した PRです。 - これにより、将来的に Trix 以外のリッチテキストエディタを公式な拡張ポイントから統一的に統合できる土台が整えられています。
- 変更内容の詳細
2-1. ActionText::Editor 基底クラスとアダプタ層
新しく以下の構成が導入されています。
- 基底クラス:
ActionText::Editor - アダプタ:
ActionText::TrixEditor(Trix 用)
- 周辺クラス:
ActionText::Editor::RegistryActionText::Editor::Configurator
これらは「エディタごとの差異を Action Text 本体から切り離す」ためのアダプタ層です。
従来はクラス名やメソッド名、引数名に trix が直接現れていた箇所がありましたが、それらを editor ベースの抽象化に移し替えています。
主な責務の集約
PR 説明にある通り、多数のクラスメソッド・インスタンスメソッドが ActionText::TrixEditor へ集約されています。
多くは「元の実装をそのままコピー」したうえで、命名に含まれる trix を editor に置き換える程度の変更に留めてあり、振る舞いは最大限維持されています。
例イメージ(あくまで概念的なもの):
# 以前: 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 つの種類の非推奨があります。
モジュール / クラスの非推奨
- Trix 特化、または内部用であるにもかかわらず
publicな定義だったクラス / モジュールが対象。 - 今回、責務の一部または全てを
ActionText::Editor/ActionText::TrixEditor側へ移譲し、旧クラス/モジュールは deprecate されたうえで、新実装経由で動くようにラップされているものがあります。 - ソース中では
:nodoc:が付いていなかったものの、実質 internal 想定の物が主な対象です。
- Trix 特化、または内部用であるにもかかわらず
メソッドの非推奨
- 名前や引数に
trixを含むもの、またはドキュメント / コメントがなく internal 想定と判断された public メソッドが対象。 - 可能な限り、旧メソッドは warning を出しつつ
ActionText::TrixEditorのメソッドを呼び出すだけの thin wrapper に変わっているため、既存コードは直ちに壊れずに移行が可能です。
- 名前や引数に
例(あくまでイメージ):
# 旧 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(...)
# 実装は従来とほぼ同じ
end2-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を登録する処理が含まれている形が想定されます。
- Engine 初期化時に
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 選択の書き方
- 影響範囲・注意点
3-1. 一般的な Rails アプリ (通常の Action Text + Trix 利用)
rich_text_areaなど、通常の公開 API を使っているだけのアプリは基本的にそのまま動作します。- ただし、以下に当てはまる場合は deprecation warning が出る可能性があります。
ActionText::TrixAttachmentやActionText::Contentの Trix 依存メソッドを直接呼んでいるtrix_*を名前に含むメソッドをアプリケーション側で利用している
現時点では互換ラッパーがあるので即座に壊れることは少ないですが、ログの deprecation warning をチェックし、将来のメジャーバージョンでの削除を見越して 新しい Editor ベースの API への移行を検討した方がよいです。
3-2. エディタ連携を拡張しているライブラリ / エンジン作者
Action Text と外部エディタ(例: Quill, CKEditor)を連携する gem / plugin を書いている場合、この PR はかなり重要です。
- これまで Trix 専用の内部 API をハックしていた部分を、
ActionText::Editorのサブクラスとして正式に実装できる方向性が示されました。 - まだアダプタインターフェイスは「実装詳細扱い」ですが、今後ここが公式拡張ポイントとして明文化されていく流れが読み取れます。
- これまで Trix 専用の内部 API をハックしていた部分を、
新規実装の基本方針:
class MyCoolEditor < ActionText::Editorを定義- Registry に登録(
ActionText::Editor::Registry.register(:my_cool_editor, MyCoolEditor)のような形が想定される) - 添付ファイル変換・HTML 生成・ツールバー設定などを TrixEditor と同等のインターフェイスで実装
まだ細部仕様はコードを読まないと分からないため、この PR を足掛かりに actiontext/lib/action_text/editor/*.rb を確認する必要があります。
3-3. 内部 API への依存
- PR で「これは実装詳細であり、API としては private のつもり」と明言されているため、
ActionText::Editor一式も将来のバージョンで破壊的変更が入る可能性があります。 - ライブラリレベルでここに依存する場合は、バージョン制約やテストで追従コストを見込んでおくべきです。
- 参考情報
- PR: https://github.com/rails/rails/pull/51238
- 関連 Issue: https://github.com/rails/actiontext/issues/41
- 変更された主なファイル:
actiontext/lib/action_text/editor.rbactiontext/lib/action_text/editor/trix_editor.rbactiontext/lib/action_text/editor/registry.rbactiontext/lib/action_text/editor/configurator.rbactiontext/lib/action_text/attachments/conversion.rbactiontext/lib/action_text/attachments/trix_conversion.rbactiontext/lib/generators/action_text/install/install_generator.rbactiontext/lib/action_text/engine.rbactiontext/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-2文で)
Railsの「Getting Started」ガイド内で、ナビゲーションバーにログインメニューを追加する手順の記述ファイル名が誤っていた点を修正し、index.html.erbではなくapplication.html.erbを参照するように更新したドキュメント専用のPRです。アプリケーションコードや挙動には一切変更はありません。
- 変更内容の詳細(あればサンプルコードも含めて)
- 対象ファイル:
guides/source/getting_started.md - 変更点の主旨:
- ガイドの 11.3 節付近で、「
Loginリンクをindex.html.erbに追加する」と書かれていたが、実際にはナビゲーションバーが定義されているapplication.html.erbに追加するべきであるため、説明文中のファイル名をapplication.html.erbに修正。
- ガイドの 11.3 節付近で、「
- 変更されたのは Markdown 上の記述のみで、コード例やロジック自体が変わったわけではなく、「どのファイルを編集するのが正しいか」という参照先の修正です。
(サンプルイメージ: 実際のPR文面からの推定)
<!-- app/views/layouts/application.html.erb のナビバー例 -->
<nav>
<%= link_to "Home", root_path %>
<%= link_to "Login", login_path %>
</nav>従来のガイドでは、上記のようなナビゲーションを index.html.erb 側に書くかのような記述になっていたため、それを「レイアウトファイルである application.html.erb に書く」という正しい形に揃えています。
- 影響範囲・注意点
- 影響範囲:
- Rails Guides(ドキュメント)のみであり、Rails本体のコード、テスト、挙動には影響しません。
- Getting Started ガイドを見ながらチュートリアルを進めているユーザーにとって、ナビゲーションバーの編集箇所が正確になり、混乱が減ります。
- 注意点:
- 既に旧ガイドの指示通りに
index.html.erbにナビバーやログインリンクを追加していた場合は、レイアウト (application.html.erb) に移動するのが望ましい構造です。- レイアウトに置けば全ページで同じナビが共有されるため、チュートリアルの意図にも合致します。
- CHANGELOG は更新されていない通り、リリースノート的な追跡も不要な軽微なDocs変更です。
- 既に旧ガイドの指示通りに
- 参考情報 (あれば)
- PR本体: rails/rails#56236
- 関連コンセプト:
- Railsにおけるレイアウトファイル:
app/views/layouts/application.html.erb- 全ページ共通のヘッダー/フッター/ナビゲーションバーなどを定義するのが一般的
- 個別ビュー (
index.html.erbなど) にナビバーを書くと、他アクションで共有されず冗長になりやすい
- Railsにおけるレイアウトファイル:
このPRにより、Getting Startedガイドが実際のRailsのベストプラクティス(共通ナビはレイアウトに置く)と一致するようになっています。
#51951 ActiveStorage immediate variants
マージ日: 2025/12/5 | 作成者: @tomrossi7
- 概要 (1–2文で)
Active Storage のバリアント(variants)生成方式に、添付と同時に同期的に生成するためのimmediate: trueオプションが追加されました。これにより、これまでの「初回アクセス時にオンデマンド生成」や「バックグラウンドでの preprocessed 生成」に加え、トラフィック集中時の“生成レース”を避ける運用がしやすくなります。
- 変更内容の詳細
新オプション immediate: true
has_one_attached / has_many_attached の variant 定義で、既存の preprocessed: true と同じ要領で immediate: true を指定できます。
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/preprocessedvariants を列挙 - 指定された attachment について、対象の variants を生成する
- これにより、複数の variants 生成を一元的に扱えるようになり、テストしやすくなっている
※TransformJob は引き続き存在し、個々の変換実行はこれを通じて行われますが、呼び出し元の整理・責務分担が行われています。
ActiveStorage::NamedVariant の拡張
activestorage/app/models/active_storage/named_variant.rb の変更により:
- 各 named variant が
immediate/preprocessedなどのオプションを持てるように整理 - 変換用のオプションと、生成タイミングを表すオプションがはっきり切り分けられた形になっている
概ね次のようなイメージで、variant 定義のメタデータとして管理されます:
attachable.variant :thumb, resize_to_limit: [100, 100], immediate: true
# => name: :thumb, transformations: { resize_to_limit: [100, 100] }, immediate: trueAttachment 周りの変更
activestorage/app/models/active_storage/attachment.rb などで:
- 添付の作成時に、関連する
immediatevariants を検出して生成するフックが追加 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.rbnamed_variant_test.rbattachment_test.rbなど
- 従来の variant 生成テストの一部が整理・削減(
preview_image_job_test.rbほか) - ガイド
active_storage_overview.mdにimmediateオプションの使い方が追記され、preprocessedとの違いが説明されている
- 影響範囲・注意点
パフォーマンス特性の変化
immediate: trueを付けた variants は「添付の保存と同じトランザクション or リクエストの中」で生成されるため、- 添付時のレスポンスタイムが伸びる可能性
- 同時に大量のファイルをアップロードするケースでは、アプリサーバ・ストレージの負荷増加
- 一方で、添付直後のアクセス集中時に variant 生成が競合する問題は緩和されます。
immediateとpreprocessedの使い分け- 高トラフィックで、「アップロード直後に確実に完成した variant を配信したい」場合は
immediate: true - 生成負荷をアプリリクエストから切り離したい場合は
preprocessed: true - どちらも付けない場合は従来どおり「初回アクセス時にオンデマンド生成」
設計としては、UX とインフラ負荷のトレードオフを見ながら、重要な variant だけ
immediateにするのが現実的です。- 高トラフィックで、「アップロード直後に確実に完成した variant を配信したい」場合は
既存コードへの互換性
immediateを使わない限り、挙動は基本的に後方互換です。- ただし Active Storage の内部クラス(
NamedVariant,Representable周りなど)に依存している場合は、- メソッドの呼び出し経路
- variant メタデータの持ち方 が変わっている可能性があるので、独自拡張をしているプロジェクトは差分確認が必要です。
ジョブキュー設定
preprocessed/immediateどちら経由であっても、内部ではTransformJobなどバックグラウンドジョブを使う部分があります。- Active Job のアダプタ(Sidekiq, Delayed Job など)の設定やキュー名をカスタマイズしている場合は、新ジョブ
CreateVariantsJobのキュー設定も確認してください。
- 参考情報 (あれば)
- 該当 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.rbactivestorage/app/models/active_storage/attachment.rbactivestorage/app/models/active_storage/named_variant.rbactivestorage/app/models/active_storage/variant*.rb
#56279 Use pattern matching on sql instead of string match event payload name
マージ日: 2025/12/5 | 作成者: @zzak
- 概要 (1-2文で)
Active Storage のコントローラテストで、ActiveSupport::Notifications のイベント名文字列に対するマッチングをやめ、代わりに SQL 文の内容に対してパターンマッチするように変更した PRです。テストが内部実装(通知名の文字列形式)の変更に依存しないようにし、より堅牢にしています。
- 変更内容の詳細
対象ファイル:
activestorage/test/controllers/representations/redirect_controller_test.rb
このテストでは、ActiveStorage::Representation のリダイレクト処理中に発行される DB クエリ(代表的には SELECT など)が実行されるかどうかを ActiveSupport::Notifications 経由で検証している部分があります。
もともとの実装では、おそらく以下のように「通知イベント名の文字列」を対象にパターンマッチしていました(イメージ):
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 / 正規表現など)を行うようにテストを変更しています。例としては以下のようなイメージです:
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 のフォローアップとして、テストの堅牢性を高める目的で行われています。
- 影響範囲・注意点
影響範囲
- 対象は Active Storage の
Representations::RedirectControllerに関するテストのみで、アプリケーション本体の挙動には影響しません。 - CI 上のテスト安定性が向上し、「通知メッセージのフォーマットが変わっただけ」でテストが落ちるリスクが減ります。
- 対象は Active Storage の
注意点
- 今後、同様に ActiveSupport::Notifications をテストで利用する場合、イベント名そのものの文字列フォーマットへの依存は避け、より安定した情報(SQL 文、payload の構造化されたキー、バインド値など)で検証するのが推奨されます。
- SQL に対するパターンマッチも、スキーマ変更(テーブル名やカラム名の変更)には依存する点は変わらないため、「何に依存するか」を意識してテストを書く必要があります。
- 参考情報 (あれば)
- 元 PR: #56225(今回のフォローアップ元。通知に関するテストまわりの変更が含まれている可能性が高い)
- 関連 API:
ActiveSupport::NotificationsActiveSupport::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-2文で)
Rails のデバッグビューでRAILS_EDITORを使っている場合に、ERB テンプレート内の構文エラーが正しく処理されずエラーになる問題を修正した PR です。ActiveSupport::SyntaxErrorProxyが、エディタ連携用に必要なabsolute_pathを返せるようにしています。
- 変更内容の詳細
問題の背景
RAILS_EDITOR環境変数を設定していると、例外発生時のデバッグ画面から「編集」ボタン等を通じて、対応するファイルを手元のエディタで開けるようになります。- その際
debug_view.rbでは、バックトレースの各フレームからabsolute_pathを取得して、editor_url(RAILS_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などの二次的な例外が発生しなくなります。
変更イメージ(概念的な擬似コード):
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が正常動作する)。
- 影響範囲・注意点
- 影響範囲:
RAILS_EDITORを設定していて、かつ ERB テンプレート内で構文エラーが起きるケースに限定されます。- 通常の例外(構文エラー以外)や
RAILS_EDITORを使っていない環境では挙動への影響はほぼありません。
- 利用者側での対応:
- Rails をこのコミットを含むバージョンに更新すると、
RAILS_EDITOR利用時の構文エラー画面が安定し、エディタ連携も期待通り動作します。 - 既に
RAILS_EDITORを使っていて、構文エラーで debug view が落ちる/エディタリンクが出ないといった症状がある場合、この修正を取り込むことで解消されます。
- Rails をこのコミットを含むバージョンに更新すると、
- 後方互換性:
- 新たに public API を壊す変更はなく、
BacktraceLocation#absolute_pathの追加は、標準のバックトレース仕様に沿う形の拡張であり、後方互換的です。
- 新たに public API を壊す変更はなく、
- 参考情報 (あれば)
- 関連 Issue: #56284
- 直前の変更(原因となった変更): #55295
- 該当コード(debug view 側で
absolute_pathを使っている箇所):actionpack/lib/action_dispatch/middleware/debug_view.rbのeditor_url生成周り
#56290 Allow schema_dump configuration to be an absolute path.
マージ日: 2025/12/5 | 作成者: @flavorjones
- 概要 (1-2文で)
database.ymlのschema_dump設定に「絶対パス」を書けるようにし、エンジン固有のスキーマファイルをアプリ本体とは別の場所(例: gem の中)に置けるようにした PR です。既存の相対パスの挙動は維持したまま、絶対パスの場合はそのまま利用されるようになります。
- 変更内容の詳細
これまでの挙動
ActiveRecord::Tasks::DatabaseTasks.schema_dump_path は、以下のような扱いをしていました:
database.ymlのschema_dump設定値は「DatabaseTasks.db_dirを基準とした相対パス」とみなされる- 絶対パスを指定しても
db_dirと結合されてしまい、意図通りに使えない - 環境変数
SCHEMAだけは絶対パス指定が可能
そのため、エンジン側で
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を基準に解決
擬似コードで表すと:
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 で次のように書けます:
<% 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に、この挙動変更(新機能)が記載されています。
- 影響範囲・注意点
- 相対パス指定の既存挙動は変わらない
- これまでどおり
schema_dump: schema.rbなどの設定はdb/ディレクトリ配下として扱われます。
- これまでどおり
- 絶対パスを指定した場合だけ新挙動
schema_dump: /foo/bar/schema.rbのようなパスは、その場所に直接スキーマが出力されます。
- 複数データベース+エンジン構成で有用
- マルチ DB(
primary,replica,saasなど)かつエンジンを gem として分離している場合に、各エンジンごとにスキーマファイルを分けて管理しやすくなります。
- マルチ DB(
- パスの権限・存在には注意
- 絶対パス先のディレクトリが存在しない、または書き込み権限がない場合は、当然ながら
db:schema:dump実行時にエラーになります。 - CI や本番環境などでパスを変える場合は、
database.ymlの ERB で環境変数等を噛ませる想定になります。
- 絶対パス先のディレクトリが存在しない、または書き込み権限がない場合は、当然ながら
- 参考情報 (あれば)
- PR: https://github.com/rails/rails/pull/56290
- 背景として挙げられている OSS アプリとエンジン:
- アプリ本体: https://github.com/basecamp/fizzy
- SaaS 向けエンジン: https://github.com/basecamp/fizzy-saas
- 関連する既存機能:
migrations_pathsは以前から絶対パス指定をサポートしており、今回のschema_dumpがそれに揃えられた形です。
#56283 ActionText: Validate RemoteImage URLs
マージ日: 2025/12/5 | 作成者: @flavorjones
- 概要 (1-2文で)
Action Text でリッチテキストに添付される「リモート画像」の URL を検証し、image.pngのような相対パス風の値が誤ってアセットパイプラインに流れないようにした変更です。これにより 500 エラーや意図しないアセット解決/悪用の可能性を減らします。
- 変更内容の詳細
背景となる問題
Trix 経由などでリッチテキストに画像が埋め込まれた際、画像の src が以下のような値だったとします:
<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 にも、この挙動変更が記載されました。
- 影響範囲・注意点
影響範囲
- 対象は Action Text で「リモート画像」を扱う部分のみ です。
- 具体的には、Trix エディタなどから埋め込まれた
<img src="...">に対し、srcが完全なリモート URL(例:https://...)なら: 従来通りRemoteImageとして扱われる。- そうでない場合(相対パス等):
RemoteImageではなくなり、MissingAttachableとして処理される。
既存アプリへの影響・移行上の注意
相対パスを「リモート画像」として使っていた場合
- これまではたまたま動いていた(あるいは 500 を rescue していた)ケースが、
今後はMissingAttachableとしてレンダリングされ、画像が表示されなくなる可能性があります。 - 対応策:
- リッチテキストに埋め込む画像 URL は、
https://example.com/foo.pngなどの完全な URL に修正する。 - あるいは、画像は Active Storage 経由で添付する(
<action-text-attachment>)運用に寄せる。
- リッチテキストに埋め込む画像 URL は、
- これまではたまたま動いていた(あるいは 500 を rescue していた)ケースが、
500 エラー対策として
Template::Errorを rescue していた場合- この PR により、「不正な相対 URL がアセット解決で例外を出す」というパスは通らなくなるので、 そのためだけの rescue は不要になる可能性があります。
- ただし他の理由で同じ例外を rescue している場合は、その用途と切り分けてください。
セキュリティ上の意味合い
- 任意の
src="foo.png"が、アセットパイプライン経由で別のローカルアセットに解決される可能性が減ります。 - 特に「ユーザ入力の HTML を Action Text に取り込み、レンダリングしている」ようなアプリでは、 意図しないアセットへのアクセスや情報露出のリスクが下がります。
- 任意の
- 参考情報 (あれば)
- 該当 PR: https://github.com/rails/rails/pull/56283
- 関連する内部 API:
ActionText::Attachables::RemoteImage.from_nodeActionText::ContentActionView::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-2文で)
Rails のtag.*ヘルパで、引数とブロックの両方でコンテンツを渡した場合に「後勝ちで上書き」されていた挙動が、「結合して連結」される挙動に変更されました。これにより、tag.div("Hello ") { "World" }は<div>Hello World</div>を返すようになります。
- 変更内容の詳細
これまでの挙動
ActionView::Helpers::TagHelper の tag ヘルパ(例: tag.div)において、以下のように文字列引数とブロックを同時に渡した場合:
tag.div("Hello ") { "World" }従来はブロック側が優先され、引数の "Hello " は無視されていました:
<div>World</div>これは「サイレントに引数コンテンツを捨てる」挙動でした。
変更後の挙動
この PR により、引数で渡したコンテンツとブロックで返したコンテンツを結合して出力するようになりました。
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 の追記が行われています。
サンプルコード
# 以前(〜この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>- 影響範囲・注意点
影響範囲
ActionView::Helpers::TagHelperのtagAPI を使い、同じタグ呼び出しで「引数のコンテンツ」と「ブロック」を併用している箇所すべてに影響します。content_tagではなくtag.div,tag.spanなどの「新しい tag API」を使っているコードが対象です。
後方互換性の観点
- 従来はブロック側が常に優先されていたため、「引数は無視されるもの」と期待していたコードでは出力が変わります。
- 例: 「引数側にダミー値」や「古いAPIとの互換用値」を入れていた場合など。
- 多くのケースでは、明示的に引数とブロックの両方を渡していれば「両方とも表示したい」意図であることが多いため、挙動としては自然になった一方で、HTML 出力の差分が生じる可能性はあります。
- 従来はブロック側が常に優先されていたため、「引数は無視されるもの」と期待していたコードでは出力が変わります。
テンプレートレビューのポイント
tag.xxx("something") do ... endのような呼び方をしているビューテンプレートを grep 等でチェックし、- その両方のコンテンツが出力されることが想定どおりか
- 余計な空白や改行が増えないか(
"Hello "の末尾スペースなど) を確認すると安全です。
パフォーマンス
- 追加の連結処理は非常に軽量であり、実質的なパフォーマンスへの影響はほぼ無視できるレベルと考えられます。
- 参考情報 (あれば)
- 対象ファイル:
actionview/lib/action_view/helpers/tag_helper.rbactionview/test/template/tag_helper_test.rbactionview/CHANGELOG.md
- 変更規模:
- 3ファイル変更
- 追加 14 行 / 削除 1 行
- 関連ヘルパ:
tag.div,tag.span,tag.pなどtagオブジェクト経由のタグ生成系ヘルパ全般に適用されます。
#56294 Fix MemCacheStore for connection_pool >= 3
マージ日: 2025/12/5 | 作成者: @fatkodima
- 概要 (1-2文で)
ActiveSupport::Cache::MemCacheStoreがconnection_poolgem の v3 以降と正しく連携できるように、初期化ロジックを1行だけ修正したPRです。直前の PR #56292 でのconnection_pool対応からmem_cache_storeだけ漏れていた部分をフォローしています。
- 変更内容の詳細
※ 実際の差分は 1 行の置き換えのみです。PR タイトルと文脈からすると、以下のような意図の修正です(擬似コードで説明します):
# 変更前(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 に対応したものに揃える変更です。
- 影響範囲・注意点
- 影響対象:
config.cache_store = :mem_cache_storeなどでMemCacheStoreを使用しつつ、connection_poolgem v3 以上を利用しているアプリケーション。
- 期待される改善:
connection_poolv3 系でMemCacheStoreを使ったときに発生していた例外(例えば「uninitialized constant ConnectionPool::Wrapper」や、逆に Wrapper 必須なのに使っていないことによるエラーなど)が解消される。RedisCacheStoreなど他のキャッシュストアと同様に、connection_poolv3+ と一貫した挙動をとる。
- 互換性:
- 変更は 1 行のみで、
connection_poolの公式な v3 向け移行パスに沿った修正のため、通常の利用において後方互換性リスクは小さいと考えられます。 - もしアプリ側で
MemCacheStoreの内部実装(@poolのクラスや API)に依存したメタプログラミングをしている場合は、@poolの型変更などにより影響する可能性があります(そのような利用は通常は非推奨です)。
- 変更は 1 行のみで、
- 参考情報 (あれば)
- この PR: https://github.com/rails/rails/pull/56294
- 直前の関連 PR (
connection_pool対応の本体と思われる): https://github.com/rails/rails/pull/56292 connection_pool3.0 リリース関連情報(API 変更の背景などを確認するときに有用):
#56280 Use Digest::UUID.uuid_v5 to generate uuid instead of hand-rolled
マージ日: 2025/12/4 | 作成者: @zzak
- 概要 (1-2文で)
Rails 内部で UUID を生成していた箇所を、自前実装ではなく Ruby 標準ライブラリDigest::UUID.uuid_v5を使うように置き換えた PR です。これにより UUID 生成ロジックが標準化され、保守性と一貫性が向上します。
- 変更内容の詳細
対象ファイル:
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 を作りたい用途に適しています。
- 影響範囲・注意点
影響範囲
Rails::DevtoolsControllerが利用している UUID 生成ロジックのみが影響を受けます。- 本番アプリケーションに直接影響するようなコア機能ではなく、開発支援用ツールに限定された変更です。
- UUID の計算方法が「完全に同一」でない場合、同じ入力から得られる UUID が従来と変わる可能性があります。ただし devtools 用なので、互換性リスクは小さいと考えられます。
注意点
Digest::UUID/Digest::UUID.uuid_v5が利用できる Ruby バージョンが前提になります。
Rails をこのコミット以降のバージョンで使う場合、対応する Ruby バージョン(Digest::UUIDを含むもの)が必要になります。- もし外部ツールやスクリプトが「devtools が返す UUID の値そのもの」に依存していると、生成値の違いにより影響が出る可能性がありますが、一般的な Rails アプリではほぼ無視できるレベルです。
- 参考情報 (あれば)
- 該当 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-2文で)
RedisCacheStoreがconnection_poolgem のバージョン 3 以上で動かなくなる問題 (#56291) を修正した PR です。RedisCacheStoreとconnection_poolのインターフェイスの差異を吸収するための、ごく小さな互換性修正が 1 行だけ入っています。
- 変更内容の詳細
※PR本文の実コード断片は提示されていないため、ここでは Rails と connection_pool 3 系の変更点から「どのような修正か」を技術的に推測・整理します。実際の 1 行差分は、この方向性のいずれかです。
背景: RedisCacheStore と connection_pool の関係
ActiveSupport::Cache::RedisCacheStore は内部で connection_pool を使って Redis クライアントのプーリングを行います。典型的な利用イメージは以下のようなものです。
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 にアップデートしたタイミングで NoMethodError や ArgumentError が発生します。
この PR の実質的な修正内容
差分が「+1/-1 の 1 行のみ」であることから、以下のようなピンポイント修正の可能性が高いです。
一例:
# 変更前(connection_pool 2 系までは動くが、3 系で壊れる書き方)
@pool.with do |conn|
# Redis 操作
end
# 変更後(3 系の挙動に合わせた呼び出し方)
@pool.with do |redis|
# Redis 操作
endまたは、キーワード引数・ブロック引数の扱いが変わったことに伴う修正:
# 例1: ブロックあり/なしのパターンに対応
@pool.with { |client| client.public_send(command, *args, **kwargs) }
# 例2: `then` のようなメソッドチェーンとの組み合わせを修正
@pool.with { |client| yield client } # と明示して新しいシグネチャに合わせるいずれにせよ、
connection_pool2 系でも動く- かつ 3 系でもエラーにならない
という互換的な呼び出し方に 1 行書き換えた修正と考えられます。
- 影響範囲・注意点
影響範囲
ActiveSupport::Cache::RedisCacheStoreを利用しており、- かつアプリケーションが
connection_pool3 以上を採用している場合
に、キャッシュアクセス時の例外発生や挙動不良が解消されます。
Rails 自身が内部で利用する Redis ベースのキャッシュ(config.cache_store = :redis_cache_store など)にも関係するため、Redis キャッシュ利用アプリ全般に影響しうる変更です。
注意点
- 既に
connection_pool2 系を使っていて問題なく動いているアプリに対しては、今回の修正による後方互換性ブレイクはほぼ考えにくく、安全なマイナー修正とみなせます。 - もし
connection_pool3 系にアップグレードした際に- Redis のキャッシュ操作で
NoMethodError,ArgumentError, またはundefined method 'with'/wrong number of argumentsなどが出ていた場合は、この PR を含む Rails バージョンへの更新で解消される可能性が高いです。
- Redis のキャッシュ操作で
- Rails 本体よりも先に
connection_poolだけを major upgrade していた場合、Rails をこの PR を含むバージョンまで引き上げることが推奨されます。
- 参考情報 (あれば)
- 該当 Issue: #56291 —
RedisCacheStoreがconnection_pool3 系で壊れる不具合報告 - PR: #56292 “Fix
RedisCacheStoreforconnection_pool>= 3” - 関連しうる外部リソース:
connection_poolGitHub リポジトリの 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-2文で)
Date.currentの RDoc 内で、コード参照Time.zone.todayだけが<tt>でマークアップされていなかった問題を修正し、他のコード参照と統一したドキュメント整備のPRです。コードの挙動やAPI自体は一切変わっておらず、見た目と可読性の改善のみです。
- 変更内容の詳細 (サンプルコード含む)
対象ファイル:
activesupport/lib/active_support/core_ext/date/calculations.rb
Date.current のドキュメントコメント(RDoc)の中にある、以下のような説明文が元々ありました:
# Returns Time.zone.today when config.time_zone is set, otherwise just returns
# Date.today.この文中で、
Time.zoneconfig.time_zoneDate.today
は <tt>...</tt> でコードとしてマークアップされていた一方で、Time.zone.today だけがプレーンテキストになっていました。
PRでは、この不整合を解消するために Time.zone.today を <tt> で囲むように1行を修正しています:
- # 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 が他と同様に等幅フォント・コードとして表示されるようになります。
- 影響範囲・注意点
影響範囲
- Railsアプリケーションの挙動、API仕様、型や返り値などには一切変更なし。
- 影響があるのは 生成されるドキュメント(RDoc) の表示のみ。
- RDoc / edgeguides / 各種自動生成ドキュメントで、
Date.currentの説明におけるコード表記が統一されます。
注意点
- 既存コードの修正やテストの変更はなく、バージョンアップに伴う互換性問題はありません。
- CIを回す必要のないドキュメントのみ変更のため、タイトルに
[ci skip][docs]が付いています。
- 参考情報 (あれば)
- 対象メソッド:
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-2文で)
このPRは、Rails本体のコメント中の英単語のスペルミスを修正しただけの変更です。コードの挙動や公開APIには一切影響しません。変更内容の詳細
修正内容
- Action Controller のコメント:
Explictly→Explicitly
- Action Cable (PostgreSQL サブスクリプションアダプタ) のコメント:
purposedly→purposely
- Action Controller のコメント:
対象ファイル
actionpack/lib/action_controller/metal/redirecting.rbactioncable/lib/action_cable/subscription_adapter/postgresql.rb
どちらも「コメントのみ」の変更であり、実行パスに関わるコードは一切変更されていません。codespell というスペルチェッカーツールで検出された誤字を修正した、ドキュメント改善系のPRです。
(疑似的なイメージ例)
# Before
# Explictly redirect to ...
# After
# Explicitly redirect to ...# Before
# This is purposedly left ...
# After
# This is purposely left ...- 影響範囲・注意点
影響範囲
- 実行時の挙動、パフォーマンス、セキュリティ、API 仕様に影響はありません。
- 変更箇所はコメントのみのため、バイナリ・コンパイル成果物にも実質的な差分は出ません。
- ドキュメントとしての読みやすさ・正確さがわずかに向上しています。
注意点
- スペル修正に伴うコメント内テキストの変更のみのため、既存コードやアプリケーション側での対応は不要です。
- コメントを参照している外部ツール(ドキュメント生成等)を使っている場合も、意味は変わらず、破壊的な変更にはなりません。
- 参考情報 (あれば)
- PR: https://github.com/rails/rails/pull/56289
- 使用ツール:
codespell(OSSプロジェクトでよく使われる英単語スペルチェックツール) - Rails の機能的な変更ではなく、品質改善(コメントの整備)カテゴリの変更として扱って問題ありません。
#56288 Add SecureRandom.base32
マージ日: 2025/12/4 | 作成者: @monorkin
- 概要 (1-2文で)
Rails のSecureRandomに、人間が読みやすく誤読しづらい Crockford 方式の Base32 文字列を生成するSecureRandom.base32が追加されました。これにより、2FA コードやマジックリンク用トークンなどを、安全かつユーザーフレンドリーな形式で簡単に生成できます。
- 変更内容の詳細
追加された API
ActiveSupport の SecureRandom 拡張に、新しく SecureRandom.base32 が追加されています。
既存の hex, base64, urlsafe_base64, base58 と同じパターンで実装されており、暗号学的に安全な乱数ソースを使いつつ、文字集合を Crockford Base32 にしたものです。
典型的な使い方は以下のようなイメージです(PR 本文から推測される使用感):
# 例: 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.rbBASE32_ALPHABETを追加- 既存の Base 系メソッドと同じテンプレートで
base32を追加
activesupport/test/core_ext/secure_random_test.rb- 新しい
base32メソッド用のテストを追加(長さ・文字集合・ランダム性などを検証)
- 新しい
activesupport/CHANGELOG.md- ActiveSupport に
SecureRandom.base32を追加した旨を記載
- ActiveSupport に
これにより、アプリケーション側で毎回「安全な乱数 + 人間向け文字集合」の実装を自前で用意する必要がなくなります。
- 影響範囲・注意点
影響範囲
- 新規メソッド追加のみで、既存 API の挙動変更や削除はありません。
- ActiveSupport をロードしている Rails アプリ/ライブラリで
SecureRandom.base32が利用可能になります。 - 2FA コード、招待コード、マジックリンク用トークンなど「人が読む・打つ前提のコード」を生成するユースケースで特に有用です。
注意点
- 「Base32」とだけ書かれているものには複数のバリアント(RFC 4648, Crockford など)があるため、この
base32は Crockford Base32 固定である点に注意してください。
既存の RFC 4648 準拠の Base32 実装とは互換ではありません。 - Rails 側では
base58が Bitcoin 形式を特に名前に含めずに採用している慣例にならって、base32という汎用的な名前で Crockford バリアントを提供しています。 - 既に独自の Base32 実装(特に Crockford Base32)を持っている場合は、徐々に
SecureRandom.base32に置き換えることで、暗号学的安全性と実装のシンプルさを統一できる可能性があります。 - 「長さの指定方法」(引数の意味)は
SecureRandom.base58等と合わせているはずなので、置き換え時にはbase58との対応関係を確認しておくと安全です。
- 「Base32」とだけ書かれているものには複数のバリアント(RFC 4648, Crockford など)があるため、この
- 参考情報 (あれば)
- PR 本文で挙げられている資料:
- Crockford's Base32 定義:
https://www.crockford.com/base32.html - Base32 (Wikipedia, Crockford セクション):
https://en.wikipedia.org/wiki/Base32#Crockford's_Base32
- Crockford's Base32 定義:
- 類似 API(インターフェイスの参考になるもの):
SecureRandom.hexSecureRandom.base64SecureRandom.urlsafe_base64SecureRandom.base58
#56287 Fix ActiveRecord::SoleRecordExceeded#record to return the relation
マージ日: 2025/12/4 | 作成者: @byroot
- 概要 (1-2文で)
ActiveRecord::SoleRecordExceeded例外オブジェクトの#recordメソッドが、本来返すべき「元の Relation」ではなく誤った値を返していたリグレッションを修正し、再び Relation を返すようにした PR です。sole/sole!利用時に、例外から安全にクエリ条件を再利用できる挙動が復元されています。
- 変更内容の詳細
背景
ActiveRecord::Relation#sole/#sole!は「レコードがちょうど1件だけ存在する」ことを期待する finder です。- 0件:
ActiveRecord::RecordNotFound(sole!) /nil(sole) - 1件: そのレコードを返す
- 2件以上:
ActiveRecord::SoleRecordExceededを発生させる
- 0件:
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 内部実装ですが、挙動の意味として):
# 修正後のイメージ
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.soleでActiveRecord::SoleRecordExceededを発生させる- rescue ブロック内で
error = e; end assert_equal relation, error.record
という形で、SoleRecordExceeded#record が元の Relation をそのまま返すことを明示的に保証するテストです。
これにより、今後の変更で同じリグレッションが再発しないように保護されています。
- 影響範囲・注意点
影響範囲
- 対象:
ActiveRecord::Relation#sole/#sole!を使っていて、「複数レコードヒット時に発生するActiveRecord::SoleRecordExceeded例外の#recordを利用しているコード」。- 例外の
recordからクエリ条件を再利用するような実装:rubybegin 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 等が顕在化する可能性があります(もともと仕様違反な使い方です)。
- 参考情報 (あれば)
- 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」を参照すると、SoleRecordExceededとrecordの意味付けが確認できます。
#56159 Restore missing Postgres type decoding
マージ日: 2025/12/4 | 作成者: @matthewd
- 概要 (1-2文で)
Rails の PostgreSQL アダプタで、moneyとbytea型の値が「公開されているクエリ実行 API (select_value/exec_queryなど)」経由でも正しくデコードされるように復元した PRです。既存アプリへの影響を最小化するための設定フラグと、PG::Connection.unescape_byteaの安全策(ダブルデコード防止)も併せて導入されています。
- 変更内容の詳細
背景
- もともと内部用メソッド
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 らしい値」が返ってくるようになる:
# 例: 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 のモンキーパッチ
この変更により問題となり得るパターン:
# これまで: 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 で追加されたものに依存します):
# 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.rbmoney/byteaについて、「デコード機能を有効にした際に期待通りの Ruby 値が返るか」を検証。
postgresql_adapter_test.rb- 公開 API(
select_value,select_all,exec_query等)での型デコードの有無、設定フラグによる挙動の違いを網羅的にテスト。
- 公開 API(
railties/test/application/configuration_test.rb- 設定フラグのデフォルト値、
new_framework_defaultsによる切り替えを検証。
- 設定フラグのデフォルト値、
- 影響範囲・注意点
影響があるコードパターン
AR モデルを経由しない生クエリ実行で
money/byteaを扱っている場合:select_value/select_all/exec_queryなどでmoney/byteaカラム(またはそれらを返す式)を直接取得しているコードは、戻り値の型・内容が「より正しくデコードされた値」に変わる可能性があります。- 特に
byteaは、文字列エスケープを前提にした独自処理を書いていると、二重デコードなどの不整合が起きかねないので注意が必要です。
PG::Connection.unescape_byteaを明示的に呼んでいるコード:- 典型的なパターン:
PG::Connection.unescape_bytea(ActiveRecord::Base.connection.select_value(...)) - 新挙動有効化後も壊れないよう PR 側で防御策を入れていますが、
将来的にはこのような「手動 unescape」は不要になるため、
デコード済み値を前提にコードを整理していく方が安全です。
- 典型的なパターン:
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でフラグをオフにしておけば、
これまでと同じ「ほぼデコードなし」の挙動を維持できる。
- 新挙動を有効にする際の推奨手順:
- テスト環境でフラグをオンにして CI を回す。
- ログ検索・grep などで
PG::Connection.unescape_byteaの利用箇所を洗い出す。 select_value/exec_queryを使ってmoney/byteaを取得している箇所の戻り値を確認し、
文字列前提のパース処理などがないかチェックする。- 問題がなければ本番でもフラグをオンにする。
- 参考情報 (あれば)
- 対象 PR: https://github.com/rails/rails/pull/56159
- 過去の実装参照(内部
queryでのみデコードしていた頃):
https://github.com/rails/rails/blob/077c3ad60db4c43cccb8f4637b53b8d8eb7a3c19/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L1167-L1168 - 設定ガイド (
configuring.md) に、このフラグや PostgreSQL の型デコード周りの説明が追記されているので、アプリ側の設定方針を決める際に参照するとよいです。
#56267 Move async execution into QueryIntent
マージ日: 2025/12/4 | 作成者: @matthewd
- 概要 (1–2文で)
- Active Record の非同期実行まわりの内部構造が変更され、実際のクエリ実行ロジックを
FutureResultからQueryIntentに移し、コネクションプールの非同期キューにはQueryIntentを積むようになりました。 - これにより、
FutureResultはあくまでユーザー向けの結果ハンドラに近い役割になり、非同期実行の責務がより明確に分離されています。
- 変更内容の詳細
全体像の変化
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側に寄せられたと考えられます(擬似的なイメージ):rubyclass 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など) の表面上の振る舞いは基本的に従来と互換のはずですが、裏側の管理単位がFutureResult→QueryIntentにシフトしています。
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の振る舞い)は変わっていない」ことの裏付けとも言えます。
- 影響範囲・注意点
アプリケーション開発者への影響
- 公開 API レベル(
Relation#load_async,FutureResult#resultなど)は原則として互換であり、通常のアプリケーションコードは変更不要と思って問題ありません。 - ただし、以下に該当する場合は注意が必要です:
- ActiveRecord の非同期実行内部 (
FutureResultの内部状態やコールバック) に依存したメタプログラミングをしている。 FutureResultを継承 / モンキーパッチして非標準な挙動を追加している。- コネクションプールの async キュー内部(キューに載るオブジェクトのクラスなど)を前提にした独自コードを書いている。
- ActiveRecord の非同期実行内部 (
- そのような場合、今後は
QueryIntentが実行単位であることを前提に実装・パッチを書き換える必要があります。
ライブラリ・フレームワーク作者への影響
- ActiveRecord の非同期クエリをラップしている gem / フレームワーク (GraphQL の
load_async連携など) で、FutureResultの内部のメソッドやインスタンス変数を直接触っている場合は互換性確認が必要です。 - 将来的に非同期実行の拡張(キャンセル、タイムアウト、優先度制御など)を行う場合は、
QueryIntentが拡張ポイントになる可能性が高く、FutureResultではなくQueryIntentの API に注目すべきです。
パフォーマンス・安定性の観点
- 実行の責務が
QueryIntentに移ったことで、以下のようなメリットが狙われていると考えられます:- 責務分離によりコードが読みやすくなり、バグが減る。
- 将来的な最適化(実行戦略の変更、バッチ化、より賢いスケジューリングなど)を
QueryIntentに閉じた形で行いやすくなる。
- この PR 自体は大規模な挙動変更を意図していないようですが、実装が大きく書き換わっているため、非同期クエリを多用するアプリではアップグレード後に実働環境での負荷テストや例外パターンの確認を推奨します。
- 参考情報 (あれば)
- 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-2文で)
ActiveRecord::Relation#scopingの挙動、とくにall_queries: trueを使ったブロックレベルスコープの動きやネスト時の挙動について、Rails Guides(Active Record クエリインターフェイスガイド)に明確な説明とサンプルを追加したドキュメント専用のPRです。コード本体の挙動変更はなく、ガイドの追記のみです。
- 変更内容の詳細
対象ファイル:
guides/source/active_record_querying.mdに約 49 行程度のドキュメント追記
主な追加内容:
2-1. Relation#scoping の基本的な説明強化
Model.where(...).scoping { ... }形式で「一時的に」デフォルトスコープのような条件を適用できる、という点をガイドで明示。- ブロック内で発行されるクエリに対して、その
Relationが持つ条件やincludes,joins,orderなどが反映されることを説明。
例(典型的なイメージ・ガイドに沿った内容のサンプル):
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を指定すると、ブロック内で発行される「すべてのモデルのクエリ」に対してスコープが影響する、という挙動を説明。
イメージとなるサンプル:
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は後勝ちなど)ため、その点を含めて例で示しています。
例(趣旨イメージ):
Post.where(published: true).scoping do
Post.where(archived: false).scoping do
Post.first
end
end- 発行される SQL は
WHERE published = TRUE AND archived = FALSEのように、ネストされたスコープが組み合わさる形になることを説明。 - また、
orderやlimitなどについても、内側のスコープで指定したものがどのように作用するかに簡単に触れていると考えられます。
2-4. 使いどころ・注意に関する簡単なガイダンス
ガイド内で、以下のような「使い方のヒント」も含めています:
- ブロックレベルで一時的にスコープを切り替えたい場合に
scopingを使うと便利であること。 default_scopeの代わりに「明示的な」スコープを適用する用途としても使えること。- ただし
all_queries: trueは影響範囲が広く、意図しないクエリに条件が掛かる可能性があるため注意すべき、という旨の注意喚起。
- 影響範囲・注意点
- これは ドキュメントのみ の変更であり、
Relation#scopingの実装には一切変更がありません。 - 既存コードの挙動・パフォーマンスには影響しません。
- 影響があるのは「開発者がこのガイドを読んだときの理解・使い方」であり、特に:
- これまで
scoping/all_queries: trueの挙動が曖昧だった部分が明文化される。 all_queries: trueの利用を検討する際に、影響範囲やネスト時の挙動を事前に把握しやすくなる。
- これまで
- 強力なため、テストコードや一部のバッチ処理などで
all_queries: trueを多用する場合、ガイドのサンプルを参考にしながら「どこまで影響が及ぶか」を確認すると安全です。
- 参考情報 (あれば)
- 対象 API:
ActiveRecord::Relation#scoping
https://edgeapi.rubyonrails.org/classes/ActiveRecord/Relation.html#method-i-scoping
- PR のプレビュー(ガイド本文の日本語訳を作る際などの参考に):
#56275 Use Bundler.with_unbundled_env for GeneratorsTestHelper run_app_update
マージ日: 2025/12/3 | 作成者: @zzak
- 概要 (1-2文で)
Generators のテストヘルパーでrun_app_updateを実行する際に、Bundler の影響を受けないようにBundler.with_unbundled_envを使うように変更した PR です。これにより、bundle exec配下でテストを実行しても、アプリ更新用コマンドが意図しない Gem 環境に縛られずに動くようになります。
- 変更内容の詳細
対象ファイル:
railties/test/generators/generators_test_helper.rb
GeneratorsTestHelper 内の run_app_update メソッドで、Bundler.with_unbundled_env を使用するようになりました。概ね以下のような変更です(イメージ・擬似コード):
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_GEMFILEやBUNDLE_BIN_PATHなど Bundler が設定する環境変数がクリアされ、いわゆる「素の」Ruby/Gem 環境でコマンドが実行されます。- Generators のテストは、テンプレートアプリケーションを生成・更新するようなコマンド(
bin/rails app:update相当)を内部的に実行しますが、それらが「テストフレームワーク側の Bundler 環境」に引きずられないようにしています。
- 影響範囲・注意点
影響範囲
- Rails 本体のランタイム挙動ではなく、「Generators 関連のテストコード」に限定された変更です。
- 主に Rails 開発者・コントリビュータ、および Rails を fork して独自にテストを回している人が対象になります。
- CI などで
bundle exec ruby -Itest ...のように Bundler 経由でテストを走らせている場合に、Generators のテストがより安定・再現性の高い形で動作するようになります。
解決している問題の方向性
- Issue #56271 で報告された、
run_app_update実行時に Bundler による環境変数や Gem ロードパスが干渉して失敗・不整合が起きる問題を緩和/解消する意図があります。 - 典型的には、テスト実行側の
Gemfileに縛られて、本来想定していない Gem バージョンでapp:updateが走ってしまう、といった症状を防ぎます。
- Issue #56271 で報告された、
注意点
Bundler.with_unbundled_envを使うと、ブロック内では「現在の Bundler コンテキスト外」の世界になるため、その中で別のbundle execを前提とするような処理を追加すると挙動が変わる可能性があります。- ただし、このメソッドはテスト専用ヘルパー内で完結しているため、一般的な Rails アプリケーション開発者がこの PR を意識する必要はほぼありません。
- 参考情報 (あれば)
- 該当 PR: https://github.com/rails/rails/pull/56275
- 紐付く Issue: https://github.com/rails/rails/issues/56271
Bundler.with_unbundled_envドキュメント:- https://bundler.io/v2.5/guides/bundler_integration.html#running-commands-in-a-clean-environment(「Running commands in a clean environment」)
#56272 Skip all system test files on app generation
マージ日: 2025/12/2 | 作成者: @eileencodes
- 概要 (1-2文で)
Rails アプリ生成時に常に作られていたapplication_system_test_case.rbを、システムテストを生成したときに限って作成するように変更した PR です。scaffold でのシステムテストスキップ対応 (#55743) の取りこぼしを補完し、不要なシステムテスト関連ファイルが生成されないようにしています。
- 変更内容の詳細
背景
- 以前の 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.rbrailties/lib/rails/generators/test_unit/scaffold/templates/application_system_test_case.rb.ttscaffold 用の 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にも検証が追加されています。
- devcontainer 環境での test 実行やファイル有無の前提が変わる影響を受けるため、
既存の app generator テストから、もはや生成されない
application_system_test_case.rbを前提としたアサーションが削除されています (-9 行)。
- 影響範囲・注意点
新規プロジェクト (
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?など) - なければ生成
を行うようにする必要があります。
- 存在チェック (
- 「ベースのシステムテストケースファイルはアプリ生成時に必ず存在する」
- 自作の generator が Rails 標準の app generator / scaffold generator の挙動に依存している場合、
- 参考情報 (あれば)
関連 PR:
- システムテスト生成のスキップ対応: https://github.com/rails/rails/pull/55743
- 本 PR の問題発見に繋がった PR: https://github.com/rails/rails/pull/56252
該当 PR:
- Skip all system test files on app generation (#56272)
https://github.com/rails/rails/pull/56272
- Skip all system test files on app generation (#56272)
#56269 Add a top level bin/test
マージ日: 2025/12/2 | 作成者: @byroot
- 概要 (1-2文で)
Rails リポジトリのルートにbin/testが追加され、サブディレクトリへcdしなくても各サブプロジェクト(activemodel, activerecord など)のテストを直接実行できるようになりました。あわせて、失敗テストの「再実行コマンド」もリポジトリルートからそのままコピペで使える形に改善されています。
- 変更内容の詳細
2-1. bin/test の新規追加
リポジトリ直下に bin/test が追加され、Rails コアの各コンポーネントのテストを簡単に実行できるようになりました。
使い方の例(PR本文のまま):
# コンポーネント単位のテスト
$ 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 activemodelやcd activerecordせずにテストを叩ける。 - 単一サブプロジェクト内でのテスト実行を想定
一回のコマンドで「activemodel と activerecord を同時に」など、複数サブプロジェクトを混在させた実行はサポートしていないと明記されています。 - おそらく内部的には既存の
tools/test.rb/tools/test_common.rbを呼び出す薄いラッパーになっており、引数に応じて対象コンポーネントを解決している形です。
bin/test の追加によって、これまで
cd activemodel && bin/test test/cases/dirty_test.rbのようにしていた作業が
bin/test activemodel/test/cases/dirty_test.rbだけで済むようになります。
2-2. テストレポーター (reporter.rb) の修正
railties/lib/rails/test_unit/reporter.rb が変更され、Rails 自身のテストを実行したときに表示される「失敗テストの再実行コマンド」が、ルートディレクトリからそのまま使える形に変わりました。
従来は、CI やローカルで失敗したときに、次のようなコマンドが表示されていました:
bin/rails test /rails/activerecord/test/cases/adapters/postgresql/connection_test.rb:187- 絶対パス (
/rails/...) になっていて、そのままコピペしても意図通りに動きにくい。 - 実行パスも
bin/railsで、コンポーネント単体テストを回す文脈とやや合わない。
これが PR によって、以下のようになります:
bin/test activerecord/test/cases/adapters/postgresql/connection_test.rb:187- リポジトリルートからの相対パスになり、そのままコピペで実行可能。
- コマンドが
bin/testに統一され、今回追加されたワークフローと整合します。
2-3. tools/test.rb / tools/test_common.rb の微修正
tools/test.rb と tools/test_common.rb にはそれぞれ 1〜2 行程度の軽微な修正が入っています。
推測される内容:
bin/testからの呼び出しをサポートするために、エントリーポイントの扱いや引数のパース方法を少しだけ調整。- コンポーネント名(
activemodel,activerecordなど)とパスの解決ロジックをbin/testと共有するための小さな共通化。
このあたりは内部実装の整合性調整であり、外部 API/インターフェースとしては主に bin/test の追加とレポーターの出力変更が本質的な変更です。
- 影響範囲・注意点
- 対象は Rails リポジトリの開発者・コントリビューター
Rails を利用するアプリ開発者向けではなく、「Rails 本体のテストを書く・直す人」の開発体験改善が主目的です。 - 既存のテスト実行方法は引き続き使用可能
cd activerecord && bundle exec ruby -Itest test/cases/...のような既存手法は変わらず動作し、bin/testは追加のエイリアス・ラッパーの位置づけです。 - 複数サブプロジェクトへの同時テスト実行はサポート外
一回のbin/test呼び出しでactivemodelとactiverecordのテストを同時に指定するような使い方は想定されていません。
→ そのようなケースでは、これまで通り各ディレクトリで個別に実行するか、独自スクリプトを使う必要があります。 - CI ログやローカル実行時の「再実行コマンド」が変わる
今後はbin/test ...形式のコマンドが出力されるため、ドキュメント・社内 Wiki 等で「失敗テストの再実行方法」を案内している場合は、必要に応じて記述を更新するとよいです。 - パス前提の変化
再実行コマンドは「リポジトリルートからの相対パス」を前提にしています。そのため、コマンドをコピペするときは「Rails リポジトリのルートで実行しているか」を意識する必要があります。
- 参考情報 (あれば)
- 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-2文で)
Api アプリケーションジェネレータのテスト (ApiAppGeneratorTest) で、bundle install(正確にはkamal実行時の Bundler 解決)に依存していた部分をスタブ化し、テストが外部の gem 解決に依存しないようにした PR です。これによりテスト時間が約 3 秒 → 約 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の実行部分をテストダブルに差し替え例(イメージ):
rubydef stub_kamal File.write("bin/kamal", <<~RUBY) #!/usr/bin/env ruby # no-op for tests RUBY FileUtils.chmod("+x", "bin/kamal") endもしくは、システムコール層のスタブ:
rubyKamal::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 件(スタブが正しく呼ばれたこと/生成物の検証が増えた)
- 影響範囲・注意点
- 影響範囲
- Rails 本体のプロダクションコードには影響せず、「railties のテストコードのみ」が対象です。
- 特に
ApiAppGeneratorがkamal用のファイル/設定をどう生成するかをテストする部分のみが変更されています。
- 良くなった点
- テストが外部の gem レジストリ状態(
rails 8.2.0.alphaがインストールされているかどうか)に依存しなくなる - CI / ローカル環境共にテストが安定し、ノイズログがなくなる
- テスト実行時間の短縮
- テストが外部の gem レジストリ状態(
- 注意点
- スタブにより「実際に
kamalコマンドを走らせたときの挙動」はこのユニットテストではカバーしない形になっています。- これは他のジェネレータテストと同じ方針で、「ジェネレータの責務(ファイル生成やコマンド呼び出しのセットアップ)だけ」をテストし、外部ツールの実際の挙動はそのツール自身のテストに任せる、という設計です。
kamal側のインターフェースが変わった場合は、このテストだけでは気付きにくくなるので、- 統合テストや手動検証など、別レイヤーのテストで補完する必要があります。
- スタブにより「実際に
- 参考情報 (あれば)
- 類似パターンがすでに存在するテスト:
PluginGeneratorTestActionTest::Generators::InstallGeneratorTest
- テストがスタブしている対象は、
bundle installや外部 CLI(kamal)の実行部分- Rails のジェネレータテストでは「外部コマンド呼び出しを no-op 化して、生成されたファイルのみ検証する」というパターンが広く使われています。
#56256 Include HTTP_FORWARDED header in IpSpoofAttackError message if available
マージ日: 2025/12/1 | 作成者: @zzak
- 概要 (1-2文で)
Rails のIpSpoofAttackErrorが発生した際のエラーメッセージに、従来のX-Forwarded-Forに加えてForwarded(HTTP_FORWARDED) ヘッダの値も含められるようになりました。これにより、IP なりすまし検出時のデバッグ情報がより充実します。
- 変更内容の詳細
どの部分が変わったか
変更ファイル:
actionpack/lib/action_dispatch/middleware/remote_ip.rbactionpack/test/dispatch/request_test.rb
ActionDispatch::RemoteIp ミドルウェア内で IpSpoofAttackError を生成するときのメッセージに、利用可能であれば HTTP_FORWARDED ヘッダを含めるように修正されています。
従来はおおむね次のようなメッセージでした(例):
IP spoofing attack?! HTTP_CLIENT_IP="1.2.3.4" HTTP_X_FORWARDED_FOR="5.6.7.8"この PR により、Forwarded ヘッダがある場合は:
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 内容からの推定を含むサンプルです(実際の実装のニュアンスとして):
# 例: エラー生成部分のイメージ
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 の値が含まれていることを検証するテストが追加されています。
- 影響範囲・注意点
影響範囲
ActionDispatch::RemoteIpを使用していて、かつ IP なりすまし (spoofing) 判定が走った場合の「例外メッセージ」が変わります。- ランタイムの挙動(例外が発生するかどうか、どの条件で発生するか)自体は変わっていません。あくまでエラーメッセージの情報量追加です。
ログ解析・監視との互換性
IpSpoofAttackErrorのメッセージ文字列をパースして独自に解析している場合、HTTP_FORWARDEDの追加でフォーマットが変わる可能性があります。- 「メッセージ全体の完全一致 (exact match)」でアラート設定やテストを書いている場合は、これを機に「部分一致」や「正規表現ベース」に変更した方が安全です。
Forwarded ヘッダを利用している環境
- RFC 7239 準拠の
Forwardedヘッダを積極的に使っているリバースプロキシ構成(例: 一部の CDN / LB)では、IP spoofing 検出時のトラブルシューティングがしやすくなります。 - どのヘッダでどの IP が渡されているかが 1 つの例外メッセージにまとまるため、プロキシの設定ミスや想定外のヘッダ上書きの発見に役立ちます。
- RFC 7239 準拠の
- 参考情報 (あれば)
- 関連 Issue:
#56186- この PR は、
IpSpoofAttackErrorにForwardedヘッダ情報が含まれないためデバッグが難しい、という報告を解消するものです。
- この PR は、
- 対象コンポーネント:
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-2文で)
ActiveJob.perform_all_laterが、ジョブクラスに設定されたenqueue_after_transaction_commitを正しく尊重するように修正された PR です。これにより、トランザクション完了後にキュー投入すべきジョブが、perform_all_later経由でも期待通りに動作します。
- 変更内容の詳細
背景
- 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 と同じようなラッピングロジックを、配列形式の複数ジョブに対して適用する形になっています。
参考となる利用イメージ(疑似コード):
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 される
end2) テストの追加
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 = falseor 未設定のジョブは、perform_all_laterでも即時 enqueue される
これにより perform_later と perform_all_later 間での挙動の一貫性がテストで担保されています。
- 影響範囲・注意点
影響を受けるケース
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がこれに揃えられます。
- トランザクションがロールバックされた場合、
- 参考情報 (あれば)
- 修正元 PR(バグ報告 / 初期修正案): https://github.com/rails/rails/pull/56246
- この PR はオリジナル作者がメンテナによる force push を許可していなかったため、本 PR (#56264) としてやり直されています。
- 機能そのものの背景:
- Rails ガイド: Active Job のドキュメント(
enqueue_after_transaction_commit周辺) - ActiveRecord のトランザクションコールバック (
after_commit) と連携する仕組みで、DB 一貫性の担保に有用です。
- Rails ガイド: Active Job のドキュメント(
#56258 Add schematized json for has_json
マージ日: 2025/11/30 | 作成者: @dhh
- 概要 (1–2文で)
Rails の JSON 属性に対して「型付き・スキーマ付き」でアクセスできるschematized_json機能が ActiveModel に追加されました。has_json/has_delegated_jsonを使うことで、UI から文字列で値を渡しても、DB には boolean / integer / string の正しい JSON 型で保存されるようになります。
- 変更内容の詳細
2-1. 機能の概要
目的
- JSON カラムを「なんでも入る Hash」ではなく、事前に決めたキーと型を持つ半構造化データとして安全に扱えるようにする。
- フォームや API からはすべて文字列として送られてきても、モデル側で boolean / integer などに 自動キャスト したい。
サポートされる型
booleanintegerstring- ネストは非対応(フラットなキーのみ)
2-2. 代表的な API と使い方
PR 説明の例をベースに整理します。
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 型
endhas_json
対象: モデルの JSON カラム(例:
settings)使い方:
- キー名 => デフォルト値 で渡すと、デフォルト値の型から JSON 型が決まる
true/false→ boolean10→ integer"Hello"→ string
- デフォルト値を持たないキーは
symbolで型指定できる(例:staff: :boolean)
- キー名 => デフォルト値 で渡すと、デフォルト値の型から JSON 型が決まる
実行時の挙動:
Account.new時に、定義されたキーに デフォルト値がセット される。before_saveでも再度デフォルトセットが行われる(nil のままの場合などの補完)。
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が返す「アクセサオブジェクト」を介さず、モデル直下にメソッドを生やすパターン。
a = Account.new
a.beta # => false (flags["beta"] の boolean 値)
a.staff # => nil (デフォルトなし、型は boolean)
a.staff = true
a.staff? # => true2-3. 文字列入力からの自動型変換
この PR のキモは、「UI からは全部文字列でも、JSON としては型付きで扱える」点です。
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,42(to_iベース)
- string
- そのまま保存(トリム・バリデーション等はこの層では行わない想定)
2-4. 実装位置
- 追加ファイル:
activemodel/lib/active_model/schematized_json.rb- スキーマと JSON を結び付ける中核クラス/モジュールが定義されている。
activemodel/lib/active_model.rb,activemodel/lib/active_model/api.rbschematized_json機能を Active Model API から利用できるように require / include。
- テスト:
activemodel/test/cases/schematized_json_test.rb- 型付け、デフォルト適用、キャスト、
has_delegated_jsonの挙動などがカバーされている。
- 型付け、デフォルト適用、キャスト、
- 影響範囲・注意点
対象バージョン
- PR #56258 がマージされる Rails の次リリース以降で利用可能(少なくとも edge / main ブランチ)。
JSON カラムの前提
- DB 側は
json/jsonb(PostgreSQL)など、ネイティブ JSON 型または text + serialize でも動くが、Rails 的には JSON 属性として定義されている前提。
- DB 側は
型の制約
- サポートは boolean / integer / string のみ。
- 配列・オブジェクト・ネストした JSON 構造はサポート外。
- これらを使いたい場合は、従来通り
store,store_accessor,serialize, JSON カスタム型などを使う必要あり。
- これらを使いたい場合は、従来通り
既存データとの整合性
- すでに JSON カラムに異なる型が入っている場合、
- 読み出し時に想定と違う型が来る可能性がある。
- その場合の挙動(強制キャストかエラーか)は実装依存なので、マイグレーションやデータクレンジングが望ましい。
- すでに JSON カラムに異なる型が入っている場合、
before_save でのデフォルト適用
before_saveでもデフォルト値が適用されるため、- 「ユーザーが nil を明示的に入れたが、保存時にデフォルトで上書きされる」ケースをどう扱うかに注意。
- 「nil を許容したい」場合は、この schematized_json に乗せるべきか、仕様を検討した方がよい。
フォーム/フロントエンド側への利点
- すべて文字列で送ってよい、という前提が作れるので、JS 側の型管理がシンプルになる。
- ただし、boolean の
"true"/"false"などは UI からのバリエーションを考慮したうえで、変換ロジックに合わせた値を送る必要がある。
- 参考情報 (あれば)
- 追加クラス:
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/ テストコードを参照するとより正確に把握できます)
- GitHub: rails/rails #56258 「Add schematized json for has_json」