Ruby on Rails PR Digest - 2026年 2月
このページは rails/rails リポジトリにマージされたPull Requestを自動的に収集し、AIで要約したものです。
#56896 Improve Trix support for Action Text to_markdown conversion
マージ日: 2026/2/27 | 作成者: @flavorjones
- 概要 (1-2文で)
Trix が生成する HTML に対してActionText::MarkdownConversionの変換精度を上げる PRです。<del>(取り消し線)、Trix 特有の<div> + <br>ベースのレイアウト、および pretty-print された HTML による不要な空白・改行の混入に対応しています。
- 変更内容の詳細
2-1. <del> を取り消し線(strikethrough)としてサポート
背景:
Trix は取り消し線に <s> ではなく <del> を使うが、既存の Markdown 変換は <del> を考慮していなかった。
対応:ActionText::MarkdownConversion に <del> を strikethrough として扱うロジックを追加。つまり、Trix の HTML:
<p>normal <del>deleted</del> text</p>が Markdown 変換されると、例えば次のような形になります(実際の記法はライブラリ側の既存仕様に準拠):
normal ~~deleted~~ textこれにより、Trix からの取り消し線が Markdown 上でも正しく表現されるようになります。
2-2. Trix の <div> + <br> による改行表現への対応 (visit_div の追加)
背景:
Trix は段落やブロック要素として <div> を使い、改行を <br> で表現します。
従来の MarkdownConversion は <div> を汎用ブロックとして扱っており、Markdown 側で余計な改行や空行が入るケースがありました。
対応:visit_div ハンドラを追加し、Trix 由来の <div> を「その子要素をそのまま連結するだけ」のブロックとして扱うように変更。
<div>自体では余分な改行や空行を挿入しない- 段落や改行の表現は
<br>に任せる
例:
<div>line 1<br>line 2<br>line 3</div>従来は、<div> ごとに改行が追加されて、意図しない空行が混ざる可能性があったが、
この PR 以降は <br> を基準にした自然な行区切りに近い Markdown になります:
line 1
line 2
line 3(実際の出力は実装済みの <br> 処理に依存しますが、意図としては「<div> が邪魔をしない」ようになります。)
2-3. pretty-print された HTML のインデント・改行を Markdown から除去
背景:
Trix の内容がシリアライザやテンプレートエンジンを経由して、以下のように pretty-print(インデント・改行整形)された HTML になるケースがあります:
<div>
Some <strong>bold</strong> text
</div>このとき、テキストノードが分割されているため、素朴に変換すると:
- 改行やインデントの空白がそのまま Markdown に出てしまう
- インライン要素の前後に意図しないスペースや改行が入り、レイアウトが崩れる
対応:
テキストノードのインデント・余計な改行の除去
- pretty-print 由来と判断できるインデントや改行をストリップし、Markdown に不要な空白が混入しないようにした。
インライン要素の隣接時は 1 つだけスペースを残す
- テキストノード境界が「インライン要素の隣にある」ときは、空白を完全には除去せず「1 つのスペース」に畳み込む。
- これにより、単語同士が結合してしまうのを防ぎ、Markdown のトークンがくっついて解釈される問題を回避。
例:
HTML(pretty-print 済み):
<div>
Some <strong>bold</strong> text
</div>素朴な実装だと:
Some
bold
textあるいは Someboldtext になる可能性がありますが、この変更により:
Some **bold** textのように、自然な単語区切りとインライン装飾が維持されます。
2-4. テストの追加・更新
actiontext/test/unit/markdown_conversion_test.rb に約 77 行のテストが追加・更新されており、以下がカバーされています:
<del>が strikethrough として正しく Markdown に変換されること- Trix スタイルの
<div>/<br>構造が余計な空行なしに期待通り Markdown になること - pretty-print された HTML(インデントや改行含み)が、不要な空白を含まない Markdown に変換されること
- インライン要素境界でのスペース折りたたみロジックが、単語結合や Markdown 記号の誤結合を起こさないこと
- 影響範囲・注意点
対象:
ActionText::MarkdownConversionを利用して、Trix HTML → Markdown 変換を行っているコード。- 特に Rails 8.2.pre 系で新機能として利用しているプロジェクト。
期待される挙動の変化:
- Trix の
<del>取り消し線が Markdown に正しく反映されるようになります。 <div>が段落として改行を追加しなくなり、Trix の<br>ベースのレイアウトに沿った Markdown となります。- pretty-print された HTML を入力しても、Markdown 側に余計な改行やインデント空白が漏れにくくなります。
- Trix の
互換性・注意点:
- これまで
<div>を「1 ブロックとして改行付きで扱う」前提で Markdown 出力を利用していた場合、出力フォーマットが変わる可能性があります(特に空行の有無)。 - pretty-print 由来の空白・改行をあえて Markdown に残していたケースがあるなら、その挙動は変わります(通常は望ましい修正ですが、差分が増える可能性があります)。
- インライン要素周辺のスペースが「1 つのスペース」に正規化されるため、意図的に複数スペースを使っていたケースでは出力が変わり得ます。
- これまで
- 参考情報 (あれば)
- PR: https://github.com/rails/rails/pull/56896
- 対象バージョン: Rails 8.2.pre の新機能に対する改善(CHANGELOG には未記載)。
- Trix の block / newline 仕様:
- デフォルト block 要素:
<div> - 改行表現:
<br> - 実装は Trix の
piece_view.js,block_view.js参照。
- デフォルト block 要素:
#56901 Don't guard action_dispatch_request and action_cable load hooks
マージ日: 2026/2/27 | 作成者: @gmcgibbon
- 概要 (1-2文で)
本PRは、action_dispatch_requestとaction_cableのロードフック(ActiveSupport.on_load)周りから「ガード条件」を外し、これらがアプリ起動時にすでにロードされてしまうケースでも確実にフックが実行されるようにする変更です。あわせて、Action Cable サーバーを読み込む際にケーブルチャンネルが正しくロードされるよう、コールバックの順序・内容も整理しています。
- 変更内容の詳細
背景
- 前提として、Rails では以下のように「ロードフック」を使って、あるコンポーネントが読み込まれたタイミングで追加設定を行うことがあります:ruby
ActiveSupport.on_load(:action_dispatch_request) do # Request オブジェクトにメソッド追加、設定など end ActiveSupport.on_load(:action_cable) do # Action Cable サーバーの設定など end - しかし本PRの説明によると、「本番環境ではアプリ初期化中に
action_dispatch_requestとaction_cableがすでにロードされてしまう」ため、- 「リクエストがブート(初期化)完了後にロードされる」という前提に依存したガード(条件付きでフックを動かすコード)があると、
- 実際にはフックが呼ばれない/設定が適用されない、という不整合が起きうる、という問題意識です。
具体的な変更
1) action_dispatch_request のロードフックからガードを削除(actionpack/lib/action_dispatch/railtie.rb)
ActionDispatch::Railtie内で行っていたActiveSupport.on_load(:action_dispatch_request)に対する「条件付き実行」をやめ、無条件にフックを設定するように変更しています。- これにより、「
ActionDispatch::Requestがアプリ起動中に既にロード済みかどうか」に依存せず、フックが確実に適用されます。
(実際の差分は2行追加・2行削除と小さいですが、本質的には「ガードとなる if 条件や early return を消して、ロードフックを常に登録する」方向の変更です。)
2) action_cable のロードフックからガードを削除(actioncable/lib/action_cable/engine.rb)
ActionCable::Engineで行っているActiveSupport.on_load(:action_cable)についても同様にガードを削除しています。- これにより、
ActionCableが「いつ」ロードされても、on_load(:action_cable)に登録された処理が確実に実行されます。
3) Action Cable サーバーとチャンネル読み込みコールバックの修正
- PR本文の「I also fixed a callback that loaded cable channels when loading the main action cable server.」から、
- Action Cable のメインサーバー(例:
ActionCable.serverを初期化/ロードするとき)に合わせて - ケーブルチャンネルをロードするコールバックを正しいタイミングに揃える修正が入っています。
- Action Cable のメインサーバー(例:
- 典型的には以下のような流れを想定しているコードが、ロードタイミングのズレで動かない可能性があり、それを是正した形です:ruby
# 例イメージ: Engine 内など ActiveSupport.on_load(:action_cable) do # config/cable.yml の読み込みや # app/channels 以下の自動読み込みなど end
イメージサンプル
以前は(イメージ):
# guard 付き(今回削除されたようなパターンの例)
if some_condition_about_boot_or_env
ActiveSupport.on_load(:action_cable) do
# 設定やチャンネルのロード
end
end今後は:
# 無条件にフックを登録
ActiveSupport.on_load(:action_cable) do
# 設定やチャンネルのロード
endこれにより、本番環境で ActionCable が「ブート中にすでに require 済み」のような場合でも、on_load が確実に発火するようになります(Rails の仕組み上、既にロード済みであれば on_load 登録時点で即時実行される)。
- 影響範囲・注意点
影響範囲
- 主に影響を受けるのは、以下のようなコード/挙動です:
ActiveSupport.on_load(:action_dispatch_request)に依存している拡張や RailtieActiveSupport.on_load(:action_cable)に依存している拡張、あるいは Action Cable の設定・チャンネル読み込み
- これまで「本番環境では意図したフックが発火していなかった」ようなケースで、挙動が安定して期待どおりになる可能性があります。
- とくに「アプリブート完了後にリクエストがロードされること」を暗黙前提にしているコードは、今回の変更によってタイミング依存の不具合が減ることが期待されます。
注意点
- もし独自の Railtie や Engine で、
on_load(:action_dispatch_request)/on_load(:action_cable)に独自処理をつないでおり、「環境によってはフックが呼ばれたり呼ばれなかったりしていた」のが、今回から安定して呼ばれるようになります。- その結果として、今まで「たまたま呼ばれていなかった処理」が呼ばれることで副作用が表面化する可能性はあります(たとえば、重複した設定や subscription など)。
- Action Cable 周りでチャンネルロードのタイミングに依存した初期化をしている場合は、動作確認しておくと安心です(特に本番環境で eager load が有効な場合)。
- 参考情報 (あれば)
- 元の議論となっている PR:
https://github.com/rails/rails/pull/56201
(今回のPRはこれの follow-up として、ロードタイミングの問題をより現実的な形に合わせて修正するもの) - 関連するキーワード:
ActiveSupport.on_loadActionDispatch::RailtieActionCable::Engine- eager load / production 環境での初期化順序
#56894 Action Text to_markdown generates markdown links for blob
マージ日: 2026/2/27 | 作成者: @flavorjones
- 概要 (1-2文で)
- Action Text の
to_markdownが Active Storage の Blob 添付を「実際の Markdown リンク(画像は、ファイルは[title](url))」として出力するようになり、これまでの単なる[caption]プレースホルダを置き換えました。 - URL 生成にはリクエスト単位のレンダリングコンテキストが使われ、マルチテナントなどで
host/protocol/script_nameが動的に変わるケースにも対応します。
- 変更内容の詳細
2-1. Blob 添付の Markdown 出力仕様
Action Text コンテンツから to_markdown を呼び出したときの挙動が整理・強化されています。
レンダリングコンテキストがある場合(通常のコントローラ・メイラー内など)
- 画像 Blob (
image/*) の場合markdown - 画像以外の Blob の場合markdown
[title](url)
ここでの title は Blob / 添付のタイトル(キャプション相当)、url は url_for(blob) で解決されるパスです。
既存の RemoteImage の  形式と統一されました。
レンダリングコンテキストがない場合(例: Rails コンソール)
- すべての Blob 添付で URL を持たないプレースホルダにフォールバック:markdown(
[title]urlは付かない。リンクテキストだけ)
これにより、最低限「何かが添付されている」ことは Markdown 上で表現しつつ、URL が安全に生成できるときのみリンクを張る方針になっています。
2-2. URL 生成方法とレンダラー
URL 生成は以下のように整理されています:
ActionText::Content.renderer(スレッドローカル)を経由して URL を生成- エンジン初期化時に
around_actionなどでリクエストごとにセットされる
- エンジン初期化時に
- 実際の URL 解決は
url_for(blob)に委譲- Active Storage のポリモーフィックルート解決を通る
config.active_storage.resolve_model_to_route(例::rails_storage_proxy)の設定を尊重
この結果、以下が「リクエストごと」に正しく反映されます:
hostprotocolscript_name(サブディレクトリ配信やマルチテナントで path prefix を変えるケース)
つまり、default_url_options には依存せず、その時点のリクエストコンテキストに基づいた URL を Markdown に埋め込みます。
2-3. 既存 API・シグネチャの互換性
以下のシグネチャや挙動には変更がありません:
Attachment#to_markdownContent#to_markdownActiveStorage::Blob#attachable_markdown_representationの「シグネチャ(引数)」自体
内部実装は変わりましたが、「attachable_markdown_representation を実装しているカスタム attachable」は基本的にそのまま動作します。
RemoteImage / MissingAttachable / ContentAttachment など、Action Text 標準の attachable 実装がリンク生成戦略の統一のために調整されています。
主な変更箇所(概念レベル):
actiontext/lib/action_text/attachment.rb- 添付ごとの
to_markdownロジックが整理され、Blob の場合に上記仕様で Markdown を生成
- 添付ごとの
actiontext/lib/action_text/content.rbContent#to_markdownが、renderer を通じた URL 生成を意識した形に
actiontext/app/models/action_text/rich_text.rbto_markdownの内部でのコンテキストの扱いを微調整
actiontext/lib/action_text/engine.rbActionText::Content.rendererのセットアップやaround_actionに関する初期化処理を追加
また、テスト (markdown_conversion_test.rb) が大幅に拡充され、以下のような観点がカバーされています:
- 画像 / 非画像 Blob の Markdown 出力差
- レンダラー有無でのリンク有無の違い
script_name・host 等を変えた場合に URL が正しく変わること- RemoteImage や MissingAttachable との一貫性
- 影響範囲・注意点
影響範囲
- Action Text + Active Storage を使い、
to_markdownでコンテンツを書き出している箇所(例: メール用 Markdown 生成、外部サービスへのエクスポートなど) - 特に Blob 添付が含まれるリッチテキストを Markdown に変換している場合
期待される挙動の変化
- 以前: Blob 添付は
[caption]のようなプレースホルダのみ(リンクなし) - 以後: コンテキストがあれば
もしくは[caption](url)となり、実際にクリック可能なリンクになる
注意点
- 本番環境で Markdown を外部に渡している場合、これまでリンクがなかった箇所に実 URL が含まれるようになります
- セキュリティポリシー/情報漏えいリスクの観点で、URL を出してよいかどうかを確認した方がよいケースがあります
- マルチテナントや、
script_nameをテナントごとに変えるような構成では「必ずリクエストコンテキストを伴ってto_markdownを呼ぶ」ようにしないと、期待通りの URL になりません- コンソールなどで
ActionText::Content#to_markdownを呼ぶとリンクなしプレースホルダになることに留意
- コンソールなどで
default_url_optionsに依存していた(と仮定していた)コードは、実際には「リクエストの host/protocol/script_name ベース」に変わっているため、挙動を確認しておく価値があります
カスタム attachable 実装について
- シグネチャ互換性は維持されているため、
attachable_markdown_representationを自前で定義している場合も基本的にそのまま動きます - ただし、今回の Blob / RemoteImage と同様に、「コンテキストがあれば URL 付き、なければプレースホルダ」にしたい場合は、renderer /
url_forの使い方を真似ることで一貫した挙動にできます
- 参考情報 (あれば)
- 関連 PR: #56858(
ActiveStorage::Blob#attachable_markdown_representationの最初の導入・調整に関するもの) - Active Storage のルーティング:
config.active_storage.resolve_model_to_route(例::rails_storage_proxy,:rails_storage_redirectなど)
→ この設定に応じてurl_for(blob)の生成先が変わり、その結果が Markdown に反映されます
- マルチテナント環境での
script_name運用:- Rack レベルで
SCRIPT_NAMEを変更している場合、今回の変更によりその prefix が Markdown の URL にも反映されるようになります
- Rack レベルで
#56898 Keep pinned connections in the pool after disconnect
マージ日: 2026/2/27 | 作成者: @byroot
- 概要 (1-2文で)
このPRは、Active Record のコネクションプールから「ピン留め(pinned)された接続」がdisconnect時に不適切に扱われてしまい、プールが不正な状態になる問題を修正しています。disconnect後でも、ピン留めされた接続がプール内の状態として矛盾しないように挙動を調整した、1 行だけの小さな修正です。
- 変更内容の詳細
※ PR 自体は connection_pool.rb の 1 行差分のみで、内容は「ピン留めされた接続を disconnect 後もプールの整合性が崩れないように扱う」ようにするものです。実際のコードはリポジトリを参照する必要がありますが、挙動としては次のような点が修正されています。
コネクションプールが
disconnect!/disconnect等を実行した際に、- 通常の接続は適切に切断・解放される
- しかし「特定スレッド/ファイバーにピン留めされた接続(
connection_pool.with_connection内でピン留めされたものや、Resolver 付き multi-DB での接続など)」については、- それを単純にプールから排除・破棄したり
- プール内部の管理構造(例:
@connections,@availableの数、貸し出しカウントなど)と矛盾する形で扱う
と、プール全体が「不正な状態(internal inconsistency)」になり得る状態でした。
本PRでは、「ピン留めされた接続を
disconnectの処理でもプール内のオブジェクトとして整合性がとれるように扱う」ように修正しています。
具体的には次のようなイメージです(あくまで挙動イメージのサンプルであり、実コードとは異なる擬似コードです):ruby# before(イメージ) def disconnect! @connections.each do |conn| conn.disconnect! end @connections.clear # ← pinned な connection がいても一律でクリアしてしまう など end # after(イメージ) def disconnect! @connections.each do |conn| conn.disconnect! end # pinned connection の存在や、pool の整合性を壊さないようにクリア/再構築方法を調整 reset_internal_state_preserving_pinned_connections end「プールから完全に除去する」のではなく、「切断はするが、プールの内部状態として整合性を保てるように扱う」方向の修正になっていると考えられます。
PR の説明文で
Otherwise we may end up in an invalid state.とある通り、この 1 行の違いが「内部状態が壊れた connection pool」に入り込むかどうかを左右しており、その意味でバグ修正としてクリティカルな変更です。
- 影響範囲・注意点
直接の影響を受けるのは ActiveRecord の ConnectionPool を利用している全アプリケーション ですが、問題が顕在化するのは主に:
- マルチデータベース構成(
connects_toなど)や - fiber/thread で接続をピン留めしているケース(
ActiveRecord::ConnectionAdapters::PoolConfigの特殊な使い方など) で、disconnect!/ 再接続処理が絡んだときです。
- マルチデータベース構成(
期待できる挙動の改善:
Rails.application.reloader.to_runやActiveRecord::Base.clear_all_connections!などで接続を切り替える場面- Puma 等のマルチスレッド環境での graceful restart 時の
disconnect - Sidekiq / ActiveJob ワーカーでの
with_connection後のクリーンナップ などで、まれに起きていた「pool が壊れて例外が出る」「コネクション数のカウントが合わない」「以後の接続取得が異常になる」類の問題が解消される可能性があります。
後方互換性:
- 表面的な API の変更はなく、内部の状態管理のバグ修正なので、基本的には後方互換的です。
- ただし、もし「壊れたプール状態」に依存してしまっていたような極端なワークアラウンドがあれば、その挙動は変わります(通常のアプリでは考慮不要)。
運用上の注意:
- Rails をこのバージョンに上げた際に、DB 接続周りのログやエラー(特に
ActiveRecord::ConnectionTimeoutErrorや pool がらみの例外)が以前と比べてどう変化するかは確認するとよいです。 - 本件はコネクションリークとも絡みやすい領域なので、「disconnect 周りの挙動が怪しかった環境」では、アップデート後に挙動が安定するかを観察すると効果が分かりやすいはずです。
- Rails をこのバージョンに上げた際に、DB 接続周りのログやエラー(特に
- 参考情報 (あれば)
- 関連 Issue:
- 関連 PR(この修正で合わせて解決されるもの):
これらでは、「disconnect 後に connection pool が不正な状態になる(pinned connection の扱いがおかしい)」といった報告・議論が行われており、本PRはその最終的な修正としてマージされています。
#56891 Fix collection caching to preserve store default expires_in
マージ日: 2026/2/27 | 作成者: @pietervisser
- 概要 (1-2文で)
ActionView::PartialRendererのコレクションキャッシュで、expires_inを常にwrite_multiに渡してしまい、キャッシュストア側のデフォルト有効期限を潰していたリグレッションが修正されました。expires_inが明示的に指定されたときだけwrite_multiに渡されるようになり、デフォルト設定が正しく尊重されます。
- 変更内容の詳細
問題の背景
- #51579 の変更により、部分テンプレートのコレクションキャッシュ(
render partial: ..., collection: ..., cached: ...)で内部的にwrite_multiを呼ぶ際、expires_inオプションを常に引数として渡すようになった。
- その結果、
- ビュー側で
cached: trueのようにexpires_inを指定していない場合でも、 expires_in: nilがwrite_multiに渡されるようになった。
- ビュー側で
- 多くのキャッシュストア(例: MemCacheStore, RedisCacheStore)は、
expires_in: nilが渡されると- 「ストアに設定されているデフォルトの
expires_inを使う」のではなく、 - 「有効期限を設定しない(実質無期限)」または「明示的にデフォルトを上書き」として扱う。
- 「ストアに設定されているデフォルトの
- そのため、「ストアに設定したデフォルト期限を活かしたい」というユースケースで、意図せず有効期限が変更されるリグレッションが発生していた (#56890)。
この PR の修正内容
ActionView::PartialRenderer::CollectionCaching内でwrite_multiを呼び出すときのオプション組み立て処理を修正:- これまでは、コレクションキャッシュ用のオプションに
expires_inキーがなくても、expires_in: nilが結果的にwrite_multiに渡されていた。 - 修正後は、コレクションキャッシュオプションに
:expires_inキーが存在するときだけwrite_multiのオプションにexpires_inを含めるようになった。cached: true(オプションなし) →write_multiにはexpires_in自体を渡さない → ストアのデフォルトが使われるcached: { expires_in: 5.minutes }→write_multi(expires_in: 5.minutes, ...)cached: { expires_in: nil }→ 明示的にexpires_in: nilを渡す(「無期限」など、ストアの仕様に依存)
- これまでは、コレクションキャッシュ用のオプションに
実際のコードイメージ(疑似コード):
# 修正前(イメージ)
options = collection_cache_options.dup
options[:expires_in] ||= nil
@cache_store.write_multi(entries, **options)
# 修正後(イメージ)
options = collection_cache_options.dup
unless collection_cache_options.key?(:expires_in)
options.delete(:expires_in) # またはそもそも追加しない
end
@cache_store.write_multi(entries, **options)テストの追加
actionview/test/template/render_test.rb にテストが追加され、以下を確認しています(PR文面から推測される観点):
cached: trueでレンダリングしたときにwrite_multiにexpires_inを渡さないこと。cached: { expires_in: 1.minute }のような指定がある場合にはexpires_inが渡されること。cached: { expires_in: nil }と明示すると、expires_in: nilが渡されること(「明示的な意図として尊重」される)。
- 影響範囲・注意点
影響範囲
- 対象:
- Rails の
ActionViewで、- コレクションレンダリング + キャッシュ を利用しているパス
render partial: "item", collection: @items, cached: truerender partial: "item", collection: @items, cached: { expires_in: ... }
- コレクションレンダリング + キャッシュ を利用しているパス
- Rails の
- 影響する挙動:
- キャッシュストア(MemCacheStore / RedisCacheStore など)の「デフォルト
expires_in設定」の扱い。
- キャッシュストア(MemCacheStore / RedisCacheStore など)の「デフォルト
変更後の挙動の整理
cached: true- Rails は
expires_inをwrite_multiに渡さない。 - → ストア側で設定しているデフォルト TTL がそのまま使われる。
- Rails は
cached: { expires_in: 10.minutes }write_multi(expires_in: 10.minutes, ...)が呼ばれる。- → 10分で期限切れになる。
cached: { expires_in: nil }write_multi(expires_in: nil, ...)が呼ばれる。- → 多くのストアでは「無期限」またはそれに準ずる扱いになり、ストアのデフォルト TTL を上書きする形になる(ここはストア実装依存)。
注意点 / マイグレーション観点
- この PR は「リグレッションの修正」であり、従来(#51579 以前)と同じ感覚で
expires_inが動くように戻しています。 - もし #51579 以降の挙動(
cached: trueで事実上無期限)に依存してしまっていた場合は、- その挙動は今後は維持されず、ストアのデフォルト TTL に戻る点に注意が必要です。
- その場合は、意図を明確にするために
- 無期限にしたい →
cached: { expires_in: nil } - 任意の期限にしたい →
cached: { expires_in: 1.hour }といった明示的指定に切り替えるべきです。
- 無期限にしたい →
- 参考情報 (あれば)
- 該当PR: https://github.com/rails/rails/pull/56891
- 回帰バグ Issue: https://github.com/rails/rails/issues/56890
- 回帰のきっかけとなったPR: https://github.com/rails/rails/pull/51579
- キャッシュストアの
expires_inの扱いはストアごとに仕様があるため、利用しているストア(Redis, Memcached など)のドキュメントも確認することを推奨します。
#56889 Fix ExecutionContext corruption during reload
マージ日: 2026/2/26 | 作成者: @alpaca-tc
- 概要 (1-2文で)
Rails のコードリロード時(特に test 環境でconfig.enable_reloading = trueかつ Spring 推奨設定を使うケース)に、ActiveSupport::ExecutionContextの内部状態が壊れてCurrentAttributesなどでNoMethodErrorが起きる不具合を修正する PR です。reload!時のコンテキストリセット方法を変更し、「スタックの深さは保ったまま中身だけを消す」挙動にすることで問題を解消しています。
- 変更内容の詳細
バグの内容
- 発生条件の典型例:
- test 環境
config.enable_reloading = trueconfig.active_support.executor_around_test_case = true(Spring でよく推奨される設定)
Rails.application.reloader.reload!を呼んだあとにCurrent.trace_id等を参照すると、ActiveSupport::CurrentAttributes内でNoMethodErrorが発生することがある:ActiveSupport::ExecutionContext::Recordは@storeが常にHashであることを前提としているが、リロード時の処理で@storeがnilになりうる- その結果
[]などの呼び出しでundefined method '[]' for nilが起こる
再現スクリプト(PR 説明より):
class TestApp < Rails::Application
config.load_defaults Rails::VERSION::STRING.to_f
config.enable_reloading = true
config.active_support.executor_around_test_case = true
end
Rails.application.initialize!
class Current < ActiveSupport::CurrentAttributes
attribute :trace_id
end
Rails.application.reloader.reload!
# ここで NoMethodError が発生していた
Current.trace_id根本原因: フック順序と clear の挙動の相性
リロード時に以下の順序でフックが実行されます(簡略化):
app.executor.to_run
→ActiveSupport::ExecutionContext.pushが呼ばれる(コンテキスト用の新しいフレームを積む)app.reloader.before_class_unload
→ ここで ExecutionContext を「リセット」していたapp.executor.to_complete
→ActiveSupport::ExecutionContext.popが呼ばれる(1 で積んだフレームを戻す想定)
もともと 2 の「リセット」で ExecutionContext.clear を使っていたため:
clearが「コンテキストの中身+スタック状態」そのものを全消去してしまう- その結果、3 の
popが「もう空になっているスタック」を pop しようとしてしまい、期待していた@storeの復元が起きずnilが残る(もしくは壊れた状態になる)
つまり「push → clear → pop」という順序になっていて、clear が「push した事実そのもの」を消すため、pop の整合性が崩れていた、という構造です。
具体的な修正内容
ActiveSupport::ExecutionContext.flushの新挙動追加- これまで存在していなかった(あるいは限定的だった)「スタックの深さは維持したままコンテキストの内容だけをクリアする」処理として
flushを実装。 - 挙動イメージ:
- すべてのフレーム(スタック)を残す
- 各フレーム内の
@storeなどの「実際のデータ」を空のHashなどにリセット
- こうすることで、「push した数」と「pop する数」は一致した状態を保ちながら、値だけはリロード時にきれいに消せる。
- これまで存在していなかった(あるいは限定的だった)「スタックの深さは維持したままコンテキストの内容だけをクリアする」処理として
Railtie のフックで
clear→flushに変更activesupport/lib/active_support/railtie.rbのbefore_class_unloadフック内の呼び出しを以下のように変更:- 旧:
ActiveSupport::ExecutionContext.clear - 新:
ActiveSupport::ExecutionContext.flush
- 旧:
- これにより、上記フック順序の 2 のステップでは「コンテキスト値のクリアのみ」を行うようになり、3 の
popでスタックを正しく戻せる。
回帰テストの追加
activesupport/test/execution_context_test.rbに回帰テストが追加されている:- 「
push -> flush -> popのシーケンスを実行しても ExecutionContext が有効なままか」を検証 - 具体的には、
flush実行後にpopしても@storeがnilにならず、以後の利用でエラーにならないことを確認するテスト
- 「
- 影響範囲・注意点
影響がある主なケース:
- test 環境で
config.enable_reloading = trueを有効にしているプロジェクト - 特に Spring 利用時に README に従い
config.enable_reloading = trueとしているアプリ config.active_support.executor_around_test_case = trueを使っているテスト設定ActiveSupport::CurrentAttributesやActiveSupport::ExecutionContextを利用したトレーシング・コンテキスト管理を行っている場合
- test 環境で
この PR により:
- リロードをまたいだあとのテスト実行で
Current.xxxアクセサ呼び出しがNoMethodError: undefined method '[]' for nilになる問題が解消される - ExecutionContext のリセットが「スタック構造を壊さない形」で行われるようになるため、他の executor 周りの処理とも整合しやすくなる
- リロードをまたいだあとのテスト実行で
互換性面:
clearをflushに置き換えているのはフレームワーク内部の利用箇所であり、アプリケーションコード側で直接ExecutionContext.clearを叩いているケースでなければ挙動の変化は基本的に透過的- リロード時に「スタックごと完全リセットされる」ことを前提にしたかなり特殊な利用をしていない限り、破壊的な影響はほぼないと考えられます
- 参考情報 (あれば)
- この不具合は、以前の PR (#55247) による挙動変更がきっかけで顕在化した回帰バグとされています。
- Spring README の該当箇所(
config.enable_reloading = true推奨設定):
https://github.com/rails/spring/blob/0242d4d5b5e6cef721f690acf3b13b389eb9276e/README.md#enable-reloading - 修正後も、
reload!によって「ExecutionContext の中身(CurrentAttributes など)はリセットされる」点は変わらないため、リロードをまたいで Current の値を保持し続ける設計は避けるのが安全です。
#56887 Add content_type parameter to http_authentication methods
マージ日: 2026/2/26 | 作成者: @ilianah
- 概要 (1-2文で)
HTTP Basic/Digest/Token 認証の 401 応答でContent-Typeをカスタマイズできるように、request_http_*_authenticationにcontent_typeオプションが追加されました。あわせて、これまで下位メソッドにはあったmessage引数がhttp_basic_authenticate_withからも指定できるように公開されています。
- 変更内容の詳細
2-1. 新しく追加された content_type パラメータ
以下のメソッドにオプション引数 content_type: が追加されています。
ActionController::HttpAuthentication::Basic.request_http_basic_authenticationActionController::HttpAuthentication::Digest.request_http_digest_authenticationActionController::HttpAuthentication::Token.request_http_token_authentication
これにより、401 を返す際のレスポンスの Content-Type を明示的に指定できます。
イメージされる実装イメージ
元々は、request メソッド内部で render や head を呼び出すときに content_type を固定、あるいは Accept などから決定していたものを、以下のように「もし content_type が指定されていればそれを優先的に使う」ようにしている形です(実際のコードは簡略化):
def request_http_basic_authentication(realm = "Application", message: nil, content_type: nil)
# ...
options = { status: :unauthorized }
options[:content_type] = content_type if content_type
if message
render(plain: message, **options)
else
head(:unauthorized, **options)
end
endDigest / Token 版も同様に、401 応答時に content_type を受け取ってレスポンスに反映するようになっています。
使用例 (コントローラから直接呼ぶ場合)
class ApiController < ApplicationController
include ActionController::HttpAuthentication::Basic::ControllerMethods
def index
authenticate
# ...
end
private
def authenticate
# JSON API で 401 も JSON を返したい場合など
unless authenticate_with_http_basic { |user, pass| valid?(user, pass) }
request_http_basic_authentication(
"My API",
message: { error: "Unauthorized" }.to_json,
content_type: "application/json"
)
end
end
endこのように、401 応答を JSON (application/json) など任意のコンテンツタイプで返せるようになります。
2-2. http_basic_authenticate_with で message を指定可能に
ActionController::HttpAuthentication::Basic::ControllerMethods#http_basic_authenticate_with は、よくある:
class AdminController < ApplicationController
http_basic_authenticate_with name: "admin", password: "secret"
endのような宣言的な記述に使われるヘルパーです。
これまでは下層の request_http_basic_authentication には message 引数があっても、この DSL からは指定できませんでしたが、今回の PR で message: オプションが公開されました。
使用例
class AdminController < ApplicationController
http_basic_authenticate_with(
name: "admin",
password: "secret",
message: "管理画面へのアクセスには認証が必要です",
content_type: "text/plain"
)
endこのように書くと、認証失敗時:
- レスポンスボディ:
"管理画面へのアクセスには認証が必要です" Content-Type:"text/plain"- ステータスコード:
401 Unauthorized
という応答を返せるようになります。
※ PR 説明では message だけに言及されていますが、実装としては http_basic_authenticate_with が内部で request_http_basic_authentication に message: を渡すように変更されています。content_type: もここまで引き上げられているかどうかは PR 本文からは明記されていませんが、少なくとも message: は確実に指定可能になっています。
2-3. テスト・ドキュメントの変更
actionpack/test/controller/http_*_authentication_test.rbにcontent_typeを指定した場合の挙動を検証するテストが追加・修正されています。actionpack/CHANGELOG.mdに、この変更が新機能として記載されています(Action Pack の動作仕様として正式にサポートされた形)。
影響範囲・注意点
デフォルトの挙動は変わらない
content_type/messageを明示的に指定しない場合は、従来どおりの 401 応答になります。- 既存コードは基本的にそのまま動きます。
API / JSON レスポンスとの相性が良くなる
- これまでは 401 だけ HTML やテキストで返ってしまうケースがありましたが、API コントローラなどで JSON 固定にしたい場合に便利です。
- API クライアント側の実装・期待に合わせて
Content-Typeを統一できます。
http_basic_authenticate_with利用時のメッセージ制御- 宣言的に Basic 認証を使っている箇所で、メッセージをカスタマイズできるようになったため、既存の DSL を崩さずに UX を改善できます。
- 多言語対応や詳細なエラーメッセージを返したい場合に有用です。
他の
content_type設定との兼ね合い- コントローラレベル・アプリケーションレベルでデフォルト
content_typeを設定している場合でも、request_http_*_authenticationにcontent_typeを指定すると、その 401 応答についてはここで指定した値が優先されます。 - すべての 401 を同じフォーマットにしたい場合は、認証フロー全体の統一を検討してください。
- コントローラレベル・アプリケーションレベルでデフォルト
- 参考情報 (あれば)
対象モジュール:
ActionController::HttpAuthentication::BasicActionController::HttpAuthentication::DigestActionController::HttpAuthentication::Token
典型的な利用パターン(整理):
- 「コントローラ内で自前で認証処理」する場合:
request_http_basic_authentication(message: ..., content_type: ...) - 「
http_basic_authenticate_withで簡易 Basic 認証」する場合:http_basic_authenticate_with name: ..., password: ..., message: ...(+ 将来的にcontent_type:も増える可能性あり)
- 「コントローラ内で自前で認証処理」する場合:
この PR により、Rails 標準の HTTP 認証を使いつつ、API 仕様や UI 仕様に合わせて 401 レスポンスを柔軟に設計しやすくなっています。
#56883 Revert "Load minitest server plugin to run minitest with --bisect option"
マージ日: 2026/2/26 | 作成者: @yahonda
- 概要 (1-2文で)
Rails 側でminitest --bisectを使うために入れていたワークアラウンドを削除し、純粋に minitest 本体の--bisectサポートに任せるように戻した PRです。これにより、Rails のテスト実行時に独自の minitest server plugin ロード処理は行われなくなります。
- 変更内容の詳細(あればサンプルコードも含めて)
- 対象ファイル:
activesupport/lib/active_support/testing/autorun.rb - 変更内容: 1行削除のみ
元 PR (#56647) では、minitest が公式に --bisect に対応していなかったため、「minitest server plugin」を読み込んで --bisect を動かすワークアラウンドが入っていました。
この PR (#56883) では、そのワークアラウンドを削除しています。
実際の変更イメージ(擬似コード):
# 変更前 (例: 実際のコードとは多少異なる可能性があります)
require "active_support/testing/autorun"
require "minitest/server" # ← この行を追加していたワークアラウンド
# ... 何らかの設定やフック ...# 変更後
require "active_support/testing/autorun"
# require "minitest/server" # ← この行が削除された理由:
- minitest 6.0.2 から、公式に
--bisectオプションがサポートされ、Rails 側の特殊対応が不要になったため。 - 作者は #56882 の最小再現プロジェクトを作成して、Rails + minitest 6.0.2 環境で
--bisectが問題なく動作することを確認済み。
- 影響範囲・注意点
影響範囲
- Rails のテストランナー(
rails testなど)で minitest を使っているプロジェクト全般。 - 特に
--bisectオプションを利用している/今後利用する Rails プロジェクト。
- Rails のテストランナー(
実質的な挙動
- これまで: Rails が minitest server plugin をロードすることで
--bisectを提供していた(ワークアラウンド)。 - これから: minitest 本体(6.0.2 以降)が
--bisectをサポートし、その機能をそのまま利用する。
- これまで: Rails が minitest server plugin をロードすることで
前提条件・注意点
- minitest のバージョン要件
--bisectを Rails で使いたい場合は、少なくとも minitest 6.0.2 以上 が必要になります。- もし古い minitest を使っていると、以前のような Rails 側ワークアラウンドがなくなるため、
--bisectが使えない・エラーになる可能性があります。
- カスタムな minitest server/plugin 利用
- 自前で
minitest/serverを使っている、あるいは独自に plugin をロードしている場合は、この PR が直接それを壊すことはありませんが、「Rails が自動で何かしてくれている前提」のコードは見直しが必要になるかもしれません。
- 自前で
- CI やスクリプト
- CI や開発用スクリプトで
rails test --bisectを使っている場合、Rails のバージョンアップと同時に minitest のバージョンも上げておくことを推奨します(bundle update minitestなど)。
- CI や開発用スクリプトで
- minitest のバージョン要件
- 参考情報 (あれば)
- この PR:
- Revert 元の PR (ワークアラウンドを入れた PR):
- 関連 Issue / 再現プロジェクトに関する PR:
- minitest 6.0.2 のリリースノート (
--bisect対応):
#56880 Avoid nesting Arel Or Nodes
マージ日: 2026/2/25 | 作成者: @skipkayhil
- 概要 (1-2文で)
このPRでは、ArelのOrノードを「2項(Binary)の入れ子」で使っていた箇所を、「多項(Nary)」としてフラットに扱うように修正し、Arelツリーのネストを浅くしてStackTooDeepエラーの発生可能性を下げています。内部実装の変更であり、アプリケーション側のAPIやクエリ結果は変えずに、パフォーマンスと安定性を向上させる目的の修正です。
- 変更内容の詳細
背景: Arel::Nodes::Or の Binary → Nary 化
もともと Arel::Nodes::Or は「左と右の2つのノード」を持つ Binary ノードとして実装されており、
(cond1 OR cond2 OR cond3)のようなクエリを組み立てる時には、内部的には以下のような入れ子構造になりがちでした:
# 概念図
Or(
Or(cond1, cond2),
cond3
)このように「OR の中に OR が入る」形でどんどん深くなっていくため、非常に多くの条件を OR で繋いだクエリでは Ruby のスタックが深くなりすぎ、SystemStackError (stack level too deep) の原因になります。
そこでArel側で Or を「Nary(N個の子ノードを持てる)」として扱えるようにしたものの、このPRで触っている2つのコードパスは、その新しい挙動を十分に活かせておらず、古いBinary的な組み立てをしていたため、無駄なネストが残っていました。
このPRは、その残りを片付ける変更です。
具体的な変更点
変更ファイルは2つのみで、行数も +4 / -4 と非常に小さいですが、中身は次の2点に要約できます。
(1) ActiveRecord::Relation::PredicateBuilder::ArrayHandler
activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
where(id: [1, 2, 3]) のようなケースや、IN句相当の条件を組み立てるときに使われるハンドラです。
以前は、複数の条件を OR で繋ぐ際に、2つずつ or していく形で Arel ツリーを構築していたため、条件数が増えると Or(Or(Or(...))) という風に深くネストしていました。
このPRでは、Or が Nary であることを前提に、
- すでに
Orノードであれば、その子ノードに新しい条件を追加する - 新たに OR 条件を作るときも、可能な限り「1つの Or ノードに複数条件を持たせる」
といった形にロジックを変更し、OR の入れ子を避けるようにしています。
概念的なイメージ:
修正前(Binary 的な組み方の例)
# OR を2つずつ組み立ててしまうイメージ
node = conds[0]
conds[1..].each do |c|
node = node.or(c) # Or(node, c) でネストが深くなる
end
# 結果: Or(Or(Or(cond1, cond2), cond3), cond4) ...修正後(Nary 的な組み方の例)
# 1つの Or ノードに配列として条件をぶら下げるイメージ
node = Arel::Nodes::Or.new([cond1, cond2, cond3, cond4])
# OR ノードの子配列が増えるだけで、入れ子にはならない(実際のコードはもう少し抽象化されていますが、狙いとしてはこのような変化です。)
(2) Arel::Predications
activerecord/lib/arel/predications.rb
Arelの eq, not_eq, in, matches など、典型的な述語(述語ビルダー)を提供するモジュールです。
ここでも複数条件を OR で繋ぐメソッドが、Or を Binary 的に組み立てていた箇所があり、それを Nary な前提で扱うように調整しています。
具体的には、
- 既存の
or呼び出しでOrノードをどんどんラップするのではなく、 - 既存の
Orノードに条件を追加していく、あるいは最初から Nary として構築する
という方向の小さなロジック修正になっています。
- 影響範囲・注意点
動作面の影響
- SQLとしての最終的なクエリ内容は変わりません(OR で繋ぐ条件は同じ)。
- 内部的なArelツリーの構造が「深い二分木」から「フラットな多分木」に近づきます。
- 多数の OR 条件を生成するようなクエリ(特に自動生成系)で、
- Ruby のスタック消費が減り、
SystemStackError (stack level too deep)/StackTooDeepなどのエラー発生確率が下がることが期待できます。
後方互換性
- 公開API(ActiveRecordのクエリインターフェイス)には変更はなく、アプリケーションコードの修正は不要です。
- Arelの内部ノード構造に直接依存しているようなメタプログラミング(例:
Arel::Nodes::Or#left/#rightに直接アクセスしているコード)は、本来非推奨ですが、そうしたコードがある場合は今後さらに壊れやすくなる可能性があります。- このPR自体はそのような外部コードに直接触れていませんが、「Or は必ず Binary である」という前提は今後ますます成り立たなくなります。
パフォーマンス・安定性
- スタックの消費を減らすという意味での安定性改善です。
- OR 条件の評価そのものの計算量は変わりませんが、Ruby側の再帰的処理やツリー走査でのオーバーヘッドが若干軽減される可能性があります。
- 参考情報 (あれば)
- 関連Issue: #56869
「OrをNaryにしたにもかかわらず、一部コードパスがBinary的な構築を続けている」ことによるスタック問題の報告と、それを解消するためのPRです。 - 変更ファイル:
activerecord/lib/active_record/relation/predicate_builder/array_handler.rbactiverecord/lib/arel/predications.rb
- 変更の本質は「
Arel::Nodes::Orを、実装・利用側ともに Nary ノードとして一貫して扱う」ことであり、今後もArel内部のOR関連ロジックは、この方向で整理されていくと考えられます。
#56873 Escape markdown metacharacters in text nodes and attachment captions
マージ日: 2026/2/25 | 作成者: @flavorjones
- 概要 (1-2文で)
Action Text の HTML→Markdown 変換時に、通常テキストや添付ファイルのキャプション等に含まれる Markdown メタ文字(*,#,[]など)が意図せず Markdown として解釈されないよう、バックスラッシュでエスケープする仕組みを導入した PR です。
同時に、添付ファイルが自前で生成する Markdown については新しいタグでラップして「非エスケープ領域」として扱い、元の Markdown を維持できるようにしています。
- 変更内容の詳細
2-1. テキストノード中の Markdown メタ文字をエスケープ
Rich Text (Action Text) の HTML を Markdown に変換する際、テキストノードに含まれる CommonMark のメタ文字が自動的にエスケープされるようになりました。
対象となるのは、以下のような記号を含むテキストノードです(代表例):
*/_(強調)#(見出し)[/](リンク)</>(HTML / 引用)- その他 CommonMark が特別扱いする記号
例: これまでは
<p>## Look at *this*</p>が Markdown としては
## Look at *this*となり、Markdown レンダラによっては「見出し+強調」として解釈されていました。
この PR 以降は変換時に
\## Look at \*this\*のようにバックスラッシュ付きで出力されるため、Markdown レンダラからは「ただのテキスト」として扱われます。
内部的には MarkdownConversion#escape_markdown_text が、テキストノード内のメタ文字を正規表現で検出して \ でエスケープするロジックを持ちます。
2-2. 添付ファイル由来の Markdown を保護するための <action-text-markdown> ラップ
一方で、Action Text には添付ファイル(ActiveStorage::Blob やカスタム attachable)のインスタンスが attachable_markdown_representation メソッドを通じて「意図的な Markdown」を生成するケースがあります。
例: 画像添付が
のような Markdown を返す場合。
今回、通常テキスト用のエスケープ処理を導入すると、こうした「わざと生成した Markdown」までエスケープされてしまい、リンクや画像として機能しなくなってしまいます。
そこで Content#to_markdown が、attachable から返された Markdown をそのまま使う代わりに、以下のようにカスタムタグでラップするようになりました:
<action-text-markdown></action-text-markdown>Markdown 変換時のツリーウォーカーは、この <action-text-markdown> 要素を特別扱いし、中身のテキストにはエスケープをかけず「生の Markdown」としてそのまま出力します。
これにより:
- 通常の本文テキスト: メタ文字をエスケープ → Markdown レンダラからは素の文字として扱われる
<action-text-markdown>内: エスケープしない → attachable 実装者が意図した Markdown としてレンダリングされる
という挙動の差別化が行われます。
2-3. 添付ファイルのキャプション・ファイル名もエスケープ対象に
ActiveStorage::Blob や RemoteImage などの添付ファイルが生成する Markdown 表現のうち、「キャプション」や「ファイル名」などユーザー入力由来になりうる部分についても、Markdown メタ文字がエスケープされるようになりました。
例えば、ファイル名が *important* doc [final].pdf のような場合、これがそのまま alt テキストやリンクテキストに使われると、* や [] の意味が衝突して Markdown レンダリングが壊れます。この PR ではそれらが
\*important\* doc \[final\].pdfのように変換され、Markdown として安全に扱われるようになります。
2-4. MarkdownConversion#escape_markdown_text を public に
アプリケーション側で独自の attachable クラスを実装し、attachable_markdown_representation を自前で組み立てている場合に備え、MarkdownConversion#escape_markdown_text が public メソッドとして公開されました。
これにより、アプリ開発者は例えば以下のようなコードを書けます:
class MyCustomAttachable
# Action Text のコンテキストを前提とした例
def attachable_markdown_representation(view_context)
escaped_title = view_context.action_text_markdown_conversion.escape_markdown_text(title)
""
end
end(実際の呼び出し方法はアプリ側の設計に依存しますが、「Action Text が使っているのと同じエスケープロジックを流用できる」ようになった、というのがポイントです。)
- 影響範囲・注意点
3-1. 見た目の変化: Markdown 経由の表示結果が「より素直なテキスト」になる
Action Text のコンテンツを Markdown に変換してから表示・配信している場合(例: メール、外部サービス連携、API レスポンスなど)、今後は:
#,*,[]などを含むテキストが- 以前: Markdown として解釈され、見出し・強調・リンクなどになりうる
- 今回以降:
\#,\*等に変換され、単なる文字として扱われる
という変化が起こります。
意図せず Markdown として解釈されていた部分が「ただのテキスト」になるので、多くのケースでは改善ですが、「それをあてにしていた」実装がある場合は表示スタイルが変わり得ます。
3-2. 既存の attachable 実装への影響
attachable_markdown_representationを実装しているクラスが、Markdown を「素の文字列」で返している場合:- 今回の
<action-text-markdown>ラップにより、その領域はエスケープされずに保たれます。 - ただし、その中でユーザー入力(キャプション・タイトル等)を直接埋め込んでいる場合、自前で
escape_markdown_textを呼ばないと、依然として Markdown メタ文字の影響を受ける可能性があります。
- 今回の
- 対策として:
- ユーザー入力系の文字列は
escape_markdown_text相当の処理でエスケープしてから Markdown に埋め込むことが推奨されます。
- ユーザー入力系の文字列は
3-3. エスケープ過剰の可能性
PR の説明にもある通り、「必要以上にエスケープしている」ケースがあることは認識された上での実装です。
- 現状の実装は安全側(壊れないことを優先)に倒しており、
- 将来的には正規表現の条件を洗練させて、「Markdown 上で本当に意味を持つ場面だけ」を選んでエスケープするように改善していく余地があります。
そのため、もし今後「このケースではエスケープされてしまうと困る」という具体的事例が出てきた場合は、Issue や PR を通じてフィードバックすると、正規表現の調整対象になり得ます。
- 参考情報 (あれば)
- 関連 PR: #56858
- 今回の PR は、その PR での残課題(通常テキストが Markdown として誤解釈される問題)のフォローアップ位置づけです。
- 主な変更ファイル:
actiontext/lib/action_text/markdown_conversion.rb- エスケープロジックの追加と public メソッド化
actiontext/lib/action_text/content.rbattachable_markdown_representationを<action-text-markdown>でラップする処理
actiontext/lib/action_text/attachment.rb- 添付ファイル markdown 表現の微調整
actiontext/test/unit/markdown_conversion_test.rb- テキストノード・添付ファイル・キャプション等に対する挙動をカバーするテストが大幅追加(+217/-36)
#56881 Fix using private lookup_cast_type in SQLite tests [8-0-stable]
マージ日: 2026/2/25 | 作成者: @skipkayhil
- 概要 (1-2文で)
Rails 8.0 系の SQLite アダプタのテストで、Column#initializeに存在しないcast_typeを前提としたプライベートメソッドを使っていた問題を解消し、より単純な形に書き直した PR です。8.1 以降で導入された API に依存していたテストコードを、8.0 の仕様に合わせて修正しています。
- 変更内容の詳細
背景
- Rails 本体のコミット: https://github.com/rails/rails/commit/a34dd91b7fc05e4b9910801b6997535d40a94196
- Rails 8.1 で
ActiveRecord::ConnectionAdapters::Column#initializeにcast_type引数が追加された。 - しかし、8.0 系ブランチ (
8-0-stable) ではcast_typeはまだ存在しないため、その前提で書かれたテストコードは不適切(もしくは将来のマージ時に問題になる)だった。
PR 説明文の要点:
cast_type wasn't added to Column's initialize until Rails 8.1, so we can just remove these private methods.
(cast_typeは Rails 8.1 になるまでColumn#initializeに追加されていないので、これらのプライベートメソッドは削除してしまってよい)
実際の変更 (テストコード内)
対象ファイル:
activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
行数の変化:
- 追加: 4 行
- 削除: 6 行
変更内容のパターンとしては次のようなものです(あくまでイメージです):
変更前(イメージ)
class SQLite3AdapterTest < ActiveRecord::TestCase
private
def lookup_cast_type(sql_type)
@connection.lookup_cast_type(sql_type)
end
def new_column(name, default, sql_type, null = true)
cast_type = lookup_cast_type(sql_type)
ActiveRecord::ConnectionAdapters::SQLite3Adapter::Column.new(
name,
default,
cast_type,
sql_type,
null
)
end
endここでは:
lookup_cast_typeというプライベートメソッドでcast_typeを取得new_columnでcast_typeをColumn.newに渡す
といった形で、「cast_typeを受け取るコンストラクタ」を前提にしている。
変更後(イメージ)
class SQLite3AdapterTest < ActiveRecord::TestCase
# 単純に Column を既存の引数形式で生成するか、
# あるいはアダプタが提供するファクトリを使うような形に修正
def new_column(name, default, sql_type, null = true)
ActiveRecord::ConnectionAdapters::SQLite3Adapter::Column.new(
name,
default,
sql_type,
null
)
end
endあるいは、テストごとに直接 Column.new を呼んでおり、lookup_cast_type と cast_type 関連のラッパーメソッド自体を削除しただけ、という可能性もあります。
いずれにせよ:
lookup_cast_typeとそれを利用してcast_typeを組み立てるプライベートメソッドを削除- 8.0 の
Column#initializeが想定するシグネチャに合わせて、テスト側を単純化
というのがこの PR の本質です。
- 影響範囲・注意点
- 対象は ActiveRecord の SQLite3 アダプタのテストコードのみ であり、本番コード(アダプタ本体の実装)には影響しません。
- 8.0 系で
Column#initializeの引数にcast_typeがないことを前提とするよう、テストの前提条件を整理しただけなので、ユーザーアプリケーション側の挙動は変わりません。 - Rails を 8.0 から 8.1 に上げる際に:
- 8.1 で
cast_typeが導入されていること - それに合わせてテストヘルパーなどの実装が変わる可能性
を意識しておくと、バックポートや cherry-pick 時にコンフリクトを減らせます。
- 8.1 で
- 独自に ActiveRecord の内部 API(特に
Column#initialize)を直接呼び出しているライブラリやアプリがある場合は、8.0 / 8.1 でシグネチャが異なることに注意が必要ですが、この PR 自体はその仕様差を「テスト側で吸収した」だけです。
- 参考情報 (あれば)
- 該当コミット:
- a34dd91b7fc05e4b9910801b6997535d40a94196
「Column#initializeにcast_typeが追加された」変更の文脈となるコミット。
- a34dd91b7fc05e4b9910801b6997535d40a94196
- ActiveRecord 内部 API の変更に関する一般的な注意点:
ActiveRecord::ConnectionAdapters::Columnは内部実装寄りのクラスであり、マイナーバージョン間でもシグネチャが変わることがあります。- アダプタやプラグインを実装する場合は、対象 Rails バージョンごとに
Column#initializeの引数を確認しておくと安全です。
#56854 Render MissingAttachable as "☒" in plain text
マージ日: 2026/2/25 | 作成者: @flavorjones
- 概要 (1-2文で)
Action Text で「存在しない/削除された添付ファイル (MissingAttachable)」をプレーンテキスト出力する際、これまで空文字列になっていたのを、HTML と同様に「☒」(U+2612) を出力するようにした修正です。これにより、添付が欠損していることがプレーンテキストでも明示的に分かるようになります。
- 変更内容の詳細
背景
- HTML レンダリング時:
<action-text-attachment>がMissingAttachableに解決される場合、既にパーシャルで「☒」を表示していた。
- プレーンテキスト (
Content#to_plain_text) レンダリング時:MissingAttachableにattachable_plain_text_representationが実装されていなかったため、該当部分が空文字列になり、ユーザーからは「何かが消えた」こと自体が分からなかった。
実際の変更
MissingAttachable クラスに attachable_plain_text_representation を追加し、プレーンテキストにおいても「☒」を返すようにしています。
概念的には以下のような変更です(実ファイルは actiontext/lib/action_text/attachables/missing_attachable.rb):
module ActionText
module Attachables
class MissingAttachable
# 既存の実装に加えて:
def attachable_plain_text_representation(caption: nil)
"☒"
end
end
end
endテスト (actiontext/test/unit/content_test.rb) では、例えば以下のようなケースが追加されていると考えられます:
test "missing attachable renders as ballot box with X in plain text" do
content = ActionText::Content.new('<action-text-attachment sgid="missing" />')
assert_equal "☒", content.to_plain_text
endまた、actiontext/CHANGELOG.md にもこの挙動変更が追記されています。
RemoteImage との違い
説明文にもある通り、この修正は「MissingAttachable」のみが対象です。
<action-text-attachment>ノードから添付を解決した結果:- MissingAttachable:
- バッキングレコードがなく、かつ
url/content-type属性などでRemoteImageとしても扱えない場合。 - プレーンテキストでは今回の変更で「☒」を出力。
- バッキングレコードがなく、かつ
- RemoteImage:
- レコードは削除されていても、ノードに
url/content-typeが残っている場合。 - プレーンテキストでは従来通り
[Image]として出力。
- レコードは削除されていても、ノードに
- MissingAttachable:
- 影響範囲・注意点
影響範囲:
ActionText::Content#to_plain_textを使っている箇所すべてで、MissingAttachable が含まれる場合の出力が「空文字列 → ☒」に変わります。- メール本文や検索インデックス、ログ出力など、
to_plain_textベースでテキスト化している処理に影響します。
互換性・注意点:
- これまで「空白として消えていた箇所」に「☒」が挿入されるため、以下のような影響がありえます:
- 文字数カウント、バイト長に依存するロジックがある場合は結果が変わる。
- フルテキスト検索のインデックスに「☒」が含まれるようになる(通常は問題ないが、厳密なテキスト比較をしているテスト等があれば要確認)。
- 「欠損した添付を一切表示したくない」という仕様で、空文字を前提にしていた場合は、アプリ側で
to_plain_text後に「☒」を取り除くなどの対応が必要です。
- これまで「空白として消えていた箇所」に「☒」が挿入されるため、以下のような影響がありえます:
- 参考情報 (あれば)
- 対象クラス:
ActionText::Attachables::MissingAttachable - 影響メソッド:
ActionText::Content#to_plain_text - 使用文字: U+2612 BALLOT BOX WITH X (
☒)- HTML 表示とプレーンテキスト表示で表現が統一されます。
#56875 Cleanup RAILS_HOST_APP_PATH related tests
マージ日: 2026/2/25 | 作成者: @byroot
- 概要 (1-2文で)
RAILS_HOST_APP_PATHを直接ENVから読むのをやめ、定数に切り出したうえでテストではその定数をスタブする形に整理した PR です。これによりテストがよりシンプルかつ安定し、環境変数依存のテスト構造が改善されています。
- 変更内容の詳細
2-1. 本体コード側の変更(debug_view.rb)
actionpack/lib/action_dispatch/middleware/debug_view.rb で、ENV["RAILS_HOST_APP_PATH"] を直接参照していた処理が、クラス内の定数経由で参照するように変更されています。
イメージとしては次のような変更です(擬似コード):
module ActionDispatch
class DebugView
- def host_app_path
- ENV["RAILS_HOST_APP_PATH"]
- end
+ RAILS_HOST_APP_PATH = ENV["RAILS_HOST_APP_PATH"]
+
+ def host_app_path
+ RAILS_HOST_APP_PATH
+ end
end
end実際のコードでは、DebugView かその周辺で RAILS_HOST_APP_PATH を定数として定義し、それを利用するようになっています(行数の変化から、元々4行あったコードが3行に整理されている程度の軽微な修正)。
要点:
- アプリケーションのホストパスについて、グローバルな環境変数読み出しではなく、クラス定数を経由して参照する形に整理。
- 本体のロジック自体(どのパスを使うか)は変わらず、参照の仕方だけが変わっています。
2-2. テスト側の変更(debug_exceptions_test.rb)
actionpack/test/dispatch/debug_exceptions_test.rb が大きく書き換えられており、以下のようなリファクタが行われています。
ENV 直接操作から定数スタブへ
- 以前はテスト内で
ENV["RAILS_HOST_APP_PATH"] = "/some/path"のように環境変数を直接セット/リセットして、エラー画面の動作を確認していたと考えられます。 - この PR では、
ActionDispatch::DebugView::RAILS_HOST_APP_PATH(もしくは同等の定数)をstub_constなどで差し替える形に変更されています。
擬似的な例:
ruby- 以前はテスト内で
- begin
- old = ENV["RAILS_HOST_APP_PATH"]
- ENV["RAILS_HOST_APP_PATH"] = "/foo/bar"
テスト実行
- ensure
- ENV["RAILS_HOST_APP_PATH"] = old
- end
- stub_const("ActionDispatch::DebugView::RAILS_HOST_APP_PATH", "/foo/bar") do
テスト実行
- end
これにより: - テスト実行中にグローバルな `ENV` を汚さない - テストが他のテストケースに影響しにくくなる - テストコードが簡潔になる
- テストコードの整理・簡略化
- 行数が「+37/-63」となっており、テスト全体がかなりスリム化されています。
- 重複していた setup/teardown(ENV の保存・復元など)が不要になり、テストごとに必要なコンテキストを定数スタブで局所的に定義する形に整理されている可能性が高いです。
- RAILS_HOST_APP_PATH が未設定のケース・設定されているケースの両方をカバーしつつ、テストが読みやすくなるように書き換えられていると考えられます。
影響範囲・注意点
実行時挙動の変更はほぼない
- 本体コードは「ENV から読み込む」こと自体は維持しており、それを定数に束ねただけなので、アプリケーションの挙動として大きな変化はありません。
- 既存の
RAILS_HOST_APP_PATHの利用者(エラーページ/デバッグビュー周り)は、引き続き同じ値を参照します。
RAILS_HOST_APP_PATHをテストで操作している場合の注意- Rails 本体のテストは、ENV を直接触るのではなく「定数をスタブする」ことを推奨する方向に寄っています。
- 自前のテストコードで
ENV["RAILS_HOST_APP_PATH"]を直接いじっていると、本体の実装との一貫性がとれなくなり、今後の変更に追随しづらくなる可能性があります。 - 外部からこの定数を期待して参照している場合(たとえば
ActionDispatch::DebugView::RAILS_HOST_APP_PATHに依存している場合)、定数の初期値はプロセス起動時のENVに依存する ことに注意してください。プロセス起動後にENVを変更しても、定数は自動的には更新されません。
テストの安定性向上
- ENV をグローバルに変更しないため、並列テストや複数テスト間の相互干渉のリスクが下がります。
- CI やマルチプロセス/マルチスレッド実行時のテストの安定性が向上します。
- 参考情報 (あれば)
- Rails のテストでは、グローバル状態(
ENV, クラス変数、グローバル変数など)をいじるより、定数スタブや依存性注入で切り替えるスタイルが推奨されつつあります。 - 同様のパターン(「ENV → 定数 → テストで定数スタブ」)は他のミドルウェアや Railtie でも採用されており、本 PR はその一環のクリーンアップと見なせます。
- 実際のコードを参照したい場合は、GitHub 上の PR #56875(
actionpack/lib/action_dispatch/middleware/debug_view.rbおよびactionpack/test/dispatch/debug_exceptions_test.rbの差分)を見ると、どのようにRAILS_HOST_APP_PATHが定数として扱われているかが確認できます。
#56867 Fix Ruby 4.0 delegator warning for inspect on DelegateClass subclasses
マージ日: 2026/2/25 | 作成者: @hammadxcm
- 概要 (1-2文で)
Ruby 4.0 で追加されたKernel#instance_variables_to_inspectにより、DelegateClassを継承した Rails のクラスでinspectを委譲している箇所から警告が出る問題を解消する PR です。inspectを Kernel に丸投げしている 2 クラスに対して、プライベートメソッドinstance_variables_to_inspectを定義し、デリゲータ自身のインスタンス変数を返すようにしています。
- 変更内容の詳細
問題の背景
Ruby 4.0 では Kernel#inspect の実装が変わり、内部で次のような流れになります:
def inspect
if respond_to?(:instance_variables_to_inspect, true)
ivars = instance_variables_to_inspect
else
ivars = instance_variables
end
# ivars を使った inspect 処理 ...
endここで respond_to?(:instance_variables_to_inspect, true) の true は「プライベートメソッドも含めてチェックする」という意味です。
Rails 側では、以下の 2 クラスが DelegateClass のサブクラスで、かつ inspect を Kernel#inspect に差し替えています。
ActiveModel::Attributes::Normalization::NormalizedValueTypeActiveRecord::Type::Serialized
これらのクラスはおおよそ次のようなことをしています:
# 疑似コードイメージ
class NormalizedValueType < DelegateClass(SomeClass)
define_method(:inspect, Kernel.instance_method(:inspect))
endKernel#inspect が実行されると respond_to?(:instance_variables_to_inspect, true) が呼ばれますが、このクラス自体は instance_variables_to_inspect を定義していないため、respond_to_missing? に処理が落ちます。そのとき DelegateClass 側の Delegator#respond_to_missing? が
- 「委譲先にあるプライベートメソッドは転送しない」
- なので「private メソッドをフォワードしない」という警告を出す
という挙動になり、以下のような warning が出ます:
warning: delegator does not forward private method #instance_variables_to_inspect解決策
2 クラスに対し、プライベートメソッド instance_variables_to_inspect を新設し、それを Kernel#instance_variables にバインドしています。
概念的にはこんな変更です:
class NormalizedValueType < DelegateClass(SomeClass)
define_method(:inspect, Kernel.instance_method(:inspect))
private
# Ruby 4.0 の Kernel#inspect が呼ぶ
define_method(:instance_variables_to_inspect, Kernel.instance_method(:instance_variables))
end
class ActiveRecord::Type::Serialized < DelegateClass(SomeClass)
define_method(:inspect, Kernel.instance_method(:inspect))
private
define_method(:instance_variables_to_inspect, Kernel.instance_method(:instance_variables))
end※ 実際のコードもほぼこのイメージです(define_method/instance_method のバインドを利用)。
ポイント:
instance_variables_to_inspectは 委譲先ではなく「デリゲータ自身」のinstance_variablesを返します。- 元々
inspectをKernel.instance_method(:inspect)にしている意図が「ラップしているオブジェクトではなく、デリゲータ自身をinspectしたい」ためであり、それに整合する実装です。
- 元々
- これにより
respond_to?(:instance_variables_to_inspect, true)がtrueを返し、Delegator#respond_to_missing?まで降りずに完結するため、警告が出なくなります。
前後の呼び出しチェーンの違い
修正前:
- delegator 上で
Kernel#inspect実行 respond_to?(:instance_variables_to_inspect, true)呼び出し- ActiveSupport の
respond_to_missing?で委譲(ただしinclude_privateは無視される) super→ Ruby 標準ライブラリのDelegator#respond_to_missing?- 委譲先には private な
instance_variables_to_inspectがある → フォワードしない旨の warning 発生
修正後:
- delegator 上で
Kernel#inspect実行 - delegator 自身が
instance_variables_to_inspect(private) を持っているので、respond_to?が即true - そのまま delegator 自身の
instance_variablesを返す - 委譲チェーンに入らないため warning なし
- 影響範囲・注意点
- 影響するクラス:
ActiveModel::Attributes::Normalization::NormalizedValueTypeActiveRecord::Type::Serialized
- 影響する Ruby バージョン:
- Ruby 4.0 以降でのみ意味を持つ変更
- Ruby 3.x 以前では
Kernel#inspectがinstance_variables_to_inspectを使わないため、追加されたメソッドは呼ばれず事実上の no-op。
- Ruby 3.x 以前では
- Ruby 4.0 以降でのみ意味を持つ変更
実務的な意味合い:
- Ruby 4.0 に上げた際に、これらのクラスの
inspect呼び出しで冗長な warning が出ていたケースが静かになります。 inspectの結果として出る情報は「ラップ先ではなく、デリゲータ自身のインスタンス変数」が対象になる点に注意してください。- これは既存の
inspectの意図(delegator 自身を見せる)に揃えているため、仕様としては自然ですが、 - 「ラップしている値の中身を
inspectから直接見たい」という期待がある場合は、元々そういう実装ではない & 今回も変わっていないことを理解しておく必要があります。
- これは既存の
テスト状況:
- ActiveModel の正規化関連テスト (10 回) および ActiveRecord のシリアライズ型関連テスト (142 回) がすべてパスしており、挙動としての後方互換性は担保されています。
- 参考情報 (あれば)
- 元 issue: https://github.com/rails/rails/issues/56756
- Ruby の
delegate実装 (Delegator#respond_to_missing?):
https://github.com/ruby/delegate/blob/f10dbc0f92c73fbc30bffaa93d4274b3e39497d2/lib/delegate.rb#L105 - 変更されたファイル:
activemodel/lib/active_model/attributes/normalization.rbactiverecord/lib/active_record/type/serialized.rb
#56871 Fix Action Text to_markdown edge cases for code blocks, links, and URI safety
マージ日: 2026/2/24 | 作成者: @flavorjones
- 概要 (1-2文で)
Action Text のto_markdownが、コードブロック・リンク・URI 安全性まわりの細かい不具合や抜けをいくつか抱えていたのをまとめて修正し、Markdown としてより正しく、安全な出力になるようにした PR です。特に<pre><code>などのコードの整形・リンク文字列/URL のエスケープ・javascript:など危険な URI のブロックが堅牢化されています。
- 変更内容の詳細
2-1. コードブロックの扱い改善
(1) <pre> 内の空白を壊さないように修正
従来は <pre> ブロック内テキストを Markdown 化する際に String#strip のような処理で前後の空白を削っていたため、
- インデントレベル
- 末尾の空行
が失われるケースがありました。
この PR では、前後にだけ不要な空行を取り除きつつ「内部の空白・インデント」は保存できるよう、delete_prefix / delete_suffix を使うように変更しています。
狙いとしては:
- HTML の
<pre>が表現していたレイアウトを、Markdown コードフェンスでも忠実に再現する - 先頭/末尾の明らかに余計な改行だけ削る
というバランス調整です。
(2) コードフェンスの長さを動的に伸ばす
Markdown のコードブロックは通常以下のように書きます。
```ruby
def hello
puts "hi"
end
しかし、コードの中に ```(バッククォート3つ)がそのまま含まれていると、Markdown の構文と衝突します。
この PR では、コードブロック内にバッククォートが含まれる場合、
- コンテンツ内の「連続したバッククォートの最長長さ」を調べ
- それより 1 つ多い長さのバッククォート列をフェンスに使う
というロジックに変更しています。
例: コンテンツ中に `````(5つ)の連続バッククォートがあれば、フェンスは ``````(6つ)にする、といった形です。
#### (3) インライン `<code>` にバッククォートが含まれる場合の処理
インラインコードも、通常は `` `foo` `` のようにバッククォート 1 つで囲みますが、中のテキストにバッククォートがあると壊れます。
この PR では:
- コンテンツ内の連続バッククォート数を見て、それより多い長さのバッククォートでインラインコードを囲む
- さらに、コンテンツの先頭・末尾がバッククォートの場合は、` `` code` `` のように、内側にスペースを挿入して Markdown として曖昧にならないようにする
という対応になっています。
簡略例(イメージ):
```markdown
# 変更後の出力イメージ
``code with `backtick` inside``のように、必要に応じてバッククォートを 2 個以上で囲むかたちになります。
2-2. リンクの処理とエスケープ強化
(1) href 内の文字を Markdown 向けにパーセントエンコード
Markdown のリンク構文 [text](url) では、URL 内にそのまま含めると問題を起こす文字がいくつかあります。
この PR では、リンクの href について:
(,)- 半角スペース
<,>- 改行 (
\n) - タブ (
\t) - キャリッジリターン (
\r)
などを URL エンコード(% エンコード)するようにしました。
これにより:
- Markdown パーサにとって有効な URL として扱われる
- 不正な改行挿入などによる構文崩れ・意図しないリンク終端の発生を防ぐ
といった効果があります。
(2) リンクテキスト中の [ / ] をエスケープ
リンクテキストに [ や ] が含まれていると、Markdown のリンク構文としてネストして解釈されてしまい、いわゆる「Markdown link injection」のような状態を引き起こし得ます。
この PR では:
[→\[]→\]
とリンクテキスト内ではエスケープするように変更し、テキストとしての角括弧とMarkdown 構文としての角括弧を区別できるようにしています。
(3) RemoteImage attachment のリンクも同様にエスケープ
Action Text では、RemoteImage 添付(外部画像)のリンクを Markdown に落とすケースがありますが、ここでも同じエスケープロジックを使うように修正されています。
- これまで RemoteImage の URL には十分なエスケープがかかっていなかった可能性
- 今回の修正により、通常のリンクと同じ安全性・正当性を持つ Markdown URL になる
という状態になります。
2-3. URI 安全性 (javascript: など) の強化
(1) allowed_uri? によるプロトコルチェックへの委譲
これまで Action Text 側で行っていた「危険な URI スキーム(javascript: など)のブロック」を、Rails::HTML::Sanitizer.allowed_uri? に委譲するように変更しています。
allowed_uri? は以下を含む多くの面倒をみてくれます:
- 制御文字の除去
- HTML エンティティ (
javascript:のような) のデコード - 大文字小文字の正規化 (
JaVaScRiPt:→javascript:) data:URI の mediatype バリデーション- 先頭に挿入された空白・タブ・改行などを削った上でのプロトコル判定
つまり、従来であれば:
javascript:を直接弾いていても
javascript:やjavascript:や\tjavascript:のような形でバイパスされる可能性があった
ところを、このメソッドに統一することでかなり堅牢になります。
(2) rails-html-sanitizer のバージョンアップ
上記 allowed_uri? の公開 API を使うために、依存している rails-html-sanitizer を ~> 1.7 にバンプしています。
actionpack.gemspecactionview.gemspecGemfile.lock
へも反映されており、Rails 全体として新しい sanitizer に依存する形になります。
2-4. テストの追加・強化
actiontext/test/unit/markdown_conversion_test.rb に大量のテスト(+142行)が追加されており、
- コードブロック内のインデント・空行が保持されること
- コンテンツ内バッククォート数に応じてフェンス長が増えること
- インラインコードのパディング/フェンス長の調整
- リンクテキスト中の
[]エスケープ - href 内の危険文字のパーセントエンコード
javascript:/ 改行混入 / エンティティ化 /data:などの URI 検証
といった多くのケースがカバーされています。
Action Text の Markdown 変換ロジック周りの回帰を防ぐためのテストが整備されたと言えます。
影響範囲・注意点
Action Text を Markdown 変換と組み合わせて使っているプロジェクト
to_markdownの出力が、- コードブロック内の空白/空行
- コードフェンスの長さ
- リンクテキストの
[/] - URL 内のスペース/カッコ/改行等
について従来とわずかに変わる可能性があります。
- 表示結果としては主に「より安定する(崩れにくくなる)」方向ですが、Markdown 文字列をさらに自前でパースしたり、文字列比較テストをしている場合は差分が出る可能性があります。
URI の安全性チェックロジックを前提にしている場合
javascript:や変形した危険な URI の扱いが、rails-html-sanitizerの挙動に依存するようになります。- これにより、以前は許可されていた一部の「ギリギリ合法扱いしていた URI」がブロックされる可能性があります(またはその逆は低いが、挙動が変わる余地はあります)。
- 信頼できない入力から生成した Trix / Action Text のコンテンツを Markdown にする場合の安全性は、全体として向上しています。
依存 gem のバージョンアップに伴う影響
rails-html-sanitizer ~> 1.7が新たな制約となるため、- 既存プロジェクトで
rails-html-sanitizerを古いバージョンに固定している場合は依存解決エラーになり得ます。
- 既存プロジェクトで
- 自前で sanitizer の挙動に依存したコード(Monkey patch 等)を書いている場合は、
1.7への対応状況を確認する必要があります。
RemoteImage を利用している場合
- リンク URL のエンコードが強化されることで、
- 一部の「元の素の URL 表記」とは見た目が変わる(% エンコードが増える)
- しかしブラウザとしては正しく解釈される
という変化が起こります。Markdown の生テキストの見た目を重視している場合は差分を認識しておくとよいです。
- リンク URL のエンコードが強化されることで、
- 参考情報 (あれば)
- 対象 PR: https://github.com/rails/rails/pull/56871
- 関連 PR: https://github.com/rails/rails/pull/56858 (今回の「エッジケース」修正の元になった変更)
Rails::HTML::Sanitizer(rails-html-sanitizer)のallowed_uri?公開 API:- gem: https://github.com/rails/rails-html-sanitizer
~> 1.7から安全な URI 判定を提供
この PR によって、Action Text → Markdown 変換を安全に外部公開するユースケース(ブログエクスポート、Git リポジトリへの変換など)が現実的かつ安全になっており、特にユーザー生成コンテンツを扱うアプリでは恩恵が大きい修正です。
#56749 Move protected_environments to ActiveRecord module
マージ日: 2026/2/24 | 作成者: @skipkayhil
- 概要 (1-2文で)
protected_environmentsの設定場所をActiveRecord::BaseからトップレベルのActiveRecordモジュールに移し、「実質グローバルな設定」であることを実装に反映した変更です。従来のclass_attribute風な API は非推奨になりつつも、後方互換性のために引き続き動作します。
- 変更内容の詳細
背景
- 元々
protected_environmentsはclass_attributeとしてActiveRecord::Baseに定義されていました。 - その後「
class_attributeは使わないが、同様の挙動をする」形に実装が変更されました。 - しかし実際の利用箇所(特に DB タスク)は常に
ActiveRecord::Baseを参照しており、class_attributeによる継承・サブクラスごとの設定といったメリットは使われていませんでした。 - 実態としては「ActiveRecord 全体で共有されるグローバルな設定」になっていたため、その意図を明示する形に変更されています。
主な変更点
1) protected_environments を ActiveRecord モジュール直下に移動
activerecord/lib/active_record.rb に、グローバルな設定としての protected_environments が追加されました。
イメージ的には以下のような形です(簡略化した擬似コード):
module ActiveRecord
class << self
# グローバル設定としての protected_environments
attr_accessor :protected_environments
end
# デフォルト値の設定
self.protected_environments = %w[production]
endこれにより、「保護された環境」の判定はActiveRecord.protected_environments
というモジュールレベルの設定に一元化されます。
2) ActiveRecord::Base 側のメソッドは非推奨化
activerecord/lib/active_record/model_schema.rb あたりで定義されていたActiveRecord::Base.protected_environments などの「クラスメソッド」は、
- 内部的には
ActiveRecord.protected_environmentsを参照する - ただし、このクラスメソッド API 自体は deprecated として扱う
という形に変更されています。
擬似コードイメージ:
module ActiveRecord
class Base
class << self
# 非推奨: 互換のため残すが、実体は ActiveRecord の設定を見に行く
def protected_environments
ActiveRecord.protected_environments
end
def protected_environments=(value)
ActiveRecord.protected_environments = value
end
end
end
endそのため、既存コードで:
ActiveRecord::Base.protected_environments = %w[production staging]と書いていた場合も動作は変わりませんが、内部的には ActiveRecord.protected_environments を更新している扱いになります。
3) マイグレーション・DBタスク側での参照先の整理
activerecord/lib/active_record/migration.rb- マイグレーション関連のコードで
protected_environmentsを参照する箇所が、ActiveRecord::Base.protected_environmentsからActiveRecord.protected_environmentsへと整理されました。
- マイグレーション関連のコードで
activerecord/test/cases/tasks/database_tasks_test.rb- DB タスク (
db:migrate,db:drop, など) が保護された環境かどうかを判定するテストも、
新しい参照先に合わせて修正されています。 - また、振る舞いが変わっていないこと(グローバル設定であること)がテストで確認されています。
- DB タスク (
4) テストの追加・更新
activerecord/test/cases/base_test.rbActiveRecord::BaseとActiveRecordモジュール双方に対して、protected_environmentsの値がどう見えるか/変更がどう伝播するかを確認するテストが追加されています。- これにより、「旧 API を経由してもグローバル設定がちゃんと変わる」「挙動自体は後方互換」という点が検証されています。
- 影響範囲・注意点
影響範囲
- 設定の性質が明確化された
もともと実質的にグローバルだったprotected_environmentsが、実装上も「ActiveRecord 全体のグローバル設定」として扱われるようになります。 - クラスごとのカスタマイズは実質サポートされていない
class_attribute風の API があっても、DB タスクなどはActiveRecord::Base/ActiveRecordの値だけを見ているため、
サブクラス単位でprotected_environmentsを変えても意味がありません。
このPRは、その事実をコード構造に揃えたものです。
注意点 (アプリケーション/ライブラリ開発者向け)
新規コードでは
ActiveRecord.protected_environmentsを使用すべき
例:ruby# 推奨 ActiveRecord.protected_environments = %w[production staging] # 動くが非推奨 (将来の削除候補) ActiveRecord::Base.protected_environments = %w[production staging]ActiveRecord::Base経由の設定は非推奨 (deprecation)- 現時点では壊れませんが、将来の Rails メジャーバージョンでは削除される可能性があります。
- ライブラリ・エンジン等で
ActiveRecord::Base.protected_environmentsを直接触っている場合は、早めにActiveRecord.protected_environmentsへ移行するのが安全です。
サブクラス単位での protected_environments 設定に依存すべきでない
もし以下のようにサブクラスで別の値を設定していると、期待通りには動いていません:rubyclass TenantRecord < ActiveRecord::Base self.protected_environments = %w[tenant_production] end実際の DB タスクは
ActiveRecordの設定のみを参照するため、
上記のような使い方は設計として想定されておらず、このPRで「グローバル前提」であることが明確になりました。
- 参考情報 (あれば)
- 元の実装 (
class_attribute導入時):
https://github.com/rails/rails/commit/900bfd94a9c3c45484d88aa69071b7a52c5b04b4 class_attributeをやめた変更:
https://github.com/rails/rails/commit/1a411d4b7f34df2471a93517ad15cc2682bbdbe2- 本PR: Move protected_environments to ActiveRecord module (#56749)
要するに、「protected_environments は ActiveRecord 全体で共有されるグローバルな設定であり、クラス毎に変えるものではない」という設計がコードにも明示された、という位置付けの変更です。
#56779 Optimize ActiveRecord::Base.new to conditionally remove STI checks
マージ日: 2026/2/24 | 作成者: @flavorjones
- 概要 (1-2文で)
ActiveRecord のBase.newが、STI を使っていないモデルでは余計な STI 判定を通らずに Ruby 標準のClass.newをそのまま使えるよう最適化されました。これにより、非 STI モデルのインスタンス生成が Ruby 3.3〜4.0 で 8〜17% 程度高速化され、Ruby 4 では「fast path allocation」最適化も有効になります。
- 変更内容の詳細
背景: これまでの ActiveRecord::Base.new
ActiveRecord は STI をサポートするため、ActiveRecord::Base がクラスメソッド .new をオーバーライドしており、インスタンス生成時に「type カラムの値からどのサブクラスを使うか」を判定する処理が入っています。
- STI モデルには必須の処理
- しかし、STI を使わないモデルにとっては毎回無駄なランタイムチェック
- Ruby 4.0 の「
Class#newの fast path 最適化」の対象外になってしまう(オーバーライドしているため)
この PR は、「STI を使わない & .new を自前で再定義していないモデル」に限って、この ActiveRecord 独自の .new をバイパスし、Ruby デフォルトの Class.new をそのまま使うようにするものです。
実際の最適化内容
非 STI モデルに対して、クラスシングルトンクラス上の .new を Ruby 組み込みの Class.new に置き換えます:
define_singleton_method :new, Class.instance_method(:new)これは「.new を再定義しているが、その実体は Class#new と同じメソッドオブジェクトにする」というテクニックで、Ruby 4 の fast path 最適化の条件を満たします。
Ruby 4 の fast path はおおざっぱにいうと:
- 「呼び出しているのが
Class#newそのものか?」を判定 - 一致すれば、通常より速いインライン化された割り当てパス(
rb_class_new_instance_pass_kw)を通す
という仕組みなので、Class.instance_method(:new) をそのままバインドしてやることで、「ユーザーコードが定義した .new ではなく、ビルトインの Class#new と同一」と判定されます。
STI モデルについて
- STI モデル(
inheritance_columnがtypeなどで設定されていて、実際に STI 判定が必要なモデル)の挙動・フローはこれまで通りです。 - つまり、
typeカラムを見てサブクラスにディスパッチするロジックは変更されていません。 - ベンチマークでも STI モデルのパフォーマンスは「same-ish(誤差範囲)」で、悪化も改善もしていないことが確認されています。
.new を再定義しているモデルの扱い
この PR で重要な制約が明示されています:
- すでにクラス側で
.newを再定義しているモデルは最適化対象外 - つまり、以下のようなクラスには手を触れない:
class User < ApplicationRecord
def self.new(*args, **kwargs, &block)
# 何らかのカスタムロジック
super
end
end- この場合、ActiveRecord は「
.newがすでに再定義されている」と見なして、define_singleton_method :new, Class.instance_method(:new)を行わないようにしてあります。
また、テストでは次のような「あとから .new を再定義する」ケースも確認されています:
- 最初は最適化されていて
Class#newfast path を使っている - ランタイムで
.newを再定義する - 以後は fast path が効かなくなる(=「deoptimization」)
これにより、「途中から .new の挙動を変えても、それに応じて正しく最適化が無効化される」ことがテストされています。
追加されたテスト
activerecord/test/cases/fast_path_allocation_test.rb が新規追加(+234 行)されています。主なポイント:
- 非 STI モデルで
.newがClass#newと同一であることの確認 .new再定義時の挙動確認(最適化されないこと、あるいは deopt されること)- Ruby を debug ビルド(
-DUSE_DEBUG_COUNTER=1)している場合、以下のカウンターを使って fast path がヒットしていることを検証できる:opt_new_hitopt_new_miss
PR 説明内のサンプルスクリプトと同じ要領で、RubyVM.reset_debug_counters / RubyVM.show_debug_counters を使って確認しています。
- 影響範囲・注意点
影響を受けるケース
性能面で恩恵を受けるのは:
- ActiveRecord モデルで
- STI を使っていない
.newを自前で再定義していない
- という条件を満たすもの
これらのモデルでは:
- Ruby 3.3〜4.0 でインスタンス生成が 8〜17% 程度高速化
- Ruby 4.0 では fast path allocation により特に効果が大きい(15〜17%)
ベンチマーク結果まとめ
PR 記載の結果をざっくりまとめると:
- Ruby 4.0.1
- 非 STI + デフォルト
.new: 1.15〜1.17x(15〜17%)高速 - 非 STI + 再定義
.new: ほぼ変化なし - STI: ほぼ変化なし
- 非 STI + デフォルト
- Ruby 3.4.8 / 3.3.10 でも同様に、非 STI + デフォルト
.newのみが 8〜13% 程度高速化
いずれも YJIT on/off 双方で測定されています。
互換性・挙動面の注意
- 機能的挙動(どのクラスが生成されるか、コールバック、バリデーション等)は STI / 非 STI ともに変わらない 前提で実装されています。
- 既存で
.newを再定義しているクラスの挙動は一切変更されません。 .newに依存するメタプログラミング(method(:new)取得、singleton_methodsの確認など)で「Class#newと同一であること」を前提にしているような特殊なコードがあれば、挙動の違いに注意が必要ですが、実質的な影響はほぼないはずです。- Ruby の debug ビルドで
RubyVM.show_debug_countersを用いているため、この一部テストは「debug ビルド時のみ意味がある」類のものですが、通常ビルドには影響しません。
開発者が意識すべき点
- 何もしていなければ(非 STI かつ
.new未再定義)勝手に速くなるため、基本的には「Rails をアップデートすれば恩恵を受ける」類の変更です。 .newをカスタマイズしているモデルを「速度のために元に戻したい」場合は、以下のように「クラスメソッドではなく、after_initializeなどのコールバックやinitializeインスタンスメソッド側にロジックを寄せる」ことを検討すると、今回の最適化の恩恵を得やすくなります。- STI を使っているモデルは今後も
.newのオーバーヘッドが残るため、インスタンス生成コストを更に詰めたい場合は、STI 設計の見直しや Factory レベルでのキャッシュなど他の手段が必要です。
- 参考情報 (あれば)
PR 内で挙げられている Ruby 側情報:
- Fast allocation に関するブログ:
https://railsatscale.com/2025-05-21-fast-allocations-in-ruby-3-5/ - Ruby Redmine: Feature #21254 (Class#new をインライン化する提案)
https://bugs.ruby-lang.org/issues/21254 - Ruby 本体 PR: Inline Class#new
https://github.com/ruby/ruby/pull/13080 - 関連 Ruby コミット:
https://github.com/ruby/ruby/commit/8ac8225c
ベンチマーク:
- 詳細な結果: benchmark.txt
- ベンチマークスクリプト: benchmark.zip
これらを読むと、「Class#new をそのまま使うだけでどれだけアロケーションが速くなるか」「どのような条件で fast path が無効化されるか」の理解が深まり、今回の Rails 側の最適化意図もより明確になります。
#56858 Add to_markdown to Action Text, mirroring to_plain_text
マージ日: 2026/2/24 | 作成者: @flavorjones
- 概要 (1–2文で)
Action Text のリッチテキストを Markdown 形式に変換するto_markdownが追加され、既存のto_plain_textと同様に使えるようになりました。HTML の装飾をできるだけ保持しつつ、安全かつ高速に Markdown へエクスポートできる機能です。
- 変更内容の詳細
新しくできること・API
Action Text の主なエントリポイントに to_markdown が追加されています:
ActionText::RichText#to_markdownActionText::Content#to_markdownActionText::Fragment#to_markdownActionText::Attachment#to_markdown
利用イメージ:
# 典型的な例: @post has_rich_text :body
markdown = @post.body.to_markdown
# => Markdown 文字列を取得to_plain_text と同じような立ち位置で、「エクスポート用の別フォーマット」として Markdown を選べるようになった形です。
変換処理の全体像
MarkdownConversion モジュールの追加
actiontext/lib/action_text/markdown_conversion.rb に、HTML ノードを Markdown に変換するモジュールが新設されました。
これは PlainTextConversion と同様に「HTML を木構造としてたどり、下から上へ畳み込む(bottom-up reduce)」方式で変換します。
主なサポート要素:
- インライン:
<strong>,<b>→**text**<em>,<i>→*text*<del>,<s>→~~text~~<code>→`code`
- ブロック:
<p>→ 改行区切りの段落<h1>〜<h6>→#,##, …######<blockquote>→> ...<pre><code>→lang ...<hr>→---
- リスト:
<ul>→- item<ol>→1. item- ネストしたリストにも対応(字下げで表現)
- リンク:
<a href="...">text</a>→[text](url)- 不正なスキーム (
javascript:など) は Loofah の許可リストでフィルタ
- 画像/添付:
- 画像や Blob などの Action Text アタッチャブルを Markdown に変換
- テーブル:
<table><thead><tbody>...を Markdown テーブルとして出力
<details><summary>:- 対応する Markdown が標準でないため、読みやすい形に落とし込む(実装依存)
これらは Trix と Lexxy の生成する HTML に対して手動テスト済み。
添付ファイルまわりの Markdown 対応
Attachment#to_markdown が追加され、実体である attachable に委譲します。
各 attachable は attachable_markdown_representation を実装:
RemoteImage形式で出力- caption がなければ alt テキストや空文字などにフォールバック
ContentAttachment- 自身が埋め込んでいる HTML を MarkdownConversion に通して Markdown に変換
ActiveStorage::Blob[caption || filename]というリンクテキストを出力(URL は文脈に依存: この PR ではテキスト表現に重点)
MissingAttachable☒を出力(壊れた添付の可視化。#56854 と整合)
サンプルイメージ:
rich = @post.body # ActionText::RichText
rich.to_markdown
# 例:
# "ここは本文です。\n\n\n\n[仕様書.pdf]"共通の木走査ロジック BottomUpReducer
もともと PlainTextConversion 内部にあった木走査クラスが切り出されました:
- 新規ファイル:
actiontext/lib/action_text/bottom_up_reducer.rb - クラス:
ActionText::BottomUpReducer
役割:
- Nokogiri の HTML ノードを再帰的にたどり、子ノードから順に文字列に畳み込む
PlainTextConversionとMarkdownConversionの両方がこのロジックを共有
これにより:
- 変換処理の重複が削減
- 将来、新たなフォーマット変換(例: reStructuredText)を作る場合にも再利用可能
パフォーマンスとセキュリティ
- ベンチマーク:
- 42KB / 932 行の HTML 文書に対し
to_markdown: 約 71 i/s(約 14ms / 回)to_plain_text: 約 52 i/s(約 19ms / 回)- Markdown 変換のほうが約 35% 高速(1.37x)
- セキュリティ:
- Markdown のリンク出力時、Loofah の許可プロトコルリストに基づいて URL スキームを検証
javascript:,data:など危険なスキームは Markdown に出さない設計
- 影響範囲・注意点
- 既存コードへの影響
- 既存の
to_plain_textAPI や挙動は基本的に維持されています。 - 内部実装が
BottomUpReducerを共有する形になったため、極めて細かい端のケースで plain text 出力が若干変化する可能性はありますが、互換性が壊れる類の変更は意図されていません。
- 既存の
- Action Text 利用プロジェクト
- これまで HTML かプレーンテキストしかエクスポート手段がなかったところに Markdown という第3の選択肢が増えます。
- LLM や外部サービスへの連携・メール生成・ドキュメント同期などで、装飾をある程度保持したいが生 HTML は渡したくない、という場面に適しています。
- 添付ファイル表現
- 添付の Markdown 表現はテキストベースの「記号的」な表現に寄せており、アプリによっては独自のフォーマット(例: URL 付きにしたい等)を追加でラップする必要があるかもしれません。
- リンクのフィルタリング
- Loofah の許可スキームに依存するため、既に HTML サニタイズ設定をカスタマイズしている場合でも、Markdown 側の出力が HTML 側と完全に 1:1 にはならないケース(危険 URL の除外など)はあります。
- 参考情報 (あれば)
- PR: https://github.com/rails/rails/pull/56858
- 関連: MissingAttachable の Markdown 表現を扱う PR #56854
- 実装のキーファイル:
actiontext/lib/action_text/markdown_conversion.rbactiontext/lib/action_text/bottom_up_reducer.rbactiontext/app/models/action_text/rich_text.rb(to_markdown追加)actiontext/lib/action_text/attachment.rb/attachables/*.rb(各種添付の Markdown 対応)
- テスト:
actiontext/test/unit/markdown_conversion_test.rbに包括的なユニットテストが追加されており、どの HTML がどの Markdown になるかの実例集としても参照可能です。
#56868 Fix deprecation of sidekiq/testing in integration test adapter at 8-1-stable
マージ日: 2026/2/24 | 作成者: @yahonda
- 概要 (1-2文で)
Rails 8.1 系の Active Job の Sidekiq 統合テスト用アダプタで、require "sidekiq/testing"による非推奨警告が出ないように修正した PRです。Sidekiq 8.1.1 以降の新しいテスト API に合わせるための追従変更です。
- 変更内容の詳細
※ファイル: activejob/test/support/integration/adapters/sidekiq.rb のみ変更(+6/-2)
Sidekiq 8.1.1 からは、従来の
require "sidekiq/testing"
Sidekiq::Testing.inline! # などといった使い方が非推奨となり、require "sidekiq/testing" を呼ぶだけで以下のような警告が出ます:
⛔️ `require "sidekiq/testing"` is deprecated and will be removed in Sidekiq 9.0.
See https://sidekiq.org/wiki/Testing#new-apiこの PR は、Active Job の「integration:sidekiq」テストで内部的に行っていた sidekiq/testing の読み込みや旧 API への依存をやめ、新しい Sidekiq のテスト API を使うように調整しています。その結果、bundle exec rake test:integration:sidekiq を実行しても上記の非推奨警告が出なくなります。
具体的な変更イメージ(抜粋レベルの擬似コード):
従来:
require "sidekiq/testing"
Sidekiq::Testing.fake!
# or inline!, disable!, etc.修正後(例示的な書き方):
require "sidekiq" # あるいは既存の require に統合
# 新しいテスト API に則ったモード設定
Sidekiq::TestMode.enable! # ※実際の名前は Sidekiq 本体の新 API に依存実際の実装では、Sidekiq 本体のコミット(c4f68077...)で導入された新 API に合わせて、Active Job の統合テストアダプタ側のモード設定/キュー操作の呼び出しを切り替えています。
- 影響範囲・注意点
影響範囲
- Rails 本体リポジトリ内の「
test:integration:sidekiq」タスク(activejobの統合テスト)でのみ影響します。 - アプリケーション側で
ActiveJob + Sidekiqを通常利用しているだけのケースには影響しません(本 PR は「テストサポート用コード」のみ変更)。
- Rails 本体リポジトリ内の「
挙動
- テストの実行結果(テストケース数・成功/失敗)は変わっておらず、ログに出る非推奨警告だけが消えています。
- Sidekiq の新テスト API に則る形に変えたことで、将来の Sidekiq 9.0 で
sidekiq/testingが削除されても Rails の統合テストが壊れにくくなります。
注意点
- この PR は Rails 内部テスト用の変更なので、アプリ側で同様の警告に悩んでいる場合は「アプリ側のテストコードも新 API に書き換える」必要があります(
require "sidekiq/testing"をやめる、あるいは Sidekiq wiki の「Testing#new-api」に従う)。 - Ruby 4.0 での
cgiやURI::RFC3986_PARSER.make_regexpに関する警告はこの PR とは無関係で、引き続き表示されます。
- この PR は Rails 内部テスト用の変更なので、アプリ側で同様の警告に悩んでいる場合は「アプリ側のテストコードも新 API に書き換える」必要があります(
- 参考情報 (あれば)
Sidekiq Testing 新 API の解説:
https://sidekiq.org/wiki/Testing#new-apiこの PR のフォローアップ元:
- Rails PR #56862(同様に Sidekiq testing の非推奨対応を行った先行 PR)
Sidekiq 本体の関連コミット:
#56263 Add RAILS_HOST_APP_PATH support for editor links in devcontainer/Docker environments
マージ日: 2026/2/24 | 作成者: @elalemanyo
- 概要 (1-2文で)
Rails のエラーページに表示される「エディタで開く」リンクが、devcontainer や Docker コンテナ内で動作するようにするため、RAILS_HOST_APP_PATHという新しい環境変数が追加されました。これにより、コンテナ内部パスをホスト側のパスに変換した URL を生成できるようになります。
- 変更内容の詳細
2-1. 課題背景
- Rails では
EDITORまたはRAILS_EDITOR環境変数を使うことで、エラーページ上の「ソースへのリンク」をクリックするとローカルエディタ(VS Code など)で該当ファイルを開けます。 - しかし、Rails を devcontainer や Docker コンテナ内で動かしている場合、
- エラーページに出るパスが
/workspaces/app/...や/app/...など「コンテナ内部のパス」になる - 実際にエディタはホストマシン側で動いており、ホスト側のパス(例:
/Users/you/projects/app/...)と一致しない
→ その結果、vscode://file/...などのリンクをクリックしてもファイルを正しく開けない、という問題がありました(#55295)。
- エラーページに出るパスが
2-2. 新しい環境変数 RAILS_HOST_APP_PATH
この PR では、ホストマシン側のアプリケーションルートパスを教えるための環境変数 RAILS_HOST_APP_PATH が追加されています。
- 役割:
- 「コンテナ内での Rails.root」と「ホスト側でのプロジェクトルート」をマッピングする
- 使い方(devcontainer の例):
// .devcontainer/devcontainer.json
{
"containerEnv": {
"EDITOR": "code",
// Rails.root (コンテナ内) に対応するホスト側パスを指定
"RAILS_HOST_APP_PATH": "${localWorkspaceFolder}"
}
}VS Code devcontainer の場合、${localWorkspaceFolder} はホストマシン上のプロジェクトパス(例: /Users/developer/projects/myapp)になります。
2-3. パス変換の動作仕様
ActionDispatch::DebugView に translate_path_for_editor という private メソッドが追加され、エディタ URL を生成する前にパス変換が行われます。
挙動は以下の通りです:
RAILS_HOST_APP_PATHが設定されている場合にのみパス変換を実行- 対象となるのは「Rails.root 配下のファイル」のみ
- Rails アプリのコード(
app/,config/,lib/,test/など) - Gem やライブラリなど、Rails.root 外のパスは変換せず、そのまま利用
- Rails アプリのコード(
- パスの先頭が Rails.root と安全に一致するかをチェックしてから置換を行う
- 例:
- Rails.root =
/workspaces/myapp - ファイルパス =
/workspaces/myapp/app/models/user.rb
→ 先頭一致が確認できるので OK
- Rails.root =
/appと/app2のような「似たパス」を誤ってマッチさせないように、ディレクトリ境界を考慮した安全な比較(/appが/app2にマッチしない)を実施
- 例:
- 一致した場合、
Rails.root部分をRAILS_HOST_APP_PATHに置き換えて返す
変換例
- コンテナ内のパス:
/workspaces/myapp/app/models/user.rb
- 設定:
Rails.root(コンテナ) =/workspaces/myappRAILS_HOST_APP_PATH(ホスト) =/Users/developer/projects/myapp
- 変換後:
/Users/developer/projects/myapp/app/models/user.rb
- 生成されるエディタ URL(VS Code の場合):
vscode://file//Users/developer/projects/myapp/app/models/user.rb:42
これは既存の ActiveSupport::Editor が行う URL 生成処理に対して、渡すパスだけを変換している形なので、基本的なエディタ連携の仕組み自体は変えていません。
2-4. 実装箇所
- 主な変更ファイル:
actionpack/lib/action_dispatch/middleware/debug_view.rbtranslate_path_for_editorメソッド追加- エラーページでリンクを組み立てるときに、この変換メソッドを通すように変更
actionpack/test/dispatch/debug_exceptions_test.rb- 新機能・エッジケース向けに 9 個のテスト追加
- 例:
RAILS_HOST_APP_PATHがない場合は挙動が変わらない- Rails.root 配下のパスだけが変換される
/appvs/app2のような誤マッチが起こらない- Gem ファイルなどアプリ外パスは変換されない など
- 例:
- 新機能・エッジケース向けに 9 個のテスト追加
actionpack/CHANGELOG.md- 新機能としての追加を記載
- 影響範囲・注意点
- 影響範囲:
- 主に「エラーページのソースコードリンク」および「
EDITOR/RAILS_EDITORを利用したエディタ連携」に限定されます。 - 普段コンテナを使っていないローカル開発(直接ホストで Rails を起動している)では、
RAILS_HOST_APP_PATHを設定しなければ既存挙動のままです。
- 主に「エラーページのソースコードリンク」および「
- 後方互換性:
RAILS_HOST_APP_PATHが未設定の場合、これまで通りコンテナ内パスのまま URL が生成されます。- 既存の
RAILS_EDITOR/EDITORの指定方法や挙動は変更されていません。
- 設定時の注意:
RAILS_HOST_APP_PATHには ホスト側から見た Rails.root に対応するパス を指定する必要があります。- 例:
- コンテナ内マウント:
/workspaces/myapp - ホスト側:
/Users/developer/projects/myapp
→RAILS_HOST_APP_PATH=/Users/developer/projects/myapp
- コンテナ内マウント:
- 例:
- マウント先がずれている、あるいはシンボリックリンクを多用している場合は、意図したパスに変換されるかを一度確認するとよいです。
- セキュリティ:
- パス比較において「部分一致」を避けるロジックが入っており、誤ったディレクトリにマッピングされにくいようになっています。
- 変換対象は Rails.root 配下のみなので、ホスト上の任意パスへ恣意的に飛ばすようなリスクは抑えられています。
- 参考情報 (あれば)
- 該当 PR:
- 関連 Issue:
- devcontainer/Docker 環境でエディタリンクが機能しない問題: https://github.com/rails/rails/issues/55295
- 実運用での設定例(VS Code Dev Containers):
.devcontainer/devcontainer.jsonでcontainerEnvにEDITORとRAILS_HOST_APP_PATHを設定EDITOR=codeと組み合わせることで、エラーページのリンク → VS Code でホスト上の該当ファイルが開くようになります。
#56828 fix: breaking support of hyphen in table names
マージ日: 2026/2/24 | 作成者: @djezzzl
- 概要 (1-2文で)
Rails 8系で「ハイフンを含むテーブル名」を使うとexec_insert実行時に失敗していた問題を、テーブル名のパース処理を修正することで直したPRです。正しくクオートされた"table-with-hyphen"のようなテーブル名でも、従来通りexec_insertが動作するようになります。
- 変更内容の詳細
問題の背景
Rails 7.0 から 8.0 / 8.1 へのアップグレード時に、以下のようなケースでエラーが発生していました:
ActiveRecord::Schema.define do
create_table "table-with-hyphen", force: true do |t|
end
end
ActiveRecord::Base.connection.exec_insert(
"INSERT INTO \"table-with-hyphen\" DEFAULT VALUES"
)Rails 8 系では暗号化機能等のために、exec_insert 内で SQL 文字列から「テーブル名(table_ref)」を抜き出し、そのテーブルの primary key / private key を取得しようとします。
このとき、INSERT 文からテーブル名を抽出する正規表現が、ハイフンを含むテーブル名をサポートしていなかったため、以下のような副作用が発生していました。
- Rails 8.0:
primary_key(table_ref)呼び出しで「未知のテーブル」として例外が発生するケースがある - main ブランチ(8.1 以降想定): スキーマキャッシュを見るようになり例外は出ないが、テーブル名をうまく取れないことで、暗号化関連で期待される処理や returning 句の組み立てに問題が出る
対応内容
ActiveRecord::ConnectionAdapters::DatabaseStatements#extract_table_ref_from_insert_sql の正規表現を 1行だけ修正しています。
変更前(概念的):
if sql =~ /into\s("[-A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im
$1.delete('"').strip
endこのメソッドは INSERT 文から "table-with-hyphen" のようなテーブル名部分を抜き出していますが、クオート有り/無しで許可する文字クラスが異なっており、ハイフンを含むパターンが取りこぼされていたため、"table-with-hyphen" が正しくマッチしないケースがありました。
PRでは、この正規表現を修正して:
INSERT INTO "table-with-hyphen" ...INSERT INTO [table-with-hyphen] ...(将来 / 他アダプタ考慮)- など、クオートされていればハイフンを含むテーブル名も正しくパースできるようにしています。
同時に、database_statements_test.rb にテストを追加し、
sql = 'INSERT INTO "table-with-hyphen" DEFAULT VALUES'
assert_equal "table-with-hyphen",
connection.send(:extract_table_ref_from_insert_sql, sql)のような形で、ハイフン付きテーブル名を正しく抽出できることを確認しています。
- 影響範囲・注意点
- 対象となるのは、**ActiveRecord の
exec_insertが利用する「INSERT 文からテーブル名を抜き出す処理」**のみです。 - 具体的な影響が出るのは次のようなケースです:
- テーブル名にハイフン
-を含めている - SQL を文字列で直接渡して
exec_insert/ ActiveRecord 内部の insert 系 API を使っている - DB アダプタが
"table-with-hyphen"のようなクオートされた識別子をサポートしている
- テーブル名にハイフン
- Rails 8.0 / 8.1 へのアップグレード時に、
- 暗号化有効な環境
- あるいは returning 句を含む INSERT など と組み合わさると、ハイフン付きテーブル名でのみエラーになる / 想定外の SQL になる、といった不具合が解消されます。
- 逆に、通常の英数字・アンダースコアのみのテーブル名を使っているアプリケーションには、実質的な挙動変更はありません(正規表現の許容範囲が広がっただけ)。
- 参考情報 (あれば)
- PR: https://github.com/rails/rails/pull/56828
- 関連コミット(暗号化関連でのテーブル名→PK 取得ロジック変更):
https://github.com/rails/rails/commit/9bbbf4b8b9bf3d7cb31c0425a6646cec49e4bc6c
このPRにより、「正しくクオートされたハイフン付きテーブル名はサポート対象」という方針が明示的にテストで担保されるようになりました。
#56865 Use instance_variables_to_inspect instead of custom inspect methods
マージ日: 2026/2/24 | 作成者: @byroot
- 概要 (1-2文で)
Rails内部で定義されていた多数のカスタムinspectメソッドをやめ、共通ヘルパinstance_variables_to_inspectを使った統一的な実装に置き換えるPRです。これにより、オブジェクトのデバッグ表示(#inspect)の一貫性が増し、保守性・後方互換性の管理がしやすくなっています。
- 変更内容の詳細
全体方針
- これまで各クラスが独自に
def inspectを定義していた部分をできるだけ削除し、instance_variables_to_inspectが返すインスタンス変数の一覧からinspectを組み立てる仕組みに変更。 - Active Support に「古いRuby用の
inspectの振る舞いを補うためのバックポート」的なモジュールを追加し、
それを使って Rails 全体で一貫したinspect出力を得られるようにしている。
主な変更対象
代表的なクラスだけ抜粋します(実際には 26 ファイルが対象):
ActionCable::Connection::BaseAbstractController::BaseActionText::AttachmentActiveModel::Error,ActiveModel::ErrorsActiveRecord::Encryption::Cipher::Aes256GcmActiveSupport::Cache::FileStore,NullStore,RedisCacheStoreActiveSupport::KeyGeneratorActiveSupport::MessageEncryptor,MessageVerifier- など
これらは以前、例えば以下のようなカスタム inspect を持っていたと考えられます:
def inspect
"#<#{self.class.name} key: #{key}, options: #{options.inspect}>"
endこれを、共通的な仕組みに寄せています。
ActiveSupport::InspectBackport の追加
activesupport/lib/active_support/inspect_backport.rb が新規追加されています(約 50 行)。
このモジュールは概ね以下のような役割を担います:
- Rubyのバージョン差異を吸収しつつ、
Rails が期待する形式で#inspectを構築するためのヘルパを提供。 - その中心となるのが
instance_variables_to_inspectで、
「inspectで表示したいインスタンス変数の一覧」を返すメソッドです。
典型的な利用イメージ:
module MyInspectMixin
def inspect
ivars = instance_variables_to_inspect
attrs = ivars.map { |ivar| "#{ivar}=#{instance_variable_get(ivar).inspect}" }.join(", ")
"#<#{self.class} #{attrs}>"
end
end各クラスは、
- 個別に
inspectを実装する代わりに、 - 「どのインスタンス変数を見せるか」を
instance_variables_to_inspectで調整する
という作りに寄せられています。
各コンポーネントの変更傾向
細部のコードはPR本体に依存しますが、だいたい以下のようなリファクタリングです。
例: Cache Store 系
ActiveSupport::Cache::FileStore / NullStore / RedisCacheStore などで:
- 旧: 独自の
def inspect実装 - 新:
instance_variables_to_inspectでインスタンス変数の選別のみ行う
擬似コード的には:
# 旧: 手作りの inspect
def inspect
"#<#{self.class.name} options=#{@options.inspect}>"
end
# 新: 表示するインスタンス変数だけ制御
def instance_variables_to_inspect
[:@options, :@cache_path] # などクラスごとの必要なもの
end
# inspect 本体は共通ロジックが利用される例: ActiveModel::Error / Errors
ActiveModel::Error や ActiveModel::Errors でも:
- 旧: メッセージや属性名を組み立てる
inspectがあった - 新:
instance_variables_to_inspectを使って簡潔化 + テストで期待する文字列表現を保証
これに対応して error_test.rb, errors_test.rb に inspect 関連の期待値テストが追加・調整されています。
例: 暗号・署名系
ActiveRecord::Encryption::Cipher::Aes256GcmActiveSupport::KeyGenerator, MessageEncryptor, MessageVerifier など:
- 内部キーや暗号パラメータの
inspect出力が、instance_variables_to_inspectにより統一的な形式になる。 - 場合によっては「見せてよいインスタンス変数だけを列挙」して、
センシティブ情報がむやみに出ないよう制御可能。
テスト(cipher_test.rbなど)で inspect 出力が壊れていないことを確認するケースが追加されています。
例: ActionCable, AbstractController, ActionText
ActionCable::Connection::Base,AbstractController::Base,ActionText::Attachment などでも同様に:
- カスタム
inspectを削除/簡略化。 instance_variables_to_inspectで表示したいインスタンス変数の範囲だけ指定。- 対応するテスト(
*_test.rb)にinspectの期待出力が追加。
- 影響範囲・注意点
影響範囲
inspectの出力が変わる可能性がある クラス:- ActionCable 接続
- 抽象コントローラ
- ActionText の添付
- ActiveModel::Error / Errors
- ActiveRecord 暗号化関連の Cipher
- Cache Store (FileStore, NullStore, RedisCacheStore)
- KeyGenerator / MessageEncryptor / MessageVerifier
- ほか PR 対象のクラス
実務的な影響
テストで
inspectの文字列を直接比較している場合- 出力フォーマットが微妙に変わる可能性があります。
- 回避策:
- 可能なら
inspectの具体的な文字列に依存しないテストに変更する
(include?,match,be_aなどで要素だけ検証する、など)。 - どうしても文字列比較が必要なら、新しい出力に合わせて更新。
- 可能なら
ロギングやデバッグ出力で
inspectを前提にしている場合- 情報量/フォーマットが変わるので、ログパース等をしていると影響が出る可能性があります。
- 特にセキュリティ・監査用に
inspect出力を機械処理している場合は確認推奨。
独自クラスでの
instance_variables_to_inspect活用のヒント- 自前ライブラリで Rails と似たスタイルの
inspectを作りたい場合、instance_variables_to_inspectで見せたいインスタンス変数を明示的に返すようにし、- あとは ActiveSupport 側の共通ロジックに任せる
という流れを真似することで、保守性の高いinspectを実装できます。
- 自前ライブラリで Rails と似たスタイルの
互換性面
- PR の目的が「古いPR(#56782)のrebase & cleanup」であることからもわかるように、 すでに議論された変更をブラッシュアップした版と考えられます。
inspectは通常「人間向けのデバッグ用」なので、
これに強く依存しているコードはそもそも非推奨に近い設計ですが、
そうしたユースケースでは挙動変化に注意が必要です。
- 参考情報 (あれば)
- 元PR: https://github.com/rails/rails/pull/56782
- 本PR: https://github.com/rails/rails/pull/56865
ActiveSupport::InspectBackportの中身と利用例を見ると、
今後Railsがinspectの挙動をどう統一していきたいかの方針がわかります。
#56817 Fix SQLite3 column equality for stored vs virtual generated columns
マージ日: 2026/2/24 | 作成者: @afurm
- 概要 (1-2文で)
SQLite3 アダプタで、生成カラム(generated columns)の比較ロジックに不整合があり、:storedと:virtualが「== では同じだが hash は違う」という状態になっていた問題を修正する PR です。これにより、生成カラムの等価判定とハッシュ値が一貫し、Hash や Set での利用時のバグを防ぎます。
- 変更内容の詳細
何が問題だったか
対象は ActiveRecord::ConnectionAdapters::SQLite3::Column の等価性判定です。
#==は「仮想カラムかどうか (virtual?)」しか見ていなかった- 一方
#hashは@generated_type(:storedか:virtualか)を含めて計算していた
そのため、例えば次のような 2 つのカラムオブジェクトは:
generated_type: :storedの生成カラムgenerated_type: :virtualの生成カラム
について、
col1 == col2はtrueになる- しかし
col1.hash == col2.hashはfalseになる
という「eql?/hash 契約違反」が発生していました。これは Ruby の Hash / Set で重大なバグ要因になります(キーは eql? かつ hash が同じでなければならない)。
今回の修正内容
1. Column#== の修正
activerecord/lib/active_record/connection_adapters/sqlite3/column.rb で、#== の比較条件に virtual_stored?(= 生成カラムが stored か virtual か)も含めるように変更しています。
概念的には、次のようなイメージになります(実際のコードは多少異なりますが、意図としてはこれに近いです):
def ==(other)
other.is_a?(self.class) &&
name == other.name &&
sql_type == other.sql_type &&
# 既存の比較条件
rowid? == other.rowid? &&
auto_increment? == other.auto_increment? &&
# ここが今回の追加: 生成タイプ (stored/virtual) まで見る
virtual_stored? == other.virtual_stored?
endポイント:
- これまでは「仮想カラムかどうか (
virtual?)」のみ見ていたが、generated_typeの違い(:storedvs:virtual)を区別していなかった - 今回の修正で、
#hashと同じ情報(generated_type)を#==でも参照するようにした
rowid と auto_increment に関する既存の比較ロジックは変更されていません。
2. 回帰テスト追加
activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb に、回帰テストが追加されています。
test_generated_type_changes_column_equality
このテストは、「同じ名前・型・その他の属性を持ちつつ、generated_type: :stored と generated_type: :virtual だけが異なる 2 つのカラムが == で異なるものとして扱われること」を検証します。
これにより、今後の変更で再び generated_type の比較を落としてしまうことを防ぎます。
- 影響範囲・注意点
影響しうるケース
- SQLite3 を使っていて
- ActiveRecord の
Columnオブジェクトを- Hash のキーとして使う
- Set の要素として使う
- あるいは
==に依存するキャッシュ・比較ロジックを独自に書いている
といったコードに影響する可能性があります。
以前の挙動:
generated_type: :storedと:virtualのカラムを==で比較すると「同じ」と判定される- しかし Hash/Set では衝突・重複が正しく扱われない可能性がある(
hashが異なるため)
今回の挙動:
generated_typeが異なる生成カラムは==でも異なるものと判定される- Hash/Set での挙動が Ruby の期待通り(
eql?/hash契約準拠)になる
互換性の観点
- もし既存コードが「generated column の stored/virtual の違いを無視して
==で同一視する」ことを前提にしていた場合、その前提は崩れます- ただし、元々その前提は
#hashと矛盾しており、Hash/Set 利用時にバグを誘発しうる動きだったため、修正は妥当かつバグフィックスと言えます
- ただし、元々その前提は
- Column オブジェクトを単純に比較していない、あるいは SQLite3 の generated columns を使っていないアプリケーションには、実質的に影響はありません
- 参考情報 (あれば)
- PR 番号: https://github.com/rails/rails/pull/56817
- 検証に使用されたテストコマンド:
- 個別テスト:
ARCONN=sqlite3 bundle exec ruby -Itest test/cases/adapters/sqlite3/sqlite3_adapter_test.rb -i "/test_(generated_type_changes_column_equality|rowid_changes_column_equality)/"
- SQLite3 アダプタテスト一式:
ARCONN=sqlite3 bundle exec ruby -Itest test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
- 個別テスト:
要点としては、「SQLite3 の生成カラムにおいて、== と hash が見ている属性を揃え、特に stored/virtual の違いをきちんと区別するようにしたバグ修正」です。
#56862 Fix deprecation of sidekiq/testing/inline
マージ日: 2026/2/24 | 作成者: @skipkayhil
- 概要 (1-2文で)
Sidekiq 8.1.1 以降でsidekiq/testing/inlineが非推奨化された影響により、これまで暗黙的に行われていたsidekiq本体の読み込みが行われなくなった問題を修正した PR です。Rails の Active Job テストで Sidekiq の inline テストアダプタを使う際に、明示的にsidekiqを require するようにしています。
- 変更内容の詳細
対象ファイル:
activejob/test/adapters/sidekiq.rb
背景:
- 以前の Sidekiq では
sidekiq/testing/inlineが内部でsidekiq/testingを require し、そのsidekiq/testingがさらにsidekiqを require していました。 - Sidekiq 8.1.1 で
sidekiq/testing/inlineが非推奨になった際、sidekiqを間接的に読み込むための require チェーンが削除されました。 - その結果、Rails 側のテストアダプタが
sidekiq/testing/inlineだけを require している構成だと、Sidekiq本体が読み込まれず、テストでエラーになる(または定義が不足する)状況が発生しました。
修正内容:activejob/test/adapters/sidekiq.rb 内で、Sidekiq のテスト用 inline アダプタを利用するために、sidekiq 本体を明示的に require する行が追加されています。
イメージ的な変更内容(概略・擬似コード):
# 変更前(イメージ)
require "sidekiq/testing/inline"
# 変更後(イメージ)
require "sidekiq" # ← 新規追加
require "sidekiq/testing/inline"実際には 6 行追加・1 行削除されており、主なポイントは「Sidekiq 本体の明示的な読み込み」と、それに伴う require 順序の調整です。
- 影響範囲・注意点
影響範囲:
- Rails の Active Job で Sidekiq アダプタを使い、かつ Rails が提供するテストアダプタ (
activejob/test/adapters/sidekiq.rb) を利用しているテストコード。 - 特に Sidekiq 8.1.1 以降のバージョンを利用しているプロジェクトで、
sidekiq/testing/inlineまわりのロードエラーや定義不足でテストが落ちていたケースが解消されます。
- Rails の Active Job で Sidekiq アダプタを使い、かつ Rails が提供するテストアダプタ (
注意点:
- この修正は「テスト環境での require 問題の解消」が目的であり、Sidekiq の非推奨 API 自体を置き換えるものではありません。Sidekiq 8.1 系での
sidekiq/testing/inline非推奨に伴う将来的な API 変更には引き続き注意が必要です。 - アプリ側で独自に
require "sidekiq/testing/inline"を行っている場合、本 PR による Rails 側の修正と合わせて Sidekiq 本体が確実にロードされるようになりますが、Sidekiq のリリースノートや deprecation メッセージに沿ったテスト構成の見直しは別途検討した方がよいです。
- この修正は「テスト環境での require 問題の解消」が目的であり、Sidekiq の非推奨 API 自体を置き換えるものではありません。Sidekiq 8.1 系での
- 参考情報 (あれば)
- Sidekiq 8.1.1 での
sidekiq/testing/inline非推奨に関する変更点や意図は、Sidekiq 本体の CHANGELOG / リリースノートを確認するとよいです。 - Rails 側では、この PR により「Sidekiq のバージョンアップに起因するテスト環境での読み込み順問題」を吸収しており、アプリケーションのテストコード自体を変更しなくても、基本的には動作が安定する想定です。
#56774 Add parallel step groups to CI runner
マージ日: 2026/2/19 | 作成者: @djmb
- 概要 (1-2文で)
Rails の CI ランナー (ActiveSupport::ContinuousIntegration) に「ステップグループ」を導入し、グループ単位でステップを並列実行できるようにした変更です。これにより、Rubocop / Brakeman / テストなど互いに依存しない処理を並列化して CI の実行時間を短縮できます。
- 変更内容の詳細
新機能: グループと並列実行
ActiveSupport::ContinuousIntegration に「グループ」という概念が追加され、group DSL でステップをまとめ、そのグループ内のステップを並列実行できるようになりました。
group "Checks", parallel: 3 do
step "Style: Ruby", "bin/rubocop"
step "Security: Brakeman", "bin/brakeman --quiet"
step "Security: Gem audit","bin/bundler-audit"
endポイント:
parallel:オプションで「同時に走らせるステップ数」の上限を指定parallel: 3→ 最大3つのステップを同時実行- 省略時は
parallel: 1で、従来通りの逐次実行
- グループ名(ここでは
"Checks")単位でまとめて実行・表示される
出力の扱いと進捗表示
- 並列実行時の標準出力が混ざらないよう、各ステップは PTY 経由(利用不可なら
Open3フォールバック)で出力をキャプチャ - 実行中のステップ名を含む「ライブ進捗行」を出して、どのステップが動いているかを把握しやすくしている
- 典型的には、現在進行中のステップ一覧が1行で更新されるイメージ
- 各ステップ終了時に、そのステップのログがまとまった形で出る構成になっていると考えられます
ネストしたグループの挙動
グループはネスト可能ですが、「並列実行できるのは最外層のグループのみ」という制約があります。
group "Checks", parallel: 2 do
group "Tests" do
step "Tests: Rails", "bin/rails test"
step "Tests: Seeds", "env RAILS_ENV=test bin/rails db:seed:replant"
end
step "Style: Ruby", "bin/rubocop"
step "Security: Brakeman", "bin/brakeman --quiet"
step "Security: Gem audit","bin/bundler-audit"
endこの例の挙動イメージ:
- 外側
"Checks"グループはparallel: 2なので、以下の「単位」を同時に2つまで走らせる:- サブグループ
"Tests"(中の2ステップは逐次実行) step "Style: Ruby"step "Security: Brakeman"step "Security: Gem audit"
- サブグループ
"Tests"グループ内の2ステップは、互いに依存がある前提 で順番に実行される- DB の状態などに依存するテストセットとシード投入などを、1スレッド中でシリアルに実行させたいケースを想定
つまり:
- 外側グループ = 並列単位 (ジョブスロット)
- 内側グループ = 1スロット内の逐次ジョブ束
という使い分けができるようになりました。
実装面での主な変更ファイル
activesupport/lib/active_support/continuous_integration.rb- CI DSL のコアロジックを拡張し、グループと並列実行をサポート
activesupport/lib/active_support/continuous_integration/group.rb- 新規ファイル。グループの表現と、並列スケジューリング・実行ロジックを定義
activesupport/test/continuous_integration_test.rb- グループ・並列実行に関するテストが多数追加され、仕様がテストでカバーされている
railties/lib/rails/generators/rails/app/templates/config/ci.rb.tt- 新規アプリ生成時の
config/ci.rbテンプレートが更新され、グループ/並列利用を前提とした構成に調整されている可能性が高い
- 新規アプリ生成時の
- 影響範囲・注意点
影響範囲
- Rails 標準の CI 設定 (
config/ci.rb) を利用しているプロジェクトで、この新しい DSL(グループ・並列実行)を利用可能 - 既存の
stepベースの CI 設定は、parallelを指定しなければ従来通り逐次実行される設計のため、後方互換性は保たれている とみなせます
注意点・設計上の前提
並列安全性の確保はユーザー側の責務
- 並列実行するステップ間で、DB・ファイルシステム・キャッシュ・外部サービスなどの副作用がぶつからないようにする必要があります
- 例:
- 同じ DB に対して schema 変更+テスト実行を並列に走らせない
- 同じ一時ディレクトリを共有して書き換えるタスクを並列にしない
ネストグループの並列設定
- 「外側のみ並列」という制約があるため、ネストしたグループに対して別途
parallel:を指定しても、実質的には意味を持たない可能性が高いです - 並列にしたい単位は「最外層グループ直下の要素」に揃えるのが安全です
- 「外側のみ並列」という制約があるため、ネストしたグループに対して別途
出力フォーマットへの依存
- 出力が PTY / Open3 経由のバッファリング・整形に変わるため、CI ログの解析を専用ツールやスクリプトで行っている場合は、ログ形式の変化に注意が必要です
実行時間・リソースのトレードオフ
- 並列度を上げるほど CPU / メモリ消費も増えるため、CI サーバーのリソース上限を考えて
parallel:の値を調整する必要があります
- 並列度を上げるほど CPU / メモリ消費も増えるため、CI サーバーのリソース上限を考えて
- 参考情報 (あれば)
- 実装・仕様の詳細は以下のファイルを見ると把握しやすいです:
activesupport/lib/active_support/continuous_integration.rbactivesupport/lib/active_support/continuous_integration/group.rbactivesupport/test/continuous_integration_test.rb
- 新規アプリでのデフォルト CI 設定:
railties/lib/rails/generators/rails/app/templates/config/ci.rb.tt
→ ここを読むと、Rails コアチームが想定している CI ベストプラクティス構成(どのチェックをどうグルーピング・並列化するか)の具体例を確認できます。
#56791 Materialize transactions before instrumentation
マージ日: 2026/2/19 | 作成者: @djmb
- 概要 (1-2文で)
このPRは、Active Record の「遅延トランザクション (lazy transaction materialization)」使用時に、BEGIN/SAVEPOINT のクエリ時間が二重計測されてしまうバグを修正するものです。トランザクションの実体化をログ計測処理の前に行うように変更し、sql.active_record の計測結果が実際のクエリ単位に正しく対応するようにしています。
- 変更内容の詳細
問題の背景
- 遅延トランザクションが有効な場合、
transaction do ... endのブロックに入った時点ではまだBEGINは送られず、最初のクエリ実行時に初めてBEGINが発行される挙動になっています(トランザクションの「遅延実体化」)。 - 以前の実装では、この「最初のクエリ」が
logメソッド(sql.active_recordの計測やログ出力を行う部分)の内部で実体化されていました。 - その結果:
- 「最初のクエリ」の
sql.active_record通知には、「BEGIN の round trip + 実際のクエリの round trip」の合計時間が含まれる - BEGIN 自体も別途
sql.active_record通知が発行される - よって BEGIN のコストが 2 回分レポートされてしまう(見かけ上、最初のクエリが 2 往復分かかっているように見える)
- 「最初のクエリ」の
遠隔 DC の DB に接続している場合など、ネットワークレイテンシがある環境では、この二重カウントが特に顕著に観測されていました。
主な修正内容
コアの変更は execute_intent 内の処理順序です。
変更前(概念的な流れ):
log(sql) do- (ブロック内で)トランザクションがまだ実体化されていなければ
BEGINを発行
- (ブロック内で)トランザクションがまだ実体化されていなければ
- 実際のクエリを実行
end(このlog全体が一つのsql.active_recordとして計測される)
変更後:
logを呼び出す前に、execute_intent内でトランザクションの実体化を済ませる- ここで
BEGIN/SAVEPOINTが必要なら、それらが個別のsql.active_recordとして完結する
- ここで
- その後、
log(sql) do ... endの中では純粋に「ユーザーが発行したクエリ」だけを実行・計測する
Ruby 擬似コードイメージ(実際のコードとは多少異なりますが、意図の説明用):
def execute_intent(sql, name = nil)
# 変更後: ここで先にトランザクションを実体化
materialize_transaction_if_needed
log(sql, name) do
# ここではもう BEGIN/SAVEPOINT は発行されない
execute_sql(sql)
end
endこれにより:
BEGIN/SAVEPOINTはそれぞれ独立したsql.active_record通知として計測される- その後の「最初のクエリ」の
sql.active_recordには、そのクエリ自身の round trip のみが含まれる - 同じトランザクション内での合計時間は変わらないが、各クエリ単位の計測が正しくなる
テストの追加
以下のテストファイルが拡張されています。
activerecord/test/cases/transaction_instrumentation_test.rbsql.active_recordの通知内容・回数、トランザクション開始時の計測挙動が期待通りであることを検証。- 主に「BEGIN / SAVEPOINT と、トランザクション内の最初のクエリが別々のイベントであり、時間が二重計測されない」ことを保証するためのテストが追加されています。
activerecord/test/cases/transactions_test.rb- 遅延トランザクションと通常のトランザクション、ネストしたトランザクションなどの組み合わせで、動作・通知が破壊されていないことを確認するテストが追加されています。
コード変更自体は abstract/database_statements.rb の 1 ファイルにとどまり、処理フローの順序変更(+13行/-1行)と、それを担保するテスト追加がメインです。
- 影響範囲・注意点
主な影響範囲
- 対象:
- Active Record のトランザクションを利用しつつ、
ActiveSupport::Notificationsやログ出力などの「クエリ単位の計測情報」を使っているコード・モニタリング基盤 - 特に
sql.active_recordのduration/allocationsなどを用いてパフォーマンス計測や可視化をしているツール
- Active Record のトランザクションを利用しつつ、
- 変化するもの:
- 「トランザクション内の最初のクエリ」の reported duration が短く見えるようになります(これまで含まれていた BEGIN / SAVEPOINT のコストが分離されるため)
- BEGIN / SAVEPOINT の
sql.active_recordイベントの数・時間が、これまでより正確になります(ダブルカウントがなくなる)
互換性・注意点
- SQL を発行する順序やトランザクションの境界自体は変わっていないため、アプリケーションのロジック的な挙動は変わりません。
- ただし、メトリクスに依存してアラート閾値などを調整している場合は、以下のような変化に注意が必要です:
- 「トランザクション開始直後のクエリ」だけが常に遅いように見えていた場合、そのスパイクが解消されるため、既存のダッシュボードや閾値にズレが出る可能性があります。
- BEGIN / SAVEPOINT のイベントを明示的にフィルタしていた場合、その前提となる挙動(1つのクエリに BEGIN + 本体が含まれる)が変わるため、不要なワークアラウンド等があれば整理できます。
- lazy transaction materialization 自体の有無によって影響の有無が変わる点に注意してください。
- lazy transaction が無効な環境では、もともとこのバグは問題になりにくく、挙動の差分も小さいです。
- 参考情報 (あれば)
問題の起源とされるコミット:
https://github.com/rails/rails/commit/542f0951dddac49bf06f7da35d990db4f3829307
この時点で、トランザクション実体化と計測ロジックの順序が現在のものになり、その副作用として今回の二重カウント問題が入り込んだと推測されています。関連概念:
- lazy transaction materialization:
transactionブロックに入った時点では DB に対してBEGINを送らず、「最初に実際の SQL を流すタイミング」で初めてBEGINを送る最適化。- 不要なトランザクション開始を減らすことで、DBへの負荷やロック時間を短縮する目的で導入されている仕組みです。
- lazy transaction materialization:
このPRにより、「トランザクション境界の最適化」と「クエリ単位の計測の正確性」が両立するようになっています。
#56837 Don't filter StructuredEventSubscriber payloads
マージ日: 2026/2/18 | 作成者: @djmb
- 概要 (1-2文で)
EventReporter がイベント payload を一律に ParameterFilter でフィルタリングしていたため、SQL のクエリ名など内部メタデータまでマスクされてしまう問題を修正した PR です。EventReporter#notify/#debugにfilter:オプションを導入し、フレームワーク内部で使う StructuredEventSubscriber から送出されるイベントについてはフィルタリングを無効化するようにしています。
- 変更内容の詳細
背景問題
- 以前の変更(54264bde)で、Rails のフレームワークログ購読者(ActiveRecord の SQL ログなど)が EventReporter 経由でイベントを受け取るようになった。
- EventReporter は、payload が Hash のとき ParameterFilter を適用してから subscriber に渡している。
- その結果、例えば以下のような設定があると:
config.filter_parameters += [:name]SQL ログに含まれる クエリ名(Person Load など)が :name というキーで管理されているため、以下のようにマスクされてしまう:
# 現状の問題のある出力
[FILTERED] (0.4ms) SELECT "people".* FROM "people" WHERE ...
# 本来望ましい出力
Person Load (0.4ms) SELECT "people".* FROM "people" WHERE ...アプリケーションレベルのパラメータ保護用のフィルタが、内部メタデータにまでかかってしまうのが問題。
対応方針
- EventReporter 側に「payload をフィルタするかどうか」を選択できるオプションを追加し、
- StructuredEventSubscriber(フレームワーク内部でログを整形して出す “structured event” 向けの subscriber)は、そのオプションを
falseで呼び出すことで、フィルタをバイパスする。
これにより:
- デフォルト挙動は変えず(後方互換性を維持)、
- Rails フレームワーク内部の StructuredEventSubscriber 経由のイベントだけ、filter を無効にする。
コードレベルの主な変更点
1. EventReporter に filter: オプションを導入
activesupport/lib/active_support/event_reporter.rb:
EventReporter#notify/#debugにfilter:オプションを追加。- デフォルト値は
true(既存挙動を維持)。 filter: trueの場合のみ、これまで通り ParameterFilter を適用する。
疑似コードイメージ:
def notify(event_name, payload = {}, filter: true, **options)
payload = apply_parameter_filter(payload) if filter && payload.is_a?(Hash)
# 既存の通知処理...
end
def debug(event_name, payload = {}, filter: true, **options)
payload = apply_parameter_filter(payload) if filter && payload.is_a?(Hash)
# 既存の debug 処理...
end※ 実際のコードでは既存ロジックとの統合がありますが、概念的には上記のようなイメージです。
2. StructuredEventSubscriber からは filter: false で呼び出す
activesupport/lib/active_support/structured_event_subscriber.rbactionview/lib/action_view/structured_event_subscriber.rb
- StructuredEventSubscriber が EventReporter に対してイベントを発行するメソッド(
emit_event,emit_debug_event)で、filter: falseを渡すように変更。
イメージ:
def emit_event(name, payload)
@event_reporter.notify(name, payload, filter: false)
end
def emit_debug_event(name, payload)
@event_reporter.debug(name, payload, filter: false)
endこれにより、ActiveRecord の SQL ログなど フレームワーク側の structured event は、ParameterFilter の影響を受けなくなる。
3. テストの追加
activesupport/test/event_reporter_test.rbfilter: true/filter: falseそれぞれで payload がフィルタされる/されないことのテストを追加。
activesupport/test/structured_event_subscriber_test.rbactionview/test/template/structured_event_subscriber_test.rb- StructuredEventSubscriber 経由のイベントが、ParameterFilter によって値を書き換えられないことを検証。
- 例えば「名前が
[FILTERED]にならないこと」などを確認しているはずです。
- 影響範囲・注意点
影響範囲
Rails フレームワーク内部のログ出力(特に StructuredEventSubscriber 経由)
- ActiveRecord の SQL ログ、ActionView のレンダリングログなど、StructuredEventSubscriber を経由して EventReporter が呼ばれる箇所。
- これらのログで、これまで ParameterFilter に巻き込まれて消えていたクエリ名や内部メタデータが、本来の値で表示されるようになります。
アプリケーションが直接 EventReporter を使っている場合
EventReporter#notify/#debugをアプリコード側から直接利用している場合、デフォルトでfilter: trueのままなので挙動は変わりません。- もし「フィルタされては困る内部イベント」を EventReporter で送っているなら、この PR によって
filter: falseを指定できるようになったので、制御がしやすくなります。
セキュリティとプライバシー上の注意点
- ParameterFilter は本来、ユーザ入力(リクエストパラメータなど)に含まれる機微情報をログからマスクするための仕組みです。
- この PR では StructuredEventSubscriber からのイベントに限って フィルタを無効にしています。
- これらの payload はフレームワーク内部のメタデータ(クエリ名、テンプレート名など)が主であるため、ユーザ入力が直接入るケースは通常想定していません。
- とはいえ:
- 「structured event payload にアプリケーション側でユーザ入力を混ぜている」ような、非標準な使い方をしている場合は、フィルタを通らなくなることでログに機微情報が出ないか確認すべきです。
- 一般的な Rails アプリの範囲では問題にならない想定です。
後方互換性
EventReporterのデフォルトはfilter: trueであり、既存コードの挙動は変わりません。- 挙動が変わるのは、エコシステム内で StructuredEventSubscriber を経由するフレームワークイベントと、明示的に
filter: falseを指定した場合のみです。
- 参考情報 (あれば)
- 関連コミット: フレームワークログ subscriber が EventReporter 経由になった変更
- コミットハッシュ:
54264bde(PR 説明中で参照されている)
- コミットハッシュ:
- キーワード:
ActiveSupport::EventReporterActiveSupport::StructuredEventSubscriberActionView::StructuredEventSubscriberconfig.filter_parameters
- 該当 PR: https://github.com/rails/rails/pull/56837
#55770 Introduce this_week?, this_month?, and this_year? to Date/Time
マージ日: 2026/2/18 | 作成者: @MatheusRich
- 概要 (1-2文で)
Date/Time/DateTimeに対して、現在の週・月・年に属しているかを判定するためのthis_week?/this_month?/this_year?メソッドが追加されました。today?/tomorrow?/yesterday?と同様に、現在時点を基準にした期間判定が簡潔に書けるようになります。
- 変更内容の詳細
追加されたメソッド
ActiveSupport の DateAndTime::Calculations 拡張に、以下のインスタンスメソッドが追加されています(Date, Time, DateTime で利用可能):
this_week?this_month?this_year?
いずれも「今この瞬間を基準にして、そのオブジェクトが同じ週/月/年に属しているか」を true/false で返します。
※実装は today? 系と同じく「現在時刻からの計算」を内部で行い、ActiveSupport が持つ「週の開始曜日」などの設定(config.beginning_of_week)を踏まえて現在の週を決定していると考えられます。
サンプルコード
PR の説明にあった例:
unless some_date.this_week?
link_to "See week recap", week_recap_path(some_date)
end他に想定される使い方:
# 今週に予定があるか?
events_this_week = events.select { |event| event.start_time.this_week? }
# 今月締めのタスクだけを表示
tasks_due_this_month = tasks.where('due_at IS NOT NULL').select do |task|
task.due_at.this_month?
end
# 今年の売上だけを集計
revenue_this_year = orders.select { |o| o.created_at.this_year? }.sum(&:amount)テスト (activesupport/test/core_ext/date_time_ext_test.rb) も追加されており、
- 今週・今月・今年に属する場合に
true - そうでない過去・未来の日付/時刻に対して
false
になることが検証されています。
activesupport/CHANGELOG.md にも新機能として追記済みです。
- 影響範囲・注意点
影響範囲
- ActiveSupport を利用している Rails / 非Rails プロジェクトで、新しいクエリヘルパとして利用可能になります。
- 既存コードには破壊的変更はなく、完全に後方互換な追加機能です。
- メソッド名が
this_*?なので、自前で同名メソッドを定義していた場合は衝突に注意してください。
動作の前提・注意点
- 判定基準は「**現在時刻(
Time.current相当)」**であり、テストやバッチ処理で「基準日」を固定したい場合は、travel_toなどの time helpers で時間を固定した上で呼び出すのが望ましいです。 - 「今週」の範囲は Rails の
beginning_of_week設定に依存します。例えば:config.beginning_of_week = :mondayの場合: 月〜日の範囲がthis_week?の対象config.beginning_of_week = :sundayの場合: 日〜土の範囲
このため、「会社の締め週」など独自定義を使いたいケースでは、別途カスタムロジックが必要です。
- タイムゾーンは ActiveSupport の TimeZone 設定に従って評価されるため、アプリの
config.time_zoneが重要です。
ユーザーごとにタイムゾーンが異なるアプリでは、ユーザーのタイムゾーンに切り替えたコンテキスト(Time.use_zoneなど)内で呼び出すと安全です。
- 判定基準は「**現在時刻(
- 参考情報 (あれば)
- 既存の関連メソッド:
today?,yesterday?,tomorrow?this_week?,this_month?,this_year?はこれらと同じ「現在基準の bool 判定」シリーズとして追加。
- 実装場所:
activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
→ 日時演算・期間判定のコア拡張が集約されているファイル。
#56805 Optimize calculating remote IP address
マージ日: 2026/2/18 | 作成者: @fatkodima
- 概要 (1-2文で)
ActionDispatch::RemoteIpのクライアント IP アドレス算出処理が最適化され、Rack ミドルウェアから IP を取り出すコストが大幅に削減されました。これにより、rack-attackなど IP ベースで判定するミドルウェア使用時のリクエスト処理時間・メモリアロケーションが改善します。
- 変更内容の詳細
何を最適化したか
対象クラスは ActionDispatch::RemoteIp::GetIp の IP 計算ロジックで、具体的には calculate_ip と、そこから呼ばれる first_non_proxy の実装が見直されています。
PR に記載されたベンチマークでは、calculate_ip の実行が約 3.1 倍高速化されています。
ベンチマークで使われた最適化版の calculate_ip は次のような構造になっています(ほぼ実際の変更内容と同様):
class OptimizedGetIp < ActionDispatch::RemoteIp::GetIp
def calculate_ip
# Rack サーバが設定する REMOTE_ADDR(単一値)
remote_addr = sanitize_ips(ips_from(@req.remote_addr)).last
# Client-Ip / X-Forwarded-For 由来の IP を取得し、reverse! 済み配列として扱う
client_ips = sanitize_ips(ips_from(@req.client_ip)).reverse!
forwarded_ips = sanitize_ips(@req.forwarded_for || []).reverse!
# Client-Ip と X-Forwarded-For の両方がある場合のスプーフィングチェック
should_check_ip = @check_ip && client_ips.last && forwarded_ips.last
if should_check_ip && !forwarded_ips.include?(client_ips.last)
raise IpSpoofAttackError, "IP spoofing attack?! " \
"HTTP_CLIENT_IP=#{@req.client_ip.inspect} " \
"HTTP_X_FORWARDED_FOR=#{@req.x_forwarded_for.inspect}" \
" HTTP_FORWARDED=" + @req.forwarded_for.map { "for=#{_1}" }.join(", ").inspect if @req.forwarded_for.any?
end
# IP ヘッダに関する前提:
# - X-Forwarded-For: プロキシごとの IP リスト (または空)
# - Client-Ip: 最も外側のプロキシから伝播 (または空)
# - REMOTE_ADDR: Rack にリクエストを投げてきた IP
ips = forwarded_ips + client_ips
ips.compact!
# すべての IP が信頼プロキシの場合は、最も遠い IP を返す
first_non_proxy(ips + [remote_addr]) || ips.last || remote_addr
end
def first_non_proxy(ips)
ips.find do |ip|
return unless ip
ip = IPAddr.new(ip) if @proxies.all?(IPAddr)
@proxies.none? { |proxy| proxy === ip }
end
end
end主なポイント:
reverse!の利用client_ips/forwarded_ipsをreverse!してから扱うことで、lastを取る処理と「外側に近い IP から順に見る」処理を整合的に行いつつ、余分な配列操作を減らしています。first_non_proxyの効率化
IP を先頭から順に走査し、最初に「信頼プロキシではない」IP を返すようにしています。その際、@proxiesがすべてIPAddrなら、対象 IP を事前にIPAddr.newして===で比較@proxiesをループで都度走査するが、findとうまく組み合わせて早期 return
といった形で、分岐と変換回数を抑えています。
例外 (
IpSpoofAttackError) のロジックは維持Client-IpとX-Forwarded-Forの両方があり、一致しない場合にスプーフィング攻撃を疑って例外を投げる挙動はそのままにしつつ、関連する検索 (include?) やヘッダ列挙のコストが下がるように整理されています。
ベンチマーク結果
PR 内のベンチマークでは、Rails HEAD(Ruby 4.0.0 + PRISM)環境で:
- 旧実装: 約 10,638 i/s (1回あたり ~94 μs)
- 新実装: 約 33,102 i/s (1回あたり ~30 μs)
=> 約 3.1 倍高速 になっています。
rack-attack のように各リクエストで request.ip を参照するケースでは、この差がそのままアプリのレスポンス時間・CPU 使用率削減につながります。
- 影響範囲・注意点
影響範囲
ActionDispatch::RemoteIpを通じてクライアント IP を取得するあらゆる箇所:request.ip,request.remote_iprack-attack等のミドルウェア・Rack アプリでREMOTE_ADDR,X-Forwarded-Forなどに依存する部分
- 特に、プロキシやロードバランサ越しのアクセスが多い構成で恩恵が大きいです。
互換性 / 挙動面
- IP スプーフィングチェック (
ip_spoofing_checkオプション) の有効/無効や、例外の条件は変わっていないため、論理的な挙動は従来と等価であることが意図されています。 - ただし、IP アドレスのリストの結合順・走査順を最適化しているため、
- 「どの IP が最終的に選ばれるか」という結果は従来と変わらない前提で実装されていますが、
- 独自に
TRUSTED_PROXIESを複雑に定義している場合や、かなり特殊なヘッダ構成の場合は、アップグレード時に念のためログ等で確認すると安心です。
- IP スプーフィングチェック (
パフォーマンス上の注意
- この最適化は CPU・メモリアロケーション双方に効くため、特に高トラフィック環境・多くのミドルウェアが IP を見る環境では効果が出やすいです。
- IP 計算そのものに依存した独自メトリクス(例えば「IP 変換にかかった時間」を直接計測しているようなコード)がある場合は、数値が変わることを想定してください。
- 参考情報 (あれば)
対象ファイル:
actionpack/lib/action_dispatch/middleware/remote_ip.rb
(この PR では +14/-4 行の小規模変更)関連設定:
config.action_dispatch.trusted_proxies
(内部的には@proxiesとしてfirst_non_proxyから利用)config.action_dispatch.ip_spoofing_check
関連機能:
rack-attackのRack::Attack::Request#ipは Rails のActionDispatch::Request#ipを経由しており、この最適化の直接の恩恵を受けます。
#56822 More railties test balancing
マージ日: 2026/2/17 | 作成者: @skipkayhil
- 概要 (1-2文で)
Rails の railties テストタスクにおける「テスト時間の見積もり(重み)」が調整され、CI でのテスト並列実行時のバランスが改善されています。特に、実行時間が長いconfiguration_test/console_test/sprockets_assets_test/query_logs_testの扱いが見直されています。
- 変更内容の詳細
この PR では railties/Rakefile 内の「テストごとの推定実行時間」を調整しています。Rails では CI での並列実行を効率化するため、テストファイル・グループごとに「このテストはおおよそ何秒(もしくは何単位)かかるか」という重みを Rake タスクに定義しておき、それを元にテストジョブを分割しています。
今回の主な変更点は以下です。
configuration_testとconsole_testの推定時間を「大幅に増加」- 従来: デフォルト近い小さい値(= 軽いと見なされていた)
- 変更後: 実際の計測に近い大きな値(= 重いテストとして扱う)
- 目的: これらのテストが 1 ジョブに他のテストと大量に詰め込まれないようにし、並列度を上げて全体時間を短縮する。
sprockets_assets_testとquery_logs_testに個別の推定時間を付与- これらは一貫して 30 秒以上かかるのに、今までは「デフォルト値(1)」として扱われていた。
- 今回の変更で、他の軽量テストと同列に扱われず、長時間テストとしてバランス分配されるようになる。
DB テストの見直し(コメントベースの調整)
- CI 上で「~5分」クラスの db テスト時間はあまり見られないことから、以前想定されていた「非常に遅い」状態は、おそらく「特定テストのハング」が原因だった可能性が高い、という認識で設定を見直している。
- そのため、必要以上に大きな重み付けをせず、実測を踏まえたバランスに寄せている。
コードとしては、おおよそ次のような変更が Rakefile 内で行われています(イメージ例):
# 例: もともとデフォルト扱いだったものに明示的な重みを設定
test_times = {
# 以前は:
# "test/application/configuration_test.rb" => 1,
# のようなデフォルト扱い
"test/application/configuration_test.rb" => 20, # 実際の所要時間を反映して増加
"test/application/console_test.rb" => 20,
# 新規に重み付け
"test/sprockets_assets_test.rb" => 35,
"test/query_logs_test.rb" => 35,
}※実際の数値やキー名は PR 内での定義に依存しますが、趣旨としては「長いテストの重みを大きくして、並列分割を賢くする」という調整です。
- 影響範囲・注意点
影響範囲
railtiesのテストを CI などで並列実行している環境で、テストのジョブ分割・実行時間分布が変化します。- 単一プロセスで
bundle exec rake testを流しているだけの場合、テストの中身は変わらないため実行順や時間に大きな差は出ません(トータル時間はほぼ同じ)。
期待される効果
- 「一部のジョブだけ極端に遅い」状態が緩和され、CI 全体の所要時間が平均的に短縮される可能性があります。
- 特に
configuration_test/console_test/sprockets_assets_test/query_logs_testが、単一ジョブのボトルネックになりにくくなります。
注意点
- あくまで統計的な実行時間に基づくチューニングであり、環境差や今後のテスト追加・変更によって、再びバランスが崩れる可能性はあります。
- CI で独自にテスト分割ロジックや Rake タスクを上書きしているプロジェクトの場合、この変更がすぐには効かない、もしくは想定外のジョブ構成変更を引き起こす可能性があるため、Rakefile の読み込み順やカスタマイズの有無を確認する必要があります。
- 参考情報 (あれば)
PR 本文のポイント
- CI で「DB テストが 5 分クラスで遅い」という状況は、現状ではあまり再現していない。
- 過去に「単一テストがハングして全体時間が膨らむ」問題があり、その影響で見積もりが過大になっていた可能性がある。
- 今回は、より実測に近い値に調整しつつ、特に遅いテスト(
configuration_test/console_test/sprockets_assets_test/query_logs_test)を明示的に重く扱うことで、CI のテスト分散を改善している。
関連のある設定・ドキュメント(一般的な参考)
- Rails の並列テスト・テスト分割ロジック(
Rakefile内のtestタスク定義) - CI 側での並列ジョブ・ワーカー数設定(GitHub Actions, CircleCI など)と組み合わせることで効果が最大化される。
- Rails の並列テスト・テスト分割ロジック(
#56833 Fix misspellings around load hook guard
マージ日: 2026/2/17 | 作成者: @flavorjones
- 概要 (1-2文で)
このPRは、#56201 で導入された「load hook guard」まわりの英単語の綴りミス(typo)を修正するものです。挙動の変更はなく、ドキュメントおよびコード内のコメント/メッセージレベルの修正のみです。
- 変更内容の詳細
修正された綴りミスは以下の2点です。
eary→earlyappliction→application
変更ファイルは以下の3つです。
guides/source/configuring.mdrailties/lib/rails/application/configuration.rbrailties/lib/rails/railtie.rb
内容としては、いずれも load hook guard の説明や関連コメント・メッセージなどの英単語が正しい綴りに修正されています。
具体的には例えば以下のようなイメージの修正です(※擬似コード例):
- # Run this eary in the appliction load process
+ # Run this early in the application load processもともと #56201 で導入された「load hook guard」(to_prepare などのロードフックが不適切なタイミングで動かないように守る仕組み)の周辺テキストにだけ影響しており、ロジックそのものには手が入っていません。
- 影響範囲・注意点
ランタイム挙動への影響
- コードロジックの変更はなく、動作には一切影響しません。
- テスト追加も不要と判断されており、実際に追加されていません。
開発者視点の影響
- ガイド (
guides/source/configuring.md) を読む際の表記が正しくなり、意味の取り違えの可能性が下がります。 - ファイル内のコメントやメッセージ等を検索する際に、
early/applicationという正しい綴りで検索しやすくなります。
- ガイド (
注意点
- 既存アプリケーション側のコード修正は不要です。
- 変更は完全に非互換性ゼロのメンテナンス修正と考えて問題ありません。
- 参考情報 (あれば)
- 元となった PR(load hook guard を導入したもの):
- #56201 (load hook guards の導入 PR)
- 当該 PR:
- #56833 "Fix misspellings around load hook guard"
- 関連ファイル(Rails 本体内)
railties/lib/rails/application/configuration.rbrailties/lib/rails/railtie.rbguides/source/configuring.md
#56832 Restore core_ext/benchmark.rb as a silent shim (8-1-stable)
マージ日: 2026/2/17 | 作成者: @jeremy
- 概要 (1-2文で)
Rails 8.1系で一度非推奨・実質削除されかけていたactive_support/core_ext/benchmarkを、「何もしないが存在はする」互換用シム(shim)として復活させたPRです。これにより、require "active_support/core_ext/benchmark"を行っている既存コードが壊れないようにしつつ、現時点では追加メソッドは提供しません。
- 変更内容の詳細
変更されたポイント
activesupport/lib/active_support/core_ext.rbrequire "active_support/core_ext/benchmark"の扱いが調整されています(+1/-1)。
主な意図は、「core_ext の一部として benchmark を引き続き位置付ける」ことです。
activesupport/lib/active_support/core_ext/benchmark.rb- 中身のコード(実装や deprecation のロジックなど)が削除され、実質的に「空のファイル」に近い形になりました(-5行)。
- つまり、「読み込んでも何も定義しないが、
LoadErrorは出ないファイル」として残されます。
背景
- 以前の PR で
Benchmark.msが削除され、core_ext/benchmark.rbに残っていた最後の拡張がなくなったため、このファイル自体が非推奨とされていました。 - しかし、
core_ext/benchmark.rbは「ベンチマーク関連の拡張が置かれる公式の場所」としての意味を持っており、将来的にも存続させるべきだと判断されました。 - この PR では、「今は拡張が1つもないが、将来の拡張の置き場所として残す」「既存コードが require していても壊さない」という方針で、サイレントな(何も言わない・何も定義しない)シムとして復活させています。
挙動イメージ(サンプル)
以前(拡張が存在した頃):
require "active_support/core_ext/benchmark"
# 例: Benchmark.ms が存在していた
Benchmark.ms { do_something } # => 実行時間(ms)現在(この PR 適用後):
require "active_support/core_ext/benchmark"
# 今は Benchmark.ms などの追加メソッドは定義されない
# Benchmark モジュール自体は Ruby 標準のもののみ- 影響範囲・注意点
互換性
require "active_support/core_ext/benchmark"を行っているアプリ/gemは、そのまま動作を続けられます(LoadErrorは発生しない)。- ただし、Active Support 由来の拡張メソッド(例:
Benchmark.ms)は復活していないため、それらに依存しているコードがある場合は引き続き壊れたままです。- そのようなコードは、独自実装するか、別の測定手段(
Benchmark.measureなど標準ライブラリ)に書き換える必要があります。
- そのようなコードは、独自実装するか、別の測定手段(
非推奨メッセージなど
- 「サイレントなシム」と明記されているため、ロード時に deprecation warning を出したりはしません。
- つまり、「存在はするが何もしていない」状態で、利用側にノイズを与えません。
将来拡張の場所としての意味
- 今後、新たなベンチマーク関連拡張を Active Support が提供することになった場合、このファイルに実装が追加されることが期待されます。
- ライブラリ作者としては、「ベンチマーク関連で Rails 拡張を提供するなら、このファイルが標準の置き場」というコンベンションを再確認できます。
- 参考情報 (あれば)
- この PR がバックポートしている元PR:
- 当該ファイル(8-1-stable ブランチ):
activesupport/lib/active_support/core_ext/benchmark.rb
- 関連する標準ライブラリ:
- Ruby 標準の
Benchmarkモジュール
https://docs.ruby-lang.org/ja/latest/class/Benchmark.html
- Ruby 標準の
#56831 Restore core_ext/benchmark.rb as a silent shim
マージ日: 2026/2/17 | 作成者: @jeremy
- 概要 (1-2文で)
core_ext/benchmark.rbから唯一の拡張メソッドBenchmark.msが削除された後も、「Benchmark関連の拡張を置くための場所」としてこのファイルを存続させるようにしたPRです。実質的な機能追加・削除はなく、「静かな(shim)空ファイル」として残すことで、将来の拡張や既存コードとの互換性を確保しています。
- 変更内容の詳細
変更点の概要
activesupport/lib/active_support/core_ext.rbrequire "active_support/core_ext/benchmark"のような行(正確な行はdiff参照)が 1 行だけ残され、core_ext.rbからbenchmark拡張を読み込むエントリポイントは維持されています(もしくは削除されていたのを復活させています)。
activesupport/lib/active_support/core_ext/benchmark.rb- 中身の実装(
Benchmark.msなど)は既に削除されており、このPRでさらに 5 行削除され、完全に「中身のない拡張ファイル」に近い状態になります。 - 目的は「このファイルを require しても何もせず静かに終わる shim(薄いラッパ/ダミー)」として扱うことです。
- 中身の実装(
実質的に何が起こるか
require "active_support/core_ext/benchmark"を行っても、追加されるメソッドやクラス・モジュールはありません。- ただし、ファイル自体は存在するため、以下のようなコードはエラーなく動作します:
# 以前からこう書いていたアプリでも
require "active_support/core_ext/benchmark"
# 何も拡張されないが、LoadError にはならない
# Benchmark.ms は既に存在しないため、呼ぶと NoMethodError になる:
Benchmark.ms { do_something } # => NoMethodError要するに、「ロードは成功するが何もしない」ファイルとして残す変更です。
- 影響範囲・注意点
影響範囲
require "active_support/core_ext/benchmark"に依存している既存アプリ・gem に対して、LoadErrorが発生しないようにする後方互換性のための対応です。- 一方で、
Benchmark.ms自体は既に削除済みなので、このPRでも復活していません。
注意点
- 以前のバージョンで
Benchmark.msを使っていたコードは、このPRの有無に関わらず動作しません。Benchmark.ms相当の処理が必要であれば、自前実装か別の API への置き換えが必要です。 core_ext/benchmark.rbは「今後の benchmark 関連拡張を置く場所」として残されているだけで、現時点では何の機能も提供しないことに注意してください。- 新規コードでは、
Benchmark.msを前提にせず、標準ライブラリのBenchmark.measure系のAPIか、計測用の別ライブラリ(stackprof, benchmark-ips など)を検討するべきです。
- 以前のバージョンで
- 参考情報 (あれば)
- このPRの背景:
Benchmark.msが削除されたためcore_ext/benchmark.rbも非推奨になったが、拡張の「受け皿」としてのファイルは残しておく方針に切り替えた、という説明がPR本文にあります。
- 関連しそうなキーワード:
- 「silent shim」「dummy file」「backwards-compatible require ターゲット」としての
core_ext/benchmark.rb
- 「silent shim」「dummy file」「backwards-compatible require ターゲット」としての
- 今後:
- 新たな benchmark 拡張を Rails が提供する場合は、このファイルに再び実装が追加される想定です。
#56798 Mark AutoFilteredParameters as :nodoc:
マージ日: 2026/2/17 | 作成者: @gmcgibbon
- 概要 (1-2文で)
Active Record の暗号化機能内部で使われているAutoFilteredParametersモジュールを、YARD/RDoc で公開 API ドキュメントに載せないよう:nodoc:指定にした PRです。外部利用を想定しない内部実装であることを明示し、将来的な削除に備える位置づけです。
- 変更内容の詳細
変更ファイルは1つだけです。
activerecord/lib/active_record/encryption/auto_filtered_parameters.rb
内容としては、このモジュール定義に :nodoc: を付けただけの1行変更です(+1 / -1)。実際のコードイメージは以下のようなものです(概念的な例):
module ActiveRecord
module Encryption
module AutoFilteredParameters # :nodoc:
# ここに実装があるが、公開ドキュメントには載せない
end
end
endもともと暗号化機能(ActiveRecord::Encryption)の内部で、パラメータの自動フィルタリング(ログなどからのマスク・フィルタリング)に関する処理を担っているモジュールですが、
- 公開 API としてはサポートしない内部実装
- 今後の PR(https://github.com/rails/rails/pull/55939)で削除予定
という位置づけのため、「ドキュメントに載せない」=「公式に使うべき API ではない」ことを明確にする目的で :nodoc: を付けています。
テスト・CHANGELOG の更新はこのPRでは行われていません(挙動変更がなくドキュメント上の扱いのみのため)。
- 影響範囲・注意点
ランタイム挙動への影響
- コードの実行パスやインターフェースには一切変更がないため、アプリケーションの動作には影響しません。
- 既存の暗号化機能やパラメータフィルタリングの挙動もそのままです。
ドキュメントへの影響
- API ドキュメント生成ツール(RDoc/YARDなど)で
ActiveRecord::Encryption::AutoFilteredParametersが表示されなくなります。 - これにより「これは内部実装であり、利用を前提としていない」という Rails チームの意図がより明確になります。
- API ドキュメント生成ツール(RDoc/YARDなど)で
将来的な互換性への示唆
- PR本文にある通り、別PR(#55939)でこのモジュール自体を削除する予定であることが示されています。
- もし現在、アプリケーションやライブラリで
ActiveRecord::Encryption::AutoFilteredParametersを直接参照・利用している場合は、- それは非公開APIへの依存であり、将来の Rails アップグレードで壊れる可能性が高い
- 代わりに、公開されている暗号化関連APIやフィルタリング設定(例:
filter_parameters等)を使うよう移行を検討すべき
:nodoc:付きのクラス/モジュールは基本的に「非推奨かつ将来変更・削除されうる内部API」と解釈するのが安全です。
- 参考情報 (あれば)
- このPR:
- 将来削除を予定しているPR(背景に記載):
- 関連概念:
- Rails のフィルタリング設定:
config.filter_parameters - Rails 内部APIの扱い:
:nodoc:が付いたモジュール・クラスは公開APIではないため、利用するとアップグレード時の破壊的変更リスクが高い。
- Rails のフィルタリング設定:
#56825 Fix eagerly loading ActiveModel::Attributes
マージ日: 2026/2/17 | 作成者: @skipkayhil
- 概要 (1-2文で)
ActiveModel::Attributes のオートロード設定の仕方により、Attributes が「遅延読み込み(lazy load)」されずに即座にロードされてしまっていた問題を修正した PR です。Normalization からの参照が原因で eager load されていたため、オートロード定義の場所を移動し、正しく遅延読み込みされるようにしています。
- 変更内容の詳細
問題の背景
- Active Model では
Attributesモジュールが autoload で読み込まれるようになっていました。 - しかし、
Normalizationのオートロード定義の中でAttributes定数が参照されていたため、Ruby の定数解決によりAttributesがすぐにロードされてしまい、「実質的に autoload ではなく eager load になっていた」という状況が発生していました。 - 参照先のモジュールをトップレベル側で autoload している状態で、そのモジュールからさらに別の定数を参照すると、autoload がトリガーされてしまうのが問題の根本です。
具体的な変更点
変更ファイル:
activemodel/lib/active_model.rb- ここにあった
Attributes関連の autoload 定義が削除されました(-6行)。
- ここにあった
activemodel/lib/active_model/attributes.rbAttributesモジュールの内部に autoload 定義が移動されました(+4行)。
擬似的なイメージとしては、もともと:
# activemodel/lib/active_model.rb
module ActiveModel
autoload :Attributes, "active_model/attributes"
autoload :Normalization, "active_model/normalization" # ← ここから Attributes を参照していた
endのような構造だったものが、Attributes 側に依存関係のある autoload を寄せる形に近くなっています:
# activemodel/lib/active_model/attributes.rb
module ActiveModel
module Attributes
autoload :Normalization, "active_model/attributes/normalization"
# ※実際のファイル名・ネスト構造は例です。PR の意図としては
# 「Normalization に関連する autoload を Attributes モジュール内部に移動」
# というイメージになります。
end
endポイントは「Normalization から Attributes を参照していたために Attributes が即ロードされていた構造」を、「Attributes モジュールの内部で必要な autoload を定義する」ように変えて、Ruby の autoload が本来の「遅延読み込み」として機能するようにしたことです。
- 影響範囲・注意点
主な影響範囲
- ActiveModel::Attributes を利用しているコード(特に Rails の起動時に autoload 周りをいじっているコード)に影響しますが、通常のアプリケーションコードはほぼ影響を受けません。
- 内部的な autoload の定義位置が変わっただけなので、API や public インターフェイスは変わっていません。
挙動の変化
- 以前は、
Attributesが他の autoload 定義の副作用で「Rails 起動直後など、最初に ActiveModel をロードしたタイミングですぐに読み込まれる」可能性がありました。 - この PR により、
ActiveModel::Attributesが実際に必要になるまでロードされない(真の lazy load になる)ようになります。 - その結果:
- ブート時間・メモリ消費がわずかに改善する可能性
- オートロード順序に依存していた(本来は依存してはいけない)コードがあれば、その不具合が表面化する可能性があります。
- 以前は、
カスタムパッチ・Monkey Patch への注意
activemodel/lib/active_model.rbでの autoload 定義を前提にrequire_dependencyやconst_getなどを行っている独自コードがある場合、ロードタイミングが変わることで思わぬ差異が出る可能性があります。ActiveModel::Attributesを monkey patch している場合、ファイルロード順に敏感なコードになっていないか確認すると安心です。
- 参考情報 (あれば)
- 参照 Issue / PR:
Ref #56824- もともとの報告・議論は #56824 で行われているようです(autoload が eager になってしまう問題の指摘)。
- Ruby の
autoloadの挙動:autoloadは「その定数が最初に参照されたタイミング」でファイルをロードします。- したがって、別の autoload 定数の定義時・初期化時に参照してしまうと、その時点で autoload が発火し、結果として eager load になりがちです。
- この PR はその典型的な落とし穴を避けるために、「依存関係のある定数の autoload を、その定数自身のモジュールの内部に移す」という設計上の整理を行った形です。
#56820 [ci skip] Improve API documentation for ActionDispatch::Integration::Session#process
マージ日: 2026/2/16 | 作成者: @ybiquitous
- 概要 (1-2文で)
ActionDispatch::Integration::Session#processの API ドキュメントを、読みやすく正しくレンダリングされるように微修正した PR です。動作仕様や振る舞いは変えず、テスト用リクエストメソッドのドキュメント表現のみが改善されています。
- 変更内容の詳細
対象: actionpack/lib/action_dispatch/testing/integration.rb 内の ActionDispatch::Integration::Session#process の RDoc コメント
主な変更点:
コード例が正しいコードブロックとして表示されるように修正
- これまで RDoc 上でコードとしてレンダリングされていなかった(インデントやマークアップが不完全で、ただのテキスト扱いになっていた)箇所を、適切なコードブロックとして解釈されるように整形。
- その結果、 https://api.rubyonrails.org/classes/ActionDispatch/Integration/Session.html#method-i-process でのサンプルコードが見やすく表示される。
一部の用語をコードとしてマーク
- 例えば
XMLHttpRequestなど、技術用語/シンボリックな語をバッククォートで囲み、コードフォントとして表示されるように変更。 - これにより、通常文との区別がつきやすくなり、
processの引数の意味や使い方がより明確になる。
- 例えば
ドキュメント生成手順の明示(PR 説明)
- ローカルで反映を確認するための手順が示されている:sh
bundle exec rake rdoc open doc/rdoc/classes/ActionDispatch/Integration/Session.html - 実際にはソースコメントのみの変更であり、この手順で HTML ドキュメントを再生成できることが確認されている。
- ローカルで反映を確認するための手順が示されている:
なお、コードとしてのロジック変更は一切なく、追加 4 行・削除 3 行のごく小さなコメント修正です。
- 影響範囲・注意点
実行時の挙動への影響
- なし。
ActionDispatch::Integration::Session#processの動作、インターフェース、テスト挙動に変更はありません。
- なし。
影響する利用者
- Rails の API ドキュメント(特に Integration テストで
processを直接使う開発者)を参照する人にとって、説明が読みやすく・誤解が減る形に改善されています。 - 既存のテストコードやアプリケーションコードの修正は不要です。
- Rails の API ドキュメント(特に Integration テストで
注意点
- ドキュメント生成 (
rdoc) を CI などで行っている場合も、ビルドプロセスに影響はほぼありません(コメント修正のみ)。 - 新しい書き方や非推奨事項の追加ではないため、ドキュメントの内容的意味が大きく変わることはありません。
- ドキュメント生成 (
- 参考情報 (あれば)
対象 API ドキュメント:
https://api.rubyonrails.org/classes/ActionDispatch/Integration/Session.html#method-i-process変更ファイル:
actionpack/lib/action_dispatch/testing/integration.rbローカルでのドキュメント確認手順:
shbundle exec rake rdoc open doc/rdoc/classes/ActionDispatch/Integration/Session.html
#56823 Remove unused "open-uri" require from AppBase
マージ日: 2026/2/16 | 作成者: @skipkayhil
- 概要 (1-2文で)
Rails のアプリケーションジェネレータ基底クラス (AppBase) から、実際には使われていないopen-uriのrequireが削除されました。16年前の実装の名残で、現在のコードパスでは参照されていないためクリーンアップされています。
- 変更内容の詳細
対象ファイル:
railties/lib/rails/generators/app_base.rb
変更内容は 1 行削除のみです。
削除前(イメージ):
# railties/lib/rails/generators/app_base.rb
require "rails/generators"
require "open-uri" # ← この行が削除対象
# ... ほかの require など削除後:
# railties/lib/rails/generators/app_base.rb
require "rails/generators"
# ... ほかの require など背景となる経緯:
- 16年前のコミット([785493f...][1])で、
Kernel#openを使っており、そのためにopen-uriが必要だった。 - その後、
--builderオプションの削除に伴い、そのopen呼び出し自体が別のコミット([bce6c...][2])で削除された。 - 現在は Rails 本体側で
open-uriを直接使っている箇所はなく、Thor 内部で必要なときに個別にrequireしている。 - したがって
AppBaseでのrequire "open-uri"は完全に未使用となっており、今回の PR で削除された。
コード的な意味合い:
- 「アプリケーションジェネレータの基底クラスが、Ruby 標準ライブラリの
open-uriを先行ロードしていたが、その依存はすでに存在しないため削除した」というだけの、純粋な不要コード削除です。
- 影響範囲・注意点
影響範囲:
- Rails のジェネレータ (
rails newや各種 generator) を実行する際に、AppBase経由で自動的にopen-uriが読み込まれなくなります。 - ただし、以下の理由から通常のアプリケーション・ライブラリにはほぼ影響ありません。
AppBase自体は「ジェネレータ用のベースクラス」であり、ランタイムのアプリケーションコードから直接使うものではない。- 現在の Rails コードベースで
open-uriが前提になっている箇所は存在しない。 - Thor 側で必要な箇所は Thor 自身が
require "open-uri"している。
注意点(想定されるレアケース):
もし「Rails のジェネレータ環境に
open-uriがすでにロードされていること」を暗黙に当てにしている自作ジェネレータやスクリプトがあった場合、今回の変更以降は自前でrequire "open-uri"が必要になります。例: これまでたまたま動いていたコード
ruby# my_custom_generator.rb class MyCustomGenerator < Rails::Generators::Base def download_something # open-uri を require していないのに、たまたま動いていたパターン URI.open("https://example.com") do |f| # ... end end end変更後に確実に動かすには:
rubyrequire "open-uri" class MyCustomGenerator < Rails::Generators::Base def download_something URI.open("https://example.com") do |f| # ... end end end通常の Rails アプリケーションコード(
app/models,app/controllersなど)にはこの変更の影響はありません。
- 参考情報 (あれば)
- PR: https://github.com/rails/rails/pull/56823
- 関連 Issue/PR: https://github.com/rails/rails/pull/56824
- 元となった 16 年前のコミット(
Kernel#openを使っていた頃):
https://github.com/rails/rails/commit/785493ffed41abcca0686bf05b0a0157e0844d47 --builder削除に伴うopen呼び出しの削除コミット:
https://github.com/rails/rails/commit/bce6cbdeabfe2d8fcfae0cee8a03f0521f14c84e
#56803 Fix in_batches use_ranges with limit producing negative LIMIT
マージ日: 2026/2/16 | 作成者: @daffo
- 概要 (1-2文で)
in_batches(use_ranges: true)を、すでに.limitが付いた Relation に対して使った際に、バッチサイズと limit が割り切れないとLIMIT must not be negativeエラーになる不具合を修正する PR です。remaining計算に使う内部カウンタが更新されず負の LIMIT が生成されていたのを、Relation のlimitを変えるタイミングで内部のbatch_limitも同期させることで防いでいます。
- 変更内容の詳細
問題の挙動
前提: ActiveRecord の in_batches を use_ranges: true で使っていて、さらに元の Relation に .limit が付いているケースです。
# DB に Post が 11件ある状態
Post.limit(5).in_batches(of: 3, use_ranges: true) { |batch| batch.count }
# => "LIMIT must not be negative" エラーuse_ranges: true のコードパスでは、次のようなロジックでバッチ処理が行われています(擬似コード):
remaining = total_limit # ex: 5
batch_limit = of # ex: 3
while remaining > 0
# 残り件数がバッチサイズより少ないときだけ limit を絞る
if remaining < batch_limit
relation = relation.limit(remaining)
# ❌ batch_limit は更新しない(元の 3 のまま)
end
# ... 実際にレコードを取得し values_size の値が batch_limit 由来になる ...
remaining -= values_size # => ここが batch_limit をベースに減算される
endバグの本質は以下です:
values_sizeに、実際に取得した件数ではなく「バッチサイズ (batch_limit)」由来の値が使われる。remaining < batch_limitのときは Relation のlimitだけremainingに合わせて縮めるが、batch_limitは元の値のまま。- 次のループで
remaining -= values_sizeが「古いbatch_limit」分だけ減らすため、remainingがマイナスになる。 - 結果として、
relation.limit(remaining)がrelation.limit(-1)となり、DB からLIMIT must not be negativeが投げられる。
修正内容
activerecord/lib/active_record/relation/batches.rb の use_ranges パスで、remaining < batch_limit となったときに Relation の limit を更新するだけでなく、内部で使っている batch_limit も remaining に合わせて更新するように変更しています。
イメージとしては:
if remaining < batch_limit
relation = relation.limit(remaining)
batch_limit = remaining # ✅ Relation の limit にあわせて内部値も更新
endこれにより、次のように動作します:
- 最後のバッチだけ小さい件数になっても、
values_size/remainingの計算が実際の件数と整合する。 remainingから引く量も正しく「実際に処理した件数分」だけになるため、remainingは 0 で止まり、負数にならない。- 結果として、
relation.limitに負の値が渡されることがなくなる。
テスト追加
activerecord/test/cases/batches_test.rb に 1 テストが追加されています(+12行)。内容としては:
.limitを付けた Relation に対してin_batches(of: X, use_ranges: true)を呼ぶ。- limit がバッチサイズで割り切れない状況を作る(例: limit: 5, of: 3)。
- その実行が例外を発生させないことを確認するテスト。
これにより、今回修正したパスが今後 regress しないように担保しています。
- 影響範囲・注意点
- 影響範囲:
- ActiveRecord の
Relation#in_batchesをuse_ranges: trueオプション付きで利用しており、かつその Relation もともとに.limitが付いているケースに限定されます。 .limitがバッチサイズの倍数になっていない場合(例:limit(5).in_batches(of: 3))に、これまで発生していた DB エラーが解消されます。use_ranges: false(デフォルトパス)には影響しません。
- ActiveRecord の
- 既存コードへの影響:
- これまで例外が出ていたケースが正常に最後のバッチまで処理されるようになるだけで、バッチの中身(処理されるレコードの順序・件数)は直感どおりの挙動です。
- 「最後のバッチが小さくなる」(例: 3, 3, 2件のような形)というのは従来想定されている動作であり、その仕様が今回明示的に正しくサポートされるようになった形です。
- 注意点:
- DB レベルでは
LIMITに負の値が禁止されている DB(PostgreSQL など)で顕在化していたバグですが、他のアダプタでも同様の不整合が避けられるようになっています。 - バッチ処理のロジック自体(範囲の刻み方・where 条件など)は変えておらず、あくまでカウンタ値の整合性を取るだけの小さな修正です。
- DB レベルでは
- 参考情報 (あれば)
- 対応する Issue: #56819 —
in_batches(use_ranges: true)+.limit組み合わせ時のLIMIT must not be negativeエラー報告 - 変更ファイル:
activerecord/lib/active_record/relation/batches.rb(+2/-2)activerecord/test/cases/batches_test.rb(+12/-0)
- テスト状況:
116 runs, 836 assertions, 0 failures, 0 errors
#56750 ActionController::UnknownHttpMethod returns 500 instead of 405 in Rails 7.2 #56740
マージ日: 2026/2/16 | 作成者: @nicolasva
- 概要 (1-2文で)
Rails 7.2 で、無効な HTTP メソッドを受け取った際に本来は 405 を返すべきところが、場合によって 500 になってしまう不具合を修正した PRです。DebugExceptionsミドルウェア内部でUnknownHttpMethodが二重に発生して rescue されず、上位まで伝播してしまう問題を抑止しています。
- 変更内容の詳細
問題の流れ
無効な HTTP メソッドのリクエストが来たときに起きていたこと:
- ルータが
request.request_methodを呼ぶ - ここで
ActionController::UnknownHttpMethodが発生 DebugExceptionsミドルウェアがこの例外を rescueDebugExceptions#render_exception内でrequest.head?を呼ぶ- この
request.head?が「新しい request オブジェクト」を経由して再度 HTTP メソッドを解決しようとする - その結果、再び
UnknownHttpMethodが発生
- この
- 2回目の
UnknownHttpMethodは既存の rescue 範囲外で発生するため、DebugExceptionsで捕まえられずに外へ漏れる - それをさらに上位の
ShowExceptionsが受け止めるが、show_exceptions: :noneのような設定(テスト環境など)の場合は最終的にアプリケーションサーバまで到達し、500 Internal Server Error になってしまう
本来は UnknownHttpMethod が発生した場合、Rack レベルで 405 Method Not Allowed を返すべきところ、例外がミドルウェア処理中に再度発生して適切に rescue されず、500 になっていたのが問題です。
修正方針
DebugExceptions#render_exception 内の request.head? 呼び出しを UnknownHttpMethod から保護するように変更しています。
同メソッド内では既に InvalidType に対して似た形の保護がされており、その扱いに合わせる形です。
擬似コードイメージ(実際の変更は1行の条件/ブロック差し替えレベル):
def render_exception(env, exception)
request = ActionDispatch::Request.new(env)
# 既にある InvalidType 保護と同様に UnknownHttpMethod を保護
allow_head = begin
request.head?
rescue ActionController::UnknownHttpMethod
false
end
if allow_head
# HEAD の場合の処理 …
else
# 通常のエラーレスポンス生成 …
end
endこれにより、request.head? を呼んだ際に HTTP メソッド解析で UnknownHttpMethod が発生しても、そこで握りつぶして「HEAD ではないリクエスト」として扱うようになります。結果として、UnknownHttpMethod のハンドリングが意図どおり Rack 側に委ねられ、405 が返されるようになります。
テストの変更
actionpack/test/dispatch/debug_exceptions_test.rb が修正されています(+4/-4)。
内容としては:
- 無効な HTTP メソッドに対して、どの環境設定でも 500 ではなく 405 が返ることを確認するテストに合わせて期待値を修正
show_exceptionsの設定や環境(開発 / 本番 / test)を変えた複数パターンで、同じ挙動(405)が得られることを確認
PR 説明に記載されているテストシナリオ:
| テスト | シナリオ | 結果 |
|---|---|---|
| 1 | INVALID_METHOD + show_exceptions: :all + dev | 405 |
| 2 | 正常な GET | 200 |
| 3 | INVALID_METHOD + production mode | 405 |
| 4 | INVALID_METHOD + show_exceptions: :rescuable | 405 |
- 影響範囲・注意点
- 対象: Rails 7.2 系で
ActionDispatch::DebugExceptionsミドルウェアを使っているアプリ全般 - 影響するケース:
- クライアントが
FOO /path HTTP/1.1のような、Rails が解釈できない HTTP メソッドでリクエストしてきた場合 - 以前は一部の設定(
show_exceptions: :noneなど)で 500 を返していたが、今後は一貫して 405 を返すようになる
- クライアントが
- 互換性:
- 正常なメソッド(GET/POST/PUT/DELETE/HEAD 等)についての挙動は変わりません
- すでに「無効メソッドで 500 が返ること」を前提にしていた独自の監視・テストがある場合は、ステータスコード 405 に変更された影響を考慮する必要があります
- セキュリティ/情報漏洩面:
- 500 → 405 への変更は、内部例外を表に出しにくくする意味でも妥当であり、一般的には望ましい改善と考えられます
- 参考情報 (あれば)
- 対応している Issue: #56740 – ActionController::UnknownHttpMethod returns 500 instead of 405 in Rails 7.2
- 修正対象ミドルウェア:
ActionDispatch::DebugExceptions - 関連しうる設定項目:
config.action_dispatch.show_exceptions(:all,:rescuable,:noneなど)
- 類似の例外保護ロジック: 同ファイル内の
InvalidTypeに対する rescue 処理(今回の修正の参考実装となっている)
#56806 [ci skip] Add documentation: deprecations and notable changes to 8.2 release notes
マージ日: 2026/2/16 | 作成者: @FKauwe
- 概要 (1-2文で)
Rails 8.2 のリリースノートに、各コンポーネントの非推奨・削除項目と「notable changes(重要な変更)」を追記したドキュメント更新のPRです。コード自体は一切変更せず、CHANGELOG をもとにリリースノートを整理・明文化したものです。
- 変更内容の詳細
変更ファイルは guides/source/8_2_release_notes.md のみで、8.2 リリースノートに以下の情報が追加されています。
全体像
- 対象コンポーネント: Railties / Action Pack / Action View / Active Record / Active Storage / Active Model / Active Support / Active Job / Action Text など
- 合計:
- Removals(削除): 1件
- Deprecations(非推奨): 9件
- Notable changes(注目すべき変更): 14件
それぞれ CHANGELOG の内容をまとめ、既存の 8.1 以前のリリースノートと同じ書き方・構成で追記しています。
コンポーネント別の主な追記内容
Railties
Notable changes:
Rails.appエイリアスRails.applicationへのより短いアクセス方法としてRails.appが追加されたことが「注目すべき変更」として記載されます。rubyRails.app # Rails.application の別名 Rails.app.revision # デプロイリビジョンなど Rails.app.creds # 認証情報へのショートカットRails.app.revision
アプリケーションの revision 情報(Git SHA やバージョン)を取得するためのAPIが追加されたことが明文化されています。Rails.app.credsRails.application.credentialsへの短縮アクセスができることが記載されます。
Action Pack
Deprecations(非推奨):
protect_from_forgeryの strategy 省略protect_from_forgeryを「戦略(strategy)」を指定せずに使う形が非推奨になったことが明記されます。ruby# 非推奨(strategy 省略) protect_from_forgery # 推奨(明示的に strategy を指定) protect_from_forgery with: :exception # または header-based な方式に移行、などInvalidAuthenticityTokenの非推奨
従来の CSRF 保護で用いられていたActionController::InvalidAuthenticityTokenまわりの扱いが非推奨方向で整理されていることが追記されています (8.2 ノートでは「非推奨になった」という事実と背景が文章化されているはずです)。
Notable changes:
- 「モダンなヘッダーベースの CSRF 保護」
Rails 8.2 で導入/強化された、クッキーではなくヘッダーを主体とした CSRF 対策についての記述が追加されています。
旧来の authenticity_token に依存しない、SPA/JSON API と相性の良い仕組みへの移行を促す内容になっているはずです。
Action View
Notable changes:
コレクションレンダリングのブロック対応
render collection:とブロックを組み合わせて使えるようになった変更が「注目すべき変更」として追記されています。想定される利用例(イメージ):
erb<%= render collection: @products do |product| %> <div class="product"> <h2><%= product.name %></h2> <p><%= product.description %></p> </div> <% end %>これにより、パーシャルファイルを増やさずに柔軟なループ描画がしやすくなった点がポイントです。
Active Record
Notable changes:
PostgreSQL:
DROP DATABASE ... WITH (FORCE)対応
PostgreSQL のDROP DATABASE FORCE(強制削除)を Active Record 経由で扱えるようになった変更が記述されています。
テストや CI 環境で、接続が残っている DB を強制的に削除したいケースで便利になります。SQLite3: CASCADE によるデータ損失バグの修正
外部キーのON DELETE CASCADE等を使う際に、意図しないデータ削除が起こりうる問題が修正されたことが「notable」として明記されます。
SQLite3 を使っているプロジェクトにとってはデータ整合性上の重要な変更です。implicit_persistence_transactionフック
保存処理時に暗黙的に張られるトランザクションにフックできるimplicit_persistence_transactionが追加されたことが記録されます。
高度なコールバック制御や監査用途での利用が想定されます。
Active Storage
Deprecations:
preprocessed: trueオプションの非推奨
バリアント(画像変換等)の事前処理を制御していたpreprocessed: trueの指定が非推奨になったことがリリースノートに反映されています。
Notable changes:
バリデーション前に添付ファイルを分析
ファイルのメタ情報(コンテンツタイプ、サイズ、画像の寸法など)をバリデーション前に分析する挙動が強化され、その変更点が説明されます。
これにより「画像の幅・高さをバリデーションでチェックする」といったケースが書きやすくなります。バリアントの即時処理
バリアント(サムネイル等)の生成をリクエスト内で即時実行するかどうかの挙動・設定が整理され、その変更が notable として説明されています。
従来の「初回アクセス時に遅延生成」モデルとの違いや、パフォーマンス上のトレードオフにも触れられているはずです。
Active Model
Notable changes:
has_json/has_delegated_json
JSON カラムをオブジェクト的に扱うためのマクロが追加されたことがドキュメント化されています。
これにより、JSON 内のキーを属性として扱ったり、委譲したりするパターンを簡潔に書けます。Argon2 +
register_algorithmforhas_secure_passwordhas_secure_passwordで PBKDF2/BCrypt 以外のアルゴリズムとして Argon2 をサポートし、
独自アルゴリズムをregister_algorithm経由で登録できるようになった変更が説明されています。イメージ例:
rubyhas_secure_password algorithm: :argon2
Active Support
Notable changes:
SecureRandom.base32の追加
Base32 形式のランダム文字列を生成するためのメソッドが追加されたことが記載されています。rubytoken = SecureRandom.base32 # => "Z5K2..." のような Base32 文字列並列テストの決定的(deterministic)割り当て
並列テスト実行時に、ワーカーへのテストファイル割り当てが「決定的」になる改善が notable として反映されています。
これにより、テストの実行順序が安定し、テストのフレーク原因の一部が減ることが期待されます。
Active Job
Removals:
sidekiqアダプタの削除
Active Job 標準のアダプタ一覧からsidekiqが削除されたことがリリースノートに明記されました。
Sidekiq は引き続き利用可能ですが、Active Job 側のメンテ対象からは外れた、という位置付けになります(通常は sidekiq gem 側で提供されるアダプタを利用)。
Deprecations:
以下のアダプタの非推奨:
queue_classicresquedelayed_jobbackburnersneakers
これらは将来的な削除候補であり、移行を促す内容が追記されています。
Notable changes:
- 「トランザクションコミット後に enqueue」機能の拡張
トランザクションがコミットされた後にのみジョブを enqueue する機能(以前からある機能)の説明が拡張されています。
DB ロールバックされた場合にジョブだけ実行されてしまう問題を防ぐ設計の重要性が、より明瞭に記載されます。
Action Text
Deprecations:
- Trix 固有のクラス/メソッドの非推奨
Trix エディタに強く依存した API やクラスが非推奨になったことがリリースノートに追記されています。
将来的には Trix 以外のエディタやより抽象化されたインターフェースへ移行する流れを示唆しています。
- 影響範囲・注意点
このPRは ドキュメントのみの変更 であり、Rails の挙動や API を実際に変更するものではありません。
ただし、記載されている内容自体は、8.2 で既に入っている/これから入る変更の「公式なまとめ」になるため、以下の用途で重要です。
- Rails 8.2 へアップグレードする際のチェックリスト
- Active Job アダプタの削除・非推奨
- Action Pack の CSRF 方式変更
- Active Storage, Active Record の挙動変更 など
- 「どこから何が非推奨になったか」を把握し、将来のアップグレードに備える
- 新機能(
Rails.app,has_json,SecureRandom.base32など)を素早くキャッチアップする
既に 8.2 のコードを使っている場合でも、このリリースノートを読むことで「いつの間にか振る舞いが変わっていた」「実は非推奨だった」という点に気づきやすくなります。
- 参考情報 (あれば)
- 対象ファイル:
guides/source/8_2_release_notes.md - 作成者は CHANGELOG をソースにし、過去5バージョン分のリリースノートのパターンを LLM を用いて抽出している旨が説明されています。
- ドキュメント生成コマンド:bashを用いてローカルでビルドし、8.1 のノートと比較しつつスタイルの一貫性を確認済みです。
bundle exec rake guides:generate ONLY=8_2_release_notes
#56760 Fix Ruby code block syntax highlighting for getting_started.md document [ci skip]
マージ日: 2026/2/16 | 作成者: @johnhailu
- 概要 (1-2文で)
Railsガイド「Getting Started with Rails」の Markdown 内で、Ruby のコードブロックに対するシンタックスハイライト指定が誤っていた箇所を修正した PR です。コード例自体の挙動やフレームワークの機能には影響せず、ガイドの表示品質のみを改善しています。
- 変更内容の詳細 (あればサンプルコードも含めて)
- 対象ファイル:
guides/source/getting_started.md - 行レベルの変更:
+5 / -5とあるので、1つないし複数のコードブロックに対して、言語指定やフェンスの書き方などを修正しています。
典型的には、次のような変更が考えられます(イメージ例):
-```ruby
-# some Ruby code
-```
+```ruby
+# some Ruby code
+```または、実際に起きがちなパターンとしては:
- 間違った言語名を指定していたのを修正
- 例:
```rb/```Ruby/```ruby:など →```ruby
- 例:
- バッククォートとチルダが混在していたのを統一
- 例:
~~~ruby→```ruby
- 例:
- コードフェンスの開始・終了が揃っておらず、ハイライトが崩れていた箇所を修正
- インラインの
`ruby`とコードブロックが衝突して Markdown パーサの解釈がズレるパターンを修正
PR の説明では「The previous version caused incorrect syntax highlighting in the rendered guide. Updating it fixes the highlighting for the example.」とあるため、特定のコード例で:
- 文字列・シンボル・キーワードなどが Ruby として色分けされない
- コードブロックの閉じ位置がずれて、その後ろの文章までコード扱いになってしまう
といった問題が出ていたものを、コードフェンスの言語指定や位置を直すことで正常な Ruby ハイライトになるように調整したと考えられます。
- 影響範囲・注意点
影響範囲:
- Rails 本体のコードやランタイム挙動には一切影響しません。
guides/source/getting_started.mdのみが対象であり、生成される HTML ガイドの見た目 (Ruby コードのハイライト) だけが変わります。- CI スキップ指定 (
[ci skip]) がタイトルについていることからも、テストなどは不要なドキュメント専用の変更であることが明確です。
注意点:
- 他のガイドファイルで同様の誤った言語指定やコードフェンスの問題があれば、同様の修正が必要になる可能性があります。
- ガイド執筆時には、
- コードブロックの開始/終了のバッククォート数が一致しているか
```ruby/```erb/```bashなど、言語名を正しく小文字で指定しているか- Surrounding Markdown 構文 (
list,blockquote,indentなど) によってコードフェンスのネストが壊れていないか
を確認すると、同様のハイライト崩れを防げます。
- 参考情報 (あれば)
- Rails ガイドのドキュメントコントリビュートガイド:
https://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation - 一般的な Markdown のコードブロック (fenced code block) ドキュメント:
- CommonMark: https://spec.commonmark.org/0.30/#fenced-code-blocks
ここに準拠したレンダラ (GitHub、Rails Guides など) では、```rubyのように言語名を正しく指定することで、Ruby のシンタックスハイライトが適用されます。
- CommonMark: https://spec.commonmark.org/0.30/#fenced-code-blocks
#56792 Bump MySQL Image version to 8.4
マージ日: 2026/2/16 | 作成者: @akhilgkrishnan
- 概要 (1-2文で)
このPRは、Railsアプリ生成時に使われる MySQL の Docker イメージバージョンを「最新の 8.4 系」に更新するものです。変更はconfig/deploy.ymlのテンプレート内のイメージタグを 1 行だけ差し替える、極小の更新です。
- 変更内容の詳細 (あればサンプルコードも含めて)
対象ファイル:railties/lib/rails/generators/rails/app/templates/config/deploy.yml.tt
このファイルは rails new などでアプリを作成するときに展開される、デプロイ用設定ファイル (config/deploy.yml) のテンプレートです。
その中で指定している MySQL のコンテナイメージのバージョンが次のように変更されています:
- image: mysql:8.0
+ image: mysql:8.4※ 実際の旧バージョンが 8.0 か 8.3 かなどはリポジトリの状態に依存しますが、このPRの趣旨は「MySQL イメージを最新の 8.4 に上げる」という一点です。
これにより、新しく生成される Rails アプリの config/deploy.yml では、MySQL 8.4 ベースのコンテナを使う設定になります。
- 影響範囲・注意点
影響範囲
- この変更は「新しく生成されるアプリケーションの
config/deploy.yml」にのみ影響します。 - 既存プロジェクトは、このテンプレートを再利用しない限り、自動的には変更されません。
- MySQL 8.4 を前提にした動作確認が今後の Rails の標準想定環境の一つになります。
- この変更は「新しく生成されるアプリケーションの
技術的な注意点
- MySQL 8.4 での互換性
- MySQL 8 系からは、認証方式 (caching_sha2_password など) やデフォルト設定が 5.7 以前と大きく変わっており、古いクライアントライブラリだと接続エラーになることがあります。
- Rails 側では
mysql2Gem を利用するのが一般的ですが、手元のmysql2バージョンが古いとうまく接続できない可能性があります。MySQL 8 対応版 (>= 0.5.x系など) を使うことを推奨します。
- SQL モードやデフォルトの差異
- MySQL 8.0 以降では
ONLY_FULL_GROUP_BYなどの厳格な SQL モードがデフォルトで有効なため、古い SQL クエリがエラーになる可能性があります。 - 特に手書き SQL や、サードパーティ Gem が発行するクエリで影響が出やすいので、8.4 に上げる際はテストを十分に行う必要があります。
- MySQL 8.0 以降では
- 本番環境とのバージョン整合性
- テンプレートに従って開発・ステージング環境を MySQL 8.4 で構築する場合、本番 DB も 8.4 付近の互換バージョンに揃えるのが望ましいです。
- もし本番が 5.7 や 8.0 のままの場合は、機能差・デフォルト設定差による挙動の違いに注意が必要です。
- MySQL 8.4 での互換性
実務での対応例
- 既存プロジェクトにこの変更を取り込む場合は:
config/deploy.ymlの MySQL イメージをmysql:8.4に変更- ローカル/CI でテストを走らせ、クエリエラーや接続エラーがないか確認
- 問題があれば
mysql2の更新や SQL の修正、必要に応じて MySQL の設定調整 (SQL_MODE など) を行う
- 既存プロジェクトにこの変更を取り込む場合は:
- 参考情報 (あれば)
- MySQL 8.4 (LTS) の位置づけ:
- MySQL 8.4 は LTS (Long Term Support) リリースとして提供されており、長期サポートが期待できるバージョンです。そのため、Rails のテンプレートもこれに追随したと考えられます。
- 関連しうるドキュメント:
- MySQL 8.4 リファレンスマニュアル(公式)
mysql2Gem の README(MySQL 8 対応状況・必要バージョンなど)- Rails ガイド: 「Active Record とデータベースとの接続」周辺 (MySQL 接続設定)
#56816 Fix Column#hash to use the correct instance variable
マージ日: 2026/2/16 | 作成者: @kamipo
- 概要 (1-2文で)
PostgreSQL・SQLite3 用のActiveRecord::ConnectionAdapters::Columnクラスにおいて、#hashメソッドが誤ったインスタンス変数を参照していた問題を修正する PR です。これにより、Columnオブジェクトのハッシュ値が意図した属性に基づいて正しく計算されるようになります。
- 変更内容の詳細(あればサンプルコードも含めて)
何を直したか
- 対象ファイル:
activerecord/lib/active_record/connection_adapters/postgresql/column.rbactiverecord/lib/active_record/connection_adapters/sqlite3/column.rb
- 内容:
Column#hashメソッド内で使用しているインスタンス変数が誤っており、正しいインスタンス変数に置き換えられました。- 変更行数は各ファイル 1 行 (合計 +2/-2) と非常に小さい修正です。
元 PR (#56801) で Column#hash 周りに手が入った結果、例えば以下のようなコードのどこかにバグが入り、それを修正した follow-up という位置づけです:
# イメージ(実際のコード例ではなく構造イメージ)
def hash
# 本来は @sql_type や @cast_type を使うべきところで
# 間違ったインスタンス変数を参照していた (例: @type など)
[name, sql_type, default, null].hash
endこの PR では、その「間違ったインスタンス変数」を、実際に Column が内部で保持している正しいインスタンス変数に置き換えています。
結果として、hash が eql? / == の判定基準と整合したハッシュ値を返すようになります。
※実際の変数名は PR 本体コードに依存しますが、趣旨としては「Column の属性表現として正しいインスタンス変数を使うようにした」という修正です。
どういう場面で Column#hash が使われるか
Ruby の hash メソッドは以下のような場面で利用されます:
HashのキーとしてColumnオブジェクトを使う場合SetにColumnオブジェクトを入れる場合- 内部的に
hashとeql?を用いるデータ構造でColumnを扱う場合
例:
columns = ActiveRecord::Base.connection.columns(:users)
set = Set.new(columns)
# -> Column#hash が正しく動いていないと、
# 同値なカラムが重複したり、逆に区別されなかったりする可能性があるこの PR によって、同値性判定 (== / eql?) と hash の整合性が取れ、上記のような使用パターンでの不整合が防がれます。
- 影響範囲・注意点
- 対象 DB アダプタ:
- PostgreSQL
- SQLite3
- 影響が出る可能性があるケース:
ActiveRecord::ConnectionAdapters::ColumnオブジェクトをHashのキー、Setの要素、- または類似のハッシュベースの構造で利用している場合。
- この修正により、以前と比べて「同じカラムだとみなされるオブジェクト」の集合が変化する可能性があります。
- 具体的には、以前は「たまたま間違ったインスタンス変数に基づくハッシュ値」で区別・集約されていた場合、それが正しいカラム属性に基づく挙動に変わります。
- 一般的なアプリケーションコードでは、
Columnオブジェクトを直接キーにして使うことは少ないため、影響は限定的と思われます。 - ただし、スキーマ情報をキャッシュするライブラリやメタプログラミング系のコードで
Columnオブジェクトをキーにしている場合は、キャッシュキーの変化などに注意してください。
- 参考情報 (あれば)
- 該当 PR: https://github.com/rails/rails/pull/56816
- 元となった PR (follow up 先): https://github.com/rails/rails/pull/56801
Column#hashや関連する内部実装が変更された際のフォローアップであり、Columnの等価性・ハッシュ値の整合性を取るための仕上げ的な修正と考えられます。
#56802 Fix SQLite3 column equality for rowid aliases
マージ日: 2026/2/16 | 作成者: @afurm
- 概要 (1-2文で)
SQLite3 のカラムオブジェクト同士の比較 (ActiveRecord::ConnectionAdapters::SQLite3::Column#==) において、rowidの扱いがhashと不整合だったバグを修正し、rowidの違いをきちんと等価性判定に含めるようにした PR です。これにより、rowidセマンティクスが異なるカラム同士が誤って「同じカラム」とみなされる問題が解消されます。
- 変更内容の詳細
問題の背景
- SQLite3 では、内部的に
rowidという行識別子があり、idのようなカラム名でrowidのエイリアスになっている場合など、rowidに関するメタ情報をカラムが持つことがあります。 ActiveRecord::ConnectionAdapters::SQLite3::Columnには==とhashが実装されていますが、hashはrowidを含めて計算している一方で、==はrowidを比較していませんでした。- Ruby のオブジェクト等価性の原則として、「
a == bならばa.hash == b.hashであるべき」ですが、ここでは逆のケースが起きうる状態でした:col1.rowid != col2.rowidcol1 == col2はtrue(rowidを見ていない)col1.hash != col2.hash(rowidを見ている)
- この不整合のせいで、たとえば
SetやHashのキーとしてカラムオブジェクトを使った際に、- 「等しいとみなされるのにハッシュが違う」か
- 「ハッシュは同じだが
==では等しくない」 などの不正動作を引き起こす可能性があり、スキーマメタデータの重複排除や比較ロジックに影響します。
具体的な変更点
ActiveRecord::ConnectionAdapters::SQLite3::Column#==にrowid比較を追加元々のコードは概ね以下のようなイメージでした(擬似コード):
rubydef ==(other) other.is_a?(self.class) && name == other.name && sql_type == other.sql_type && # ... ほかの属性 ... endこの PR で、ここに
rowidの比較が追加されます:rubydef ==(other) other.is_a?(self.class) && name == other.name && sql_type == other.sql_type && # ... ほかの属性 ... rowid == other.rowid end※実際のコードはこれに近い形で 1 行の比較追加が入っているだけです。
回帰テストの追加
activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rbに以下のようなテストが追加されています(概要):rubydef test_rowid_changes_column_equality # 同じ設定のカラムだが、rowid フラグだけが異なる 2 つの Column を生成 column1 = ActiveRecord::ConnectionAdapters::SQLite3::Column.new( "id", nil, "integer", **options, rowid: true ) column2 = ActiveRecord::ConnectionAdapters::SQLite3::Column.new( "id", nil, "integer", **options, rowid: false ) # 以前は true になってしまっていたが、修正後は false になる assert_not_equal column1, column2 endこのテストにより、「
rowidだけが違うカラムオブジェクトは==で等しくない」と明示的にチェックしています。テスト実行状況
- 対象テストファイルのみを SQLite3 で実行:
ARCONN=sqlite3 bundle exec ruby -Itest test/cases/adapters/sqlite3/sqlite3_adapter_test.rb- 結果:
94 runs, 237 assertions, 0 failures, 0 errors, 0 skips
- 既存挙動の破壊は(少なくとも SQLite3 テストスイート範囲では)発生していないことが確認されています。
- 影響範囲・注意点
影響範囲
- 対象:
ActiveRecord::ConnectionAdapters::SQLite3::Columnを直接扱うコード、またはカラムオブジェクトをSetHashのキー- 配列での
uniqなどで重複排除・比較に使っている箇所。
具体的な影響:
rowidの違いを無視してカラムを「同一」とみなしていたコードが、今後は別物として扱われる例:
- これまでは「
rowid: trueなidカラム」と「ただのintegerカラムのid」を同じとみなしていたロジックがあると、今後は==でfalseになります。 - スキーマ比較やマイグレーション生成系ツールで、「意図せずマージされてしまっていたカラム差分」が、正しく「差分あり」と検出される可能性があります。
- これまでは「
hashと==の整合性が取れるようになる- これにより、
SetやHashを使うロジックでのバグの温床が一つ取り除かれます。 - もし現在、
Set内の SQLite3 カラムオブジェクトが「重複してしまう」「期待通りに dedup されない」などの現象があれば、この修正で直る可能性があります。
- これにより、
互換性・注意点
- あくまで「バグ修正」という位置づけであり、CHANGELOG には追加されていません。
- しかし挙動としては「今まで等しいと判定されていた 2 つの Column が、等しくなくなる」方向の変更なので、
- スキーマメタ情報を読み取り、独自に等価性に依存しているライブラリやツール
- 特に SQLite3 に特化したスキーマ比較・同期ツール では、影響がないか念のため確認した方が安全です。
- アプリケーションレベルで ActiveRecord の Column オブジェクトを直接比較することはあまり一般的ではないため、通常の Rails アプリへの影響は限定的と考えられます。
- 参考情報 (あれば)
- PR: https://github.com/rails/rails/pull/56802
- 関連クラス:
ActiveRecord::ConnectionAdapters::SQLite3::Column
- Ruby における等価性の原則(
==とhashの整合性)について:Object#hashのドキュメント: https://docs.ruby-lang.org/ja/latest/method/Object/i/hash.html
「eql?がtrueを返すオブジェクト同士は同じhashを返さなければならない」 というルールがあり、今回の修正はこの原則に沿う形になっています。
#56717 Skip ROLLBACK statement following TransactionRollbackError
マージ日: 2026/2/16 | 作成者: @sorah
- 概要 (1-2文で)
COMMIT時にTransactionRollbackError(例:SerializationFailure)が発生した場合に、その直後の不要なROLLBACKを発行しないようにし、PostgreSQL/libpq からの「WARNING: there is no transaction in progress」ログが出ないようにした PR です。これにより、Aurora DSQL や SERIALIZABLE トランザクション使用時でも、ログ抑制に頼らずにクリーンなログ運用がしやすくなります。
- 変更内容の詳細
何が問題だったか
COMMIT実行時にSerializationFailureなどのTransactionRollbackErrorが発生するケースがあります(Aurora DSQL や PostgreSQL のSERIALIZABLEなどで顕著)。- こうした DB は、
COMMITで競合検出時に「トランザクションを自動ロールバックしたうえでエラーを返す」挙動をとります。 - しかし Active Record 側では、
COMMITでTransactionRollbackErrorが起きたあとに「通常のエラー時ロールバック」と同じ流れでROLLBACKを投げてしまっていました。 - すでに DB 側でトランザクションは終了しているため、
ROLLBACKに対して PostgreSQL/libpq がWARNING: there is no transaction in progress
を stderr に直接吐きます。 - これまではテストでは
client_min_messagesで警告を抑制して誤魔化していましたが、本番環境でのログ汚染を防ぐには根本的にROLLBACK自体を出さないほうがよい、という判断です。
今回の修正の要点
COMMIT実行部分でTransactionRollbackErrorを明示的に rescue し、「トランザクションはすでに DB 側でロールバック済み」という前提で、Active Record 内部状態だけを「ロールバック済み」に整えるようにしました。- 具体的には:
- 例外を捕まえたら、そのトランザクション(
RealTransaction)を「無効化(invalidate)」し、 - 以降の「通常のロールバック処理フロー」は通すものの、「DB に対して
ROLLBACKを投げる部分」だけは無効化された状態により実行されないようにしています。
- 例外を捕まえたら、そのトランザクション(
- 既存の invalidation ロジックをそのまま流用できなかった理由:
- 以前の変更(
6a8a90ea...)により、COMMITを発行する前に対象トランザクションは@stateから pop 済みになっており、 current_transactionはすでにNullTransactionになっているため、そこから「今のトランザクションを無効化する」ことができませんでした。- この PR では、その制約を踏まえた上で、
COMMIT例外発生タイミングで適切に invalidation できる処理を追加しています。
- 以前の変更(
テストまわりの変更
- これまでは PostgreSQL アダプタのトランザクションテストで、
client_min_messagesを変更して libpq の WARNING を抑制していましたが、根本原因が解消されたためその抑制コードを削除しました。 SerializationFailure/TransactionRollbackErrorが発生するシナリオで、不要なROLLBACKが発行されないこと、かつ Active Record のトランザクション状態が正しく更新されることを確認するテストが整理・追加されています(行数としてはテストの削除行 > 追加行になっており、抑制やワークアラウンド寄りのコードがスリムになった形です)。
※ PR 本文からは完全なメソッドシグネチャは読み取れませんが、イメージとしては以下のようなパターンの変更です(あくまで擬似コード):
def commit_db_transaction
begin
@connection.execute("COMMIT")
rescue ActiveRecord::TransactionRollbackError => e
# このトランザクションは DB 側ではすでにロールバックされている
real_txn.invalidate! # 内部状態だけ「ロールバック済み」にする
# 通常のロールバックフローに乗せるが、
# invalidate によって実際の "ROLLBACK" SQL は送られない
handle_transaction_rollback(e)
end
end- 影響範囲・注意点
- 対象:
- PostgreSQL 系アダプタ(特に Aurora DSQL など
SerializationFailureが頻発しうる環境)。 SERIALIZABLEレベルなどでトランザクションを多用し、同時更新競合が起こりやすいワークロード。
- PostgreSQL 系アダプタ(特に Aurora DSQL など
- 動作上の意味:
- DB の実際の振る舞いは変わりません。もともと
COMMIT時に DB がロールバックしていたケースにおいて、「二重にROLLBACKしに行こうとして怒られていたのをやめる」だけです。 - アプリ側から見た「トランザクションが
TransactionRollbackErrorで失敗する」というセマンティクスは従来通りで、リトライ戦略やエラーハンドリングコードに変更は不要です。
- DB の実際の振る舞いは変わりません。もともと
- ログへの影響:
- これまで stderr(libpq 直)に出ていた
WARNING: there is no transaction in progressが出なくなります。 - ログ監視やアラート設定で、この WARNING をトリガーに何かしていた場合は、今後そのシグナルが消えることに注意が必要です。
- これまで stderr(libpq 直)に出ていた
- 後方互換性:
- 実トランザクションの完了タイミングやエラー種別は変えていないため、通常のアプリケーションコードへの互換性の影響はほぼありません。
- Rails 内部のテストが log suppression に依存していない=ワークアラウンドが減ったので、将来的なトランザクションまわりの保守性は向上します。
- 参考情報 (あれば)
- PR 本文で触れられている関連コミット:
- トランザクションが
@stateから pop される挙動変更:6a8a90ea39527f1c78793698e36e430e6014b3c6
- トランザクションが
- Amazon Aurora DSQL の楽観的同時実行制御仕様:
https://docs.aws.amazon.com/aurora-dsql/latest/userguide/working-with-concurrency-control.html - この変更は
activerecord/CHANGELOG.mdにも記載されており、「TransactionRollbackError発生時に不要なROLLBACKを飛ばさない」という挙動変更として公式にドキュメント化されています。
#56813 Fix small typo for an active record load hook
マージ日: 2026/2/16 | 作成者: @chaadow
- 概要 (1-2文で)
Active Record の load hook 名のタイポ(スペルミス)をテストコード内で修正した PR です。
本体機能ではなく、railtiesの設定テスト(configuration_test.rb)のみが 1 行だけ修正されています。
- 変更内容の詳細
※ 実際の diff は 1 行の微修正のみで、ActiveRecord の load hook 名の誤記を正しい名前に直した内容です。
PR タイトルの「an active record load hook」という表現と、変更対象が railties/test/application/configuration_test.rb であることから、以下のようなテストコードが修正されたと考えられます。
典型的には Rails では、以下のように ActiveSupport.on_load で Active Record のフックを設定します。
ActiveSupport.on_load(:active_record) do
# ここに Active Record ロード後の初期化処理を書く
endこの PR で行われたのは、例えば次のようなテストコードの一部:
ActiveSupport.on_load(:activerecord) do
# ...
endのように シンボル名が誤っている箇所 を、
ActiveSupport.on_load(:active_record) do
# ...
endのように 正しい load hook 名 に直す、といった 1 行の修正です。
(実際の before/after の具体的なスペルは PR タイトルからは読み取れませんが、active_record 系の load hook 名に対する軽微な typo 修正であることは確実です。)
つまり、load hook のシンボル名が正しくないためにテストが意図どおりの hook を検証できていなかった状態を修正した PR です。
- 影響範囲・注意点
影響範囲
- 変更は
railties/test/application/configuration_test.rbのみで、本体コード(activerecordやアプリケーションコード)には一切変更がありません。 - したがって、Rails を利用する既存アプリケーションの挙動には影響しません。
- 影響は Rails のテストスイート(内部テスト)のみ です。
- 変更は
注意点
- この PR 自体は typo 修正のみですが、Rails 内部で正式に使われている load hook 名が「どれか」を確認したい場合、
ActiveSupport.on_loadの利用箇所やドキュメントと整合していることが改めて確認された形になります。 - アプリ側で
ActiveSupport.on_loadを使っている場合も、シンボル名の typo をするとフックが発火しない(サイレントに失敗する)ため、今回のようなタイプミスに注意が必要です。
例: 正::active_record/ 誤::actve_record,:activerecordなど。
- この PR 自体は typo 修正のみですが、Rails 内部で正式に使われている load hook 名が「どれか」を確認したい場合、
- 参考情報 (あれば)
ActiveSupport.on_loadの代表的な hook 名::active_record:action_controller:action_mailer:action_view:active_job
Rails での利用例(アプリ側で initializer に書く場合):
ruby# config/initializers/active_record_extension.rb ActiveSupport.on_load(:active_record) do # Active Record がロードされた後に実行したい処理 include MyActiveRecordExtension end
この PR は、上のような hook 名の「正しさ」を前提としたテストコードでの typo を直し、テストの信頼性を高めるための非常に小さな修正です。
#56790 Skip test_via_to_sql_with_complicating_connection for PostgreSQL 19+
マージ日: 2026/2/15 | 作成者: @yahonda
- 概要 (1-2文で)
PostgreSQL 19以降ではstandard_conforming_strings = OFFを設定できなくなったため、それに依存しているtest_via_to_sql_with_complicating_connectionテストを、PostgreSQL 19+ 環境ではスキップするようにしたPRです。これにより、PostgreSQL 19dev 上での Active Record テストスイートがエラーなく通るように調整しています。
- 変更内容の詳細
対象ファイル:
activerecord/test/cases/adapters/postgresql/bytea_test.rbのみ(+2行)
このテストファイル内の PostgresqlByteaTest#test_via_to_sql_with_complicating_connection に対し、PostgreSQL のバージョンが 19 以上の場合にテストをスキップする条件が追加されています。
テストの目的(既存仕様の背景):
- 元々このテストは、
unescape_byteaの挙動が「コネクション単位」で決まることを保証するためのものです(#14632 参照)。 - PostgreSQL では
standard_conforming_stringsの設定値が、byteaのアンエスケープ処理に影響します。 - そのため、テストでは「ある接続で
standard_conforming_strings = off、別の接続でon」のように設定を変え、それぞれでunescape_byteaの動作が変わることを確認していました。
しかし PostgreSQL 19dev では、以下のように standard_conforming_strings = off を設定するとエラーとなります:
SET standard_conforming_strings = off;
-- ERROR: non-standard string literals are not supportedその結果、Rails のテスト実行中にも次のようなエラーが落ちます:
PG::FeatureNotSupported: ERROR: non-standard string literals are not supportedつまり、「接続ごとに standard_conforming_strings を変える」という前提自体が PostgreSQL 19 以降では成立しないため、
- PostgreSQL 19+ ではこの前提に依存したテストを意味のある形で維持できない
- かつ常にエラーになってしまう
という理由から、バージョン判定でテストをスキップするようにした、という変更です。
Rails 側の実コード(本番経路)には手を入れておらず、テストコードのみの調整です。
- 影響範囲・注意点
影響範囲:
- Active Record の PostgreSQL アダプタのうち、
bytea関連のテストの一部(test_via_to_sql_with_complicating_connection)のみ。 - 本番コード(
postgresql/byteaサポート自体)には変更なし。 - PostgreSQL 18 以前では、従来どおりテストは実行され、
unescape_byteaの接続単位の性質を引き続き検証します。 - PostgreSQL 19 以降では、この特定テストがスキップされるため、「unescape_bytea が per-connection であること」を PostgreSQL 19+ 環境に対しては自動テストで保証できなくなります(ただし、そもそも設定が変えられないので、実質意味を失っているテストとも言えます)。
- Active Record の PostgreSQL アダプタのうち、
注意点:
- PostgreSQL 19 以降をターゲットにしている場合、
standard_conforming_stringsを OFF にして挙動を変える、といったアプリケーション設計はもはや取れません。Rails 以外の生 SQL でも同様にエラーになります。 - 「接続ごとに
standard_conforming_stringsを変えて動作を分岐させる」ようなコードを将来書かない前提で考えるべきです。 - CI 環境で PostgreSQL 19dev を利用している場合、この PR 適用後はテストスイートがグリーンになりやすくなりますが、「テストが緑 = PostgreSQL 18 以前とまったく同じ条件を検証できている」という意味ではないことに注意が必要です(前述のとおり前提が破綻したテストを落とさないようにしただけ)。
- PostgreSQL 19 以降をターゲットにしている場合、
- 参考情報 (あれば)
- PostgreSQL 本体の該当コミット:
- https://github.com/postgres/postgres/commit/45762084545ec14dbbe66ace1d69d7e89f8978ac
standard_conforming_strings = offを禁止し、非標準な文字列リテラルをサポートしない方向に振った変更。
- https://github.com/postgres/postgres/commit/45762084545ec14dbbe66ace1d69d7e89f8978ac
- 関連 Issue:
- Rails: Fix #56789 — このテストエラー報告を受けての修正。
- 過去の設計背景: #14632 —
unescape_byteaを per-connection とすることを保証するためのテストが導入された経緯。
#56430 Fix & standardize comments for CombinedConfiguration
マージ日: 2026/2/15 | 作成者: @jordan-brough
- 概要 (1-2文で)
CombinedConfiguration / EncryptedConfiguration / EnvConfiguration のコメントが整理・統一され、挙動の説明(特に「nil は欠損とみなす」点)が明確化されました。
コードのロジック変更はほぼなく、ドキュメンテーションの精度と一貫性を高めるための PR です。
- 変更内容の詳細
※この PR はほぼ「コメントのみ」の変更であり、実装仕様を変えるものではありません。そのうえで、実装理解に重要なポイントがコメントとして明示された、という意味で有益な変更です。
2-1. CombinedConfiguration のコメント標準化
ActiveSupport::CombinedConfiguration は、複数の設定ソース(例: 環境変数、暗号化設定ファイル、YAML 等)から値を「いいとこ取り」して組み合わせるためのヘルパークラスです。
今回の変更で:
- 「どういう優先順位・ルールで値をマージするか」
- 「どのような値を “存在する/しない” とみなすか」
といった仕様が、3つのクラスで共通の表現・フォーマットで説明されるようになりました。
代表的なコメント内容(※趣旨ベースの日本語まとめ):
- CombinedConfiguration は複数の設定ソースを順番に見て、最初に「存在する」値を採用する
- 値が「nil」の場合は「存在しない(missing)」と扱われる
- 例として、次のようなシナリオがコメント内で提示される
- 例:
primary設定に値がなく、fallback設定にだけ値がある場合は fallback 側の値が採用される - 例:
primaryにnilが設定されている場合も「値なし」とみなされ、次のソースが参照される
- 例:
これにより、CombinedConfiguration を使うときの「マージ戦略」がコメントだけで把握しやすくなっています。
2-2. EncryptedConfiguration のコメント修正
ActiveSupport::EncryptedConfiguration は config/credentials.yml.enc などの暗号化設定を扱うクラスです。
今回の変更点:
ENV を参照している古いコメントの修正
以前のコメントは「ENV を見る」といったニュアンスになっていた箇所がありましたが、実際の挙動や最近の API 変更と合致していないため、CombinedConfiguration ベースのより正確な説明に修正されています。「nil は欠損とみなす」ことをコメントに明記
EncryptedConfiguration が CombinedConfiguration 上に成り立っていること、そして CombinedConfiguration が nil を “missing” と扱うことがコメントで説明されています。シナリオベースの例の導入
コメント内で、例えば次のようなパターンが解説されています(要旨):- credentials 側に
foo: "value"があり、別のソースにfooがなければ credentials の値が使われる - 別ソースに
foo: nilが定義されていても、nil は「ないもの」とされるので credentials の値が使われる
- credentials 側に
これにより、「ENV など他ソースとの統合で credentials がどう扱われるか」をコメントを読むだけで理解しやすくなっています。
2-3. EnvConfiguration のコメント統一
ActiveSupport::EnvConfiguration は主に環境変数ベースの設定をラップするクラスです。
CombinedConfiguration / EncryptedConfiguration と同じスタイル・構成のコメントに変更
どういうキーの優先順位で見るか、値が存在しないとみなされる条件(nil 扱い)などを、EncryptedConfiguration と同じ基準で説明
シナリオ形式のコメント例で、例えば
text# 1. ENV["FOO"] が設定されていればそれを使う # 2. ENV["FOO"] が未設定 (または nil で “missing”) の場合は、他ソースの値を使うといったような「実際によくある状況」で仕様を説明するように整理
- 影響範囲・注意点
実行時の挙動への影響はほぼなし
変更はコメントが中心で、コード上のロジック変更は含まれていない(含まれてもごく軽微で、実質的な仕様変更はない)とみなしてよい内容です。nil を “missing” と扱う仕様がより明示された
- もともとその動作だったが、コメントがそれを明確にドキュメント化した形です。
- つまり、「
nilを明示的な値として保持したい」「上書きしたい」と思っていると、CombinedConfiguration 上では「存在しない」と扱われてしまう点がはっきりしました。 - 将来的に「nil も有効な上書き」として扱いたい場合は、設計・実装レベルで別の仕組みが必要になることがコメントから読み取れます。
ドキュメントを見ながら設定まわりを触る人にとっての安心度が向上
- 特に Rails credentials / ENV / 他設定とのマージ動作に関して、コメント由来の思い込みバグが起きにくくなります。
- 「ENV を見る」といった、現在の実装とずれたコメントが修正されているため、コードリーディング時の混乱が減ります。
- 参考情報 (あれば)
- この PR は、CombinedConfiguration の導入や整理を行った PR: https://github.com/rails/rails/pull/56404 に付随するコメント整備です。
- 変更対象ファイル:
activesupport/lib/active_support/combined_configuration.rbactivesupport/lib/active_support/encrypted_configuration.rbactivesupport/lib/active_support/env_configuration.rb
- 設定のマージ戦略や「nil の扱い」に関する仕様を確認したい場合は、これらファイル先頭付近のクラスコメントを読むのが最も手早く、今回の PR によりその情報がかなり整理されました。
#56801 Improve #hash methods
マージ日: 2026/2/14 | 作成者: @byroot
- 概要 (1–2文で)
このPRは、Active Record の各種クラスで定義されている#hashメソッドの実装を見直し、Ruby 3.2 以降で最適化されたArray#hashを素直に利用する形に変更するものです。これにより、独自のハッシュ値の手動合成をやめて、シンプルでパフォーマンス的にも有利な実装に統一しています。
- 変更内容の詳細
全体方針
これまで一部の #hash 実装は、各属性の hash を自前で組み合わせるようなコードになっていたと考えられます(例: XOR や加算でまとめるなど)。
Ruby 3.2 からは Array#hash 自体が配列確保を避ける最適化(no-alloc hash)を持っているため、「配列にまとめて array.hash する」方式で十分高速かつシンプルになりました。
このPRでは、Active Record の以下のクラス周辺で #hash を「配列化して Array#hash に任せる」実装へと整理しています:
ActiveRecord::ConnectionAdapters::ColumnActiveRecord::ConnectionAdapters::SqlTypeMetadata- 各 DB アダプタの type / column 情報関連クラス
- MySQL:
Mysql::TypeMetadata - PostgreSQL:
PostgreSQL::Column,PostgreSQL::TypeMetadata,PostgreSQL::Utilsの一部 - SQLite3:
SQLite3::Column
- MySQL:
ActiveRecord::Coreのハッシュ関連のごく小さい調整
実装イメージの例
(実際のコードはファイルごとに微妙に異なりますが、おおよそ以下のような変化が行われています)
従来(例):
def hash
h = 17
h = 31 * h + name.hash
h = 31 * h + sql_type.hash
h = 31 * h + default.hash
h = 31 * h + null.hash
h
endまたは、もう少し複雑に XOR したり独自の合成ルールを使っていた形から、
変更後(例):
def hash
[name, sql_type, default, null].hash
endといった形へ統一されます。
Column, TypeMetadata など、Active Record が内部的に「値オブジェクト」として扱うクラス(キャッシュキーや Hash のキーとして使われることがあるもの)で、この単純な配列ベースハッシュ方式が採用されています。
Ruby 3.2 の最適化を前提とした理由
PR の説明にもある通り、Ruby 3.2 以降では Array#hash 実装が改善され、「配列を一旦生成して、それをハッシュ化する」というコストが実質的に低減されました。Ruby 本体側の PR: https://github.com/ruby/ruby/pull/6090 を前提にしており、結果として:
- 自前でハッシュ値を合成する複雑なコードを書くメリットが薄れた
- Ruby 本体に委ねることで、将来の最適化の恩恵も自動的に受けられる
という背景があります。
- 影響範囲・注意点
機能面での影響
- 外部から見た振る舞い(動作仕様)は基本的に変わりません。
- 変わるのは「同一オブジェクトから算出されるハッシュ値の具体的な数値」であり、
eql?/==と整合している限り、Ruby のHash動作としては問題ありません。 - ただし、「生成されるハッシュ値の数値そのもの」に依存しているコード(通常はアンチパターン)は動作が変わる可能性があります。
実際の影響が出やすいケース
通常の Rails アプリでは影響はほぼありませんが、以下のようなケースでは挙動変化の可能性があります:
ColumnやSqlTypeMetadata等の「Active Record 内部の型/カラム情報オブジェクト」を Hash のキーにしており、- かつ、同じキーを用いたキャッシュなどを「プロセスをまたいで」比較・共有しようとしている(例: ハッシュ値のみをシリアライズしている)
- テスト等で「特定オブジェクトの
hashがこの値である」ことを直接アサートしている
ただし、どちらも一般的な利用パターンではなく、多くのアプリケーションは影響を受けません。
パフォーマンス的な側面
- ハッシュ値の計算の実装が Ruby 本体の最適化された経路に乗るため、理論上は CPU 使用量や割り当てオブジェクト数がわずかに改善される可能性があります。
- 一方で、自前でハッシュを組み立てていた実装からシンプルな
Array#hashに変わることで、コードの可読性・保守性が大きく向上します。
- 参考情報 (あれば)
- Ruby 本体の関連 PR(
Array#hashの最適化) - この PR 自体:
- Rails PR #56801: “Improve #hash methods” by byroot
- Ruby の Hash と
hash/eql?に関する仕様(Ruby リファレンス)- オブジェクトを Hash のキーにする場合、
hashとeql?の整合が必要 - ハッシュ値そのものの数値は Ruby のバージョンや実装依存であり固定ではない、という設計に沿った変更です。
- オブジェクトを Hash のキーにする場合、
#56800 [ci skip] Fix typo: skippped -> skipped
マージ日: 2026/2/14 | 作成者: @coderhs
- 概要 (1-2文で)
このPRは、Rails のテストコード内にある文字列のタイポ「skippped」を正しい綴り「skipped」に修正するものです。機能や挙動の変更はなく、純粋なスペル修正のみです。
- 変更内容の詳細
対象ファイル:
railties/test/generators/app_generator_test.rb
行数レベルの変更内容は以下のようなものと考えられます(実際の差分イメージ):
- assert_match "skippped", output
+ assert_match "skipped", outputもしくはテスト内のメッセージ/期待値の文字列が同様に修正されています。
ポイント:
- テストコード中の固定文字列(期待メッセージやログの一部など)の typo を修正
- 修正前後で意味は同じで、単に英単語の綴りが正しくなっただけです
- PR タイトルに
[ci skip]が付いていることから、変更が極めて小さく、安全であると判断され、CI 実行をスキップしていることが分かります
- 影響範囲・注意点
- 実行時のアプリケーションコード(本体機能)には一切影響ありません。
- 影響を受けるのは以下のみです:
- 対象テストが出力を検証している場合、その出力に含まれる文字列が「skipped」であることを前提に動きます。
- もし外部のコードや社内ツールなどが、テスト出力やログの文字列「skippped」に依存していた場合は、マッチしなくなる可能性があります(通常はほぼ無い想定)。
- 単なる typo 修正のため、Rails をアップデートしてもアプリ側で特別な対応は不要です。
- 参考情報 (あれば)
- PR: https://github.com/rails/rails/pull/56800
[ci skip]は GitHub Actions などの CI を走らせたくないコミット・PR でよく使われる慣習的な記法で、ドキュメント修正や今回のような軽微な typo 修正で使われることが多いです。
#56797 Include virtual? in Column#== and Column#hash for SQLite3 and PostgreSQL
マージ日: 2026/2/14 | 作成者: @uberjay
- 概要 (1-2文で)
SQLite3 / PostgreSQL アダプタにおいて、カラム同士の同一性判定(==とhash)に仮想カラム(GENERATED / virtual)かどうかを含める変更です。これにより、仮想カラムと通常カラムが誤って同一とみなされて INSERT / UPDATE 対象から外れてしまう不具合が解消されます。
- 変更内容の詳細
背景: Deduplicable とカラムの同一性判定
Active Record には Deduplicable というモジュールがあり、カラムインスタンスをレジストリで管理しつつ「同一と判定されたカラムは再利用する(重複を排除する)」仕組みがあります。
このとき、
==(等価比較)hash(ハッシュ値)
を使って「同一カラムかどうか」を判断しています。
問題となっていたのは、次のようなケースです。
- あるテーブルに GENERATED(仮想)カラムがある
- 別のテーブルに「同じ名前・同じ型だが通常カラム」のカラムがある
- 仮想カラムのインスタンスが先にレジストリに登録される
- 後から通常カラムが登録される際、「同じ名前・型なので同一」と判定され、仮想カラムのインスタンスに“重複排除”されてしまう
その結果、通常カラムであるにもかかわらず、「仮想カラム(書き込み対象ではない)」として扱われてしまい、INSERT/UPDATE のカラムリストから外れて NULL が入る、という致命的なバグが発生していました。
今回の修正内容
SQLite3 と PostgreSQL の Column クラスにおいて、以下を変更しています。
==の比較条件にvirtual?を含めるhashの計算要素にvirtual?を含める
これにより、
- 「同じ名前・同じ型でも、仮想カラムと通常カラムは別物」として扱われる
generated_type(PostgreSQL の STORED / VIRTUAL などの違い)を反映したvirtual?の結果が同一判定に効くようになる
疑似的なイメージコードは以下のようになります(実際のコードとは多少異なりますが意図の説明用):
# 変更前(イメージ)
def ==(other)
other.is_a?(self.class) &&
name == other.name &&
sql_type == other.sql_type
end
def hash
[name, sql_type].hash
end
# 変更後(イメージ)
def ==(other)
other.is_a?(self.class) &&
name == other.name &&
sql_type == other.sql_type &&
virtual? == other.virtual?
end
def hash
[name, sql_type, virtual?].hash
endテストの追加
次のファイルにテストが追加されています。
activerecord/test/cases/adapters/postgresql/virtual_column_test.rbactiverecord/test/cases/adapters/sqlite3/virtual_column_test.rb
テストのポイントは:
- 同じ名前・型の「仮想カラム」と「通常カラム」をそれぞれ作成
Deduplicable経由でレジストリに登録したときに、別々のカラムとして扱われること- 特に、通常カラムが INSERT / UPDATE から除外されないこと
このあたりを確認するためのケースが用意されています。
- 影響範囲・注意点
影響範囲
- 対象:
- SQLite3 アダプタの
ActiveRecord::ConnectionAdapters::SQLite3::Column - PostgreSQL アダプタの
ActiveRecord::ConnectionAdapters::PostgreSQL::Column
- SQLite3 アダプタの
- 機能的には以下に影響:
- カラムメタデータの dedup(再利用)ロジック
- 仮想カラム / GENERATED カラムを含むスキーマでの INSERT / UPDATE のカラム選択
- 直接アプリコードを書き換える必要は基本的にありませんが、内部のカラムオブジェクトの同一性判定が変わることで、以下のようなコードに影響する可能性があります:
- カラムオブジェクトをハッシュキーにして保持している独自実装
==/eql?/hashを前提に何かしらキャッシュしているコード
ただし、「仮想かどうか」を区別せずに同一扱いしていたこと自体がバグに近く、多くの場合は今回の変更の方が期待に沿った挙動です。
実務的な注意点
- もし、PostgreSQL/SQLite3 で GENERATED / 仮想カラムを使っていて、
「なぜか別テーブルの同名カラムに値が入らず NULL になる」といった現象が出ていた場合、この PR によって解消される可能性があります。 - 逆に、「カラムオブジェクトを dedup したい独自実装」をしている場合、
今後は「仮想かどうか」が同一性の一部になることに注意が必要です(通常は問題ないはずです)。
- 参考情報 (あれば)
- 対応 Issue: #56795
「GENERATED(仮想)カラムがレジストリに登録されることで、別テーブルの通常カラムが dedup されてしまう」というバグ報告に対する修正。 - 関連機能:
ActiveRecord::ConnectionAdapters::PostgreSQL::ColumnActiveRecord::ConnectionAdapters::SQLite3::ColumnActiveRecord::Delegation::Deduplicable(カラムインスタンスの dedup ロジック)
- 生成列 / 仮想カラムを積極的に使うスキーマ(計算列や STORED GENERATED など)を利用しているアプリケーションでは、Rails のバージョンアップ時にこの修正が含まれているかどうかを確認しておくと良いです。
#56794 Add non-interactive exec subcommand to devcontainer tool
マージ日: 2026/2/13 | 作成者: @bheeshmar
- 概要 (1-2文で)
Rails の開発用 devcontainer ツールに、TTY を伴わない非対話的なexecサブコマンドが追加され、ホスト側からコンテナ内でコマンドを実行できるようになりました。あわせてmiseを有効化してから実行することで、rubyやbundleなどのツールが確実に PATH に乗るようにしています。
- 変更内容の詳細
tools/devcontainerスクリプトに、新たにexecサブコマンドが追加されました。- これにより、ホストマシン上から次のようにコンテナ内で任意のコマンドを実行できます:sh
./tools/devcontainer exec "echo hi" ./tools/devcontainer exec "bundle exec rake test" ./tools/devcontainer exec "ruby -v" - 「非インタラクティブ (non-interactive)」という説明から、
docker exec -iのように TTY をアタッチせずに実行する形が想定されています。- CI、スクリプト、エージェント (agentic coding) など、対話を前提としないワークフローからの利用が主な目的です。
- コマンド実行前に
miseを有効化する処理が入っており、miseが管理するruby/bundle/ その他ツールが PATH に確実に入る状態でコマンドが走ります。- これにより「コンテナに入ってみたら
rubyが見つからない」といった PATH 周りの不整合を避けられます。
- これにより「コンテナに入ってみたら
- PR のモチベーションとしては、
rdoc-to-mdスクリプト改善のために「エージェントに devcontainer 内で繰り返しコマンドを実行させる」ユースケースがあり、そのための土台としてdevcontainer execが導入されています。
(実装は 5 行のみの小さな追加で、既存の devcontainer 用ラッパースクリプトに exec 分岐と mise 有効化 + コマンド実行を足しただけという構成と考えられます。)
- 影響範囲・注意点
- 主に影響する対象
- Rails リポジトリの devcontainer を使っている開発者
- 自動生成ツールや CI、エージェントなどから devcontainer を操作したいユースケース
- 互換性
- 既存の
devcontainerサブコマンドの挙動を変える変更ではなく、新しいサブコマンド追加のみのため、後方互換性への影響は限定的と考えられます。
- 既存の
- 利用時の注意点
- 非対話実行前提なので、
stdinや TTY 入力を前提とするコマンド(対話プロンプトを出すインストーラなど)はそのままだと動かない可能性があります。 miseに依存するツールバージョンが適切に設定されていることが前提になります。プロジェクトの.mise.tomlなどの設定が壊れていると、ruby/bundleが期待通りに解決されない可能性があります。- この PR ではテスト追加や CHANGELOG 更新は行われていないため、問題があれば実利用の中でフィードバックされる形になります。
- 非対話実行前提なので、
- 参考情報 (あれば)
- 典型的な利用シナリオ例:
- 外側のスクリプトから Rails テストを devcontainer 内で実行:sh
./tools/devcontainer exec "bundle exec rails test" - ドキュメント生成や rdoc 変換バッチをコンテナ内で定期実行:sh
./tools/devcontainer exec "bundle exec rake rdoc:to_md" - エージェントや LLM ベースの自動修正ツールが、コンテナを開き直さずに繰り返しコマンドを投げる、といったワークフローに向いています。
- 外側のスクリプトから Rails テストを devcontainer 内で実行:
#49821 Move route helper method_missing to ActionController::TestCase
マージ日: 2026/2/13 | 作成者: @gmcgibbon
- 概要 (1-2文で)
このPRは、ルーティング関連テストで使われていたmethod_missingベースのルートヘルパー委譲処理を、「すべてのテスト」から「コントローラテスト専用」に整理した変更です。結果として、Integration Test はこれまで通りintegration_sessionを経由し、Controller Test だけがmethod_missingによる URL ヘルパー解決を行うようになります。
- 変更内容の詳細
何をどこからどこへ移動したか
従来は、ActionDispatch::Assertions::Routing(routing assertions モジュール)の中に method_missing が定義されており、そこからルートヘルパー(users_path など)を解決していました。
今回のPRでは、この method_missing を以下のように移動しています。
- 削除元:
actionpack/lib/action_dispatch/testing/assertions/routing.rb - 追加先:
actionpack/lib/action_controller/test_case.rb(ActionController::TestCase)
これにより:
- Controller Test (
ActionController::TestCase)
→ クラス側にmethod_missingが定義され、そこでルートヘルパーを解決 - Integration Test (
ActionDispatch::IntegrationTest)
→ 従来通りintegration_sessionのルートヘルパーを使う(method_missingには依存しない)
背景・理由
元の
method_missingには「TODO」が付いており、「本当にここにあるべきか?」という懸念があった。Integration Test では既に以下のように
integration_sessionにルートヘルパー委譲する仕組みがあるため、routing assertions 側のmethod_missingは本来 Controller Test 用とみなすのが自然:ruby# (リンク先の実際のコードは要参照だが、イメージとして) def method_missing(name, *args, &block) if integration_session.respond_to?(name) integration_session.public_send(name, *args, &block) else super end endRouting Assertions モジュールは Integration Test にも include されているが、Integration Test 側はすでに別経路でルートヘルパーを扱っているため、そこで
method_missingを定義しておく必要はない、という整理。
テストの追加
actionpack/test/controller/action_pack_assertions_test.rb にテストが追加され、Controller Test 文脈でルートヘルパーが期待通りに解決されることが確認されています。
これにより、「routing assertions から method_missing を外しても Controller Test では動作が維持される」ことをテストベースで保証しています。
- 影響範囲・注意点
想定される影響
Controller Test (
ActionController::TestCase)- 実質的な挙動はほぼ同じで、引き続き URL / path ヘルパーを
method_missingで解決できます。 - ただし「routing assertions モジュールを単独で include して URL ヘルパーを拾っていた」ような、ややイレギュラーな使い方をしている場合は動作が変わる可能性があります(
ActionController::TestCaseに依存する形になったため)。
- 実質的な挙動はほぼ同じで、引き続き URL / path ヘルパーを
Integration Test (
ActionDispatch::IntegrationTest)- 既に
integration_sessionにルートヘルパーを委譲する仕組みがあるため、今回の変更による挙動変化は基本的にありません。 - もし「routing assertions による
method_missingに直接依存していた」場合には、そちらが使われなくなるため注意が必要ですが、PRの記述からはそれを正式な想定パスとは見なしていないことが読み取れます。
- 既に
Public API としての位置づけ
- 移動した
method_missingは事実上 public な振る舞い(テスト環境での URL ヘルパー解決)を担っていますが、Rails チームとしては「Controller Test 文脈で使われること」を前提として整理し直しています。 - routing assertions モジュールを直接 include して URL ヘルパーを解決するのは、今後も安全とは限らないため、基本的には:
- Controller Test:
ActionController::TestCase/ActionDispatch::IntegrationTest - Integration Test:
ActionDispatch::IntegrationTest+integration_session経由
での利用を前提にした方がよさそうです。
- Controller Test:
- 参考情報 (あれば)
- Integration Test のルートヘルパー委譲実装(
integration_session)actionpack/lib/action_dispatch/testing/integration.rbの該当箇所#url_helpersやmethod_missingを介してintegration_sessionに委譲する部分
- Routing Assertions モジュール
actionpack/lib/action_dispatch/testing/assertions/routing.rb- 今回
method_missingが削除され、純粋にルーティング関連アサーションに責務が限定された形になっている。
- 今回
#56783 Fix ActiveStorage::Blob content type methods to handle nil
マージ日: 2026/2/13 | 作成者: @kudoas
- 概要 (1-2文で)
ActiveStorage::Blob のコンテンツタイプ判定系メソッド(image?など)がcontent_type: nilを持つレコードでNoMethodErrorを起こしていた問題を修正した PR です。purge_laterで未添付 Blob をクリーンアップする際に落ちるケースを、nilを安全に扱うことで防いでいます。
- 変更内容の詳細
問題となっていた挙動
Active Storage の未添付 Blob を削除する処理(例: rake タスクや
purge_later)の途中で、以下のようなエラーが発生していました。textundefined method `start_with?' for nil:NilClass (NoMethodError)これは
image?などの「コンテンツタイプに基づく判定メソッド」が内部でrubycontent_type.start_with?("image/")のように
start_with?を素直に呼んでいたため、content_typeがnilの場合に例外になっていたのが原因です。content_type: nilのレコードは、脆弱性診断ツールなどが/rails/active_storage/direct_uploadsにContent-Typeを付けずにリクエストを送ることで作成されてしまうことがあり、現実的に起こりうるデータ不整合です。
対応内容
Blob モデルでの nil 安全化
activestorage/app/models/active_storage/blob.rb で、コンテンツタイプを判定するメソッド群(少なくとも image? / audio? / video? / text? といった MIME type prefix 判定系)が、content_type が nil の場合でも例外を出さないように修正されています。
実装イメージとしては、以下のどちらかのパターンで nil を考慮する形です(PR 説明より):
# 例1: presence チェック
def image?
content_type.present? && content_type.start_with?("image/")
end
# 例2: セーフナビゲーション
def image?
content_type&.start_with?("image/") || false
endいずれの場合も、nil のときは false を返し、例外は起こさないようにしています。
テスト追加
activestorage/test/models/blob_test.rb に、content_type: nil の Blob に対して各判定メソッドを呼んでも例外が出ず、かつ false になることを確認するテストが追加されています。
これにより、今後同様のリグレッションが起きにくくなっています。
- 影響範囲・注意点
影響範囲
- ActiveStorage::Blob のコンテンツタイプ判定メソッド(
image?,audio?,video?,text?など)を利用しているコード全般。 purge_laterや未添付 Blob をまとめて削除するタスクが、content_type: nilを含む環境でも最後まで処理できるようになります。
- ActiveStorage::Blob のコンテンツタイプ判定メソッド(
互換性
- これらのメソッドは元々「コンテンツタイプがある前提」で書かれていましたが、
nilのときは例外ではなく 常にfalseを返すようになります。 - 多くのアプリでは期待通りの挙動(「タイプ不明ならそのタイプとは見なさない」)であり、後方互換性上も問題はほぼありません。
- もしアプリ側で「
image?が例外を投げることを前提にしている」ような特殊なロジックがあれば挙動が変わりますが、現実的にはほぼ無いと考えられます。
- これらのメソッドは元々「コンテンツタイプがある前提」で書かれていましたが、
運用上の注意
- 今回の修正は「
nilを安全に扱う」ものであり、content_type: nilのレコード自体を自動的に修正するものではありません。 - データとして
nilを許容したくない場合は、- direct upload リクエストで
Content-Typeを必須にする - DB レベルで
content_typeの NOT NULL 制約を追加する - 既存の
nilレコードをバッチで埋める・削除する などの対応をアプリ側で検討する必要があります。
- direct upload リクエストで
- 今回の修正は「
- 参考情報 (あれば)
- PR: https://github.com/rails/rails/pull/56783
- 関連する Rails コード:
ActiveStorage::Blobの MIME type 判定メソッド (image?,audio?,video?,text?など)
- 背景となるエンドポイント:
- Direct upload エンドポイント:
/rails/active_storage/direct_uploads
→ セキュリティスキャンツールなどがここに不正・不完全なリクエストを投げると、content_type: nilの Blob が作られ得る。
- Direct upload エンドポイント:
#53417 Remove storage from gitignore if skip_active_storage and database is not sqlite3
マージ日: 2026/2/13 | 作成者: @vinibispo
- 概要 (1-2文で)
Railsアプリ生成時に--skip-active-storageかつ DB が sqlite3 以外の場合、storageディレクトリ関連のエントリを.gitignore/.dockerignoreから外すようにした PR です。これにより Active Storage を使わない構成で、誤ってstorageを無視してしまう問題 (#53413) を解消しています。
- 変更内容の詳細
2-1. 対象ファイル
railties/lib/rails/generators/rails/app/templates/gitignore.ttrailties/lib/rails/generators/rails/app/templates/dockerignore.tt- テスト:
railties/test/generators/app_generator_test.rb
2-2. 何をしているか
Rails new 時に使われる .gitignore / .dockerignore テンプレート (*.tt) に条件分岐を追加し、以下の条件のときだけ storage 関連の ignore エントリを出力するようにしています。
- Active Storage を有効にしている(=
--skip-active-storageではない) - もしくは DB が sqlite3 の場合
逆にいうと、「Active Storage をスキップしていて、DB が sqlite3 以外」の場合は、storage ディレクトリを無視する行を出力しません。
実際のテンプレート中のイメージ(擬似コード):
<% unless options[:skip_active_storage] && options[:database] != 'sqlite3' -%>
/storage/*
!/storage/.keep
<% end -%>同様の条件が .dockerignore テンプレートにも追加されています。
2-3. テストの追加
railties/test/generators/app_generator_test.rb に以下のようなパターンのテストが追加されています。
--skip-active-storageかつ--database=mysqlや--database=postgresqlなど
→ 生成された.gitignore/.dockerignoreにstorage行が「含まれない」ことを検証- Active Storage をスキップしない、または DB が sqlite3 の場合
→ これまで通りstorage行が含まれることを検証
これにより、PR の挙動が generator レベルで担保されています。
- 影響範囲・注意点
- 新規に
rails newするプロジェクトのみが対象で、既存プロジェクトの.gitignore/.dockerignoreには影響しません。 --skip-active-storageを指定し、かつ DB に sqlite3 以外(MySQL/PostgreSQL 等)を選択した場合:- これまで:
storage/*などが.gitignoreに入ってしまい、storageに何か置くと git 管理から漏れることがあった。 - これから:
storage関連の ignore 行が生成されないため、任意の用途でstorageを使っても git に普通に乗ります。
- これまで:
- まだ Active Storage を使う予定があり、のちに導入する場合は、自分で
.gitignoreにstorage/*エントリを追加する必要があります(ただし Active Storage 用にstorageを使わない設計であれば不要)。 - sqlite3 の場合は従来通り
storageを ignore します。これは Rails 標準の Active Storage + sqlite3 の典型的な初期構成を維持するための互換性配慮と考えられます。
- 参考情報 (あれば)
- 関連 Issue: #53413 — Active Storage をスキップしたのに
storageが.gitignoreに入る問題の修正 - 生成テンプレート:
.gitignore:railties/lib/rails/generators/rails/app/templates/gitignore.tt.dockerignore:railties/lib/rails/generators/rails/app/templates/dockerignore.tt
- Generator オプション:
--skip-active-storage--database=sqlite3|mysql|postgresql|...
#56772 Bump Github Action cache version to 5
マージ日: 2026/2/13 | 作成者: @akhilgkrishnan
- 概要 (1-2文で)
Rails が生成する GitHub Actions 用 CI 設定テンプレートにおいて、actions/cacheのバージョン指定を v5 系に更新した PRです。アプリ/プラグイン生成時に作られる.github/workflows/ci.ymlで、最新世代のキャッシュアクションが使われるようになります。
- 変更内容の詳細
対象ファイルは以下の 2 つのテンプレートです。
railties/lib/rails/generators/rails/app/templates/github/ci.yml.ttrailties/lib/rails/generators/rails/plugin/templates/github/ci.yml.tt
これらは rails new や rails plugin new で GitHub Actions 用 CI 設定を生成するときのひな形です。
変更内容は「GitHub Actions のキャッシュアクションのメジャーバージョンを 4 → 5 に上げた」だけの 1 行変更です(実際の PR では概ね以下のような差分になります)。
- - uses: actions/cache@v4
+ - uses: actions/cache@v5
with:
path: vendor/bundle
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
restore-keys: |
${{ runner.os }}-gems-※ 上記は典型的な Rails CI テンプレートの例であり、実際のパス・キー名はテンプレートに依存しますが、バージョン指定部分だけが @v5 に変わった、というのが本質です。
これにより、Rails のジェネレータが出力する CI 設定は、actions/cache の v5.0.3 系リリース(v5 タグ)が指す実装を利用するようになります。
- 影響範囲・注意点
影響範囲
- 新規に
rails newあるいはrails plugin newを実行し、「GitHub CI 用テンプレート」を使って.github/workflows/ci.ymlを生成した場合にのみ影響します。 - 既存の Rails プロジェクトの GitHub Actions 設定ファイルは、自動的には書き換えられません。必要であれば手動で
actions/cache@v4→actions/cache@v5に更新することになります。
- 新規に
互換性・注意点
actions/cachev5 は Node 20 ベースになるなど内部実装が変わっていますが、基本的な使用方法(uses,with: path/key/restore-keysなど)は v4 と後方互換です。通常の Rails の Bundler キャッシュにはそのまま利用できます。- GitHub Actions のランナー環境が古く、
actions/cache@v5に未対応であるケースはほぼ想定されませんが、オンプレミスの GitHub Enterprise Server などを使っている場合はサーバーのバージョンと互換性を確認する価値はあります。 - キャッシュキーの形式やパス指定は変更していないため、「キャッシュのヒット率」や「キャッシュの粒度」といった挙動は従来のテンプレートと同等です。
- 参考情報 (あれば)
- PR 本体: https://github.com/rails/rails/pull/56772
actions/cachev5.0.3 リリースノート:
https://github.com/actions/cache/releases/tag/v5.0.3actions/cacheドキュメント:
https://github.com/actions/cache
この PR は CI 設定テンプレートのメンテナンス的更新であり、Rails 本体の挙動やアプリケーションコードには影響しませんが、今後生成されるプロジェクトがより新しい GitHub Actions の実装を利用できるようにするためのものです。
#56773 Bump Github Action upload-artifact version to 6
マージ日: 2026/2/13 | 作成者: @akhilgkrishnan
- 概要 (1-2文で)
GitHub Actions のactions/upload-artifactを v6 に更新するための、CI 用テンプレート修正 PRです。Railsアプリ・プラグイン生成時に出力される GitHub Actions ワークフローのバージョン指定だけを更新しており、機能的な挙動変更はほぼありません。
- 変更内容の詳細
対象は Rails のジェネレータが吐き出す GitHub Actions の CI 定義テンプレート 2ファイルです:
railties/lib/rails/generators/rails/app/templates/github/ci.yml.ttrailties/lib/rails/generators/rails/plugin/templates/github/ci.yml.tt
これらの中に記述されている GitHub Action actions/upload-artifact のバージョン指定が 5 → 6 に変更されました。
イメージとしては以下のような差分です(実際のファイルはテンプレート構文を含みますが、本質はバージョン番号のみ):
# 修正前
- name: Upload test artifacts
uses: actions/upload-artifact@v5
with:
name: test-artifacts
path: tmp/test-results
# 修正後
- name: Upload test artifacts
uses: actions/upload-artifact@v6
with:
name: test-artifacts
path: tmp/test-resultsRails new / plugin new で --github オプションなどを指定したときに生成される .github/workflows/ci.yml に、最新版(v6)の upload-artifact が使われるようになります。
- 影響範囲・注意点
影響範囲
- 新規に Rails アプリ / プラグインを生成した際の GitHub Actions CI 設定のみが影響を受けます。
- 既存プロジェクトのワークフローはこの PR では自動変更されません。
実運用上の注意点
- すでに GitHub が v5 を Deprecated にしていく流れを考えると、テンプレートが v6 に追従した形になります。
- 既存プロジェクトでテンプレートを参考にしている場合は、
.github/workflows/ci.ymlの該当箇所を手動でactions/upload-artifact@v6に上げると良いです。 - v5 → v6 の間で
upload-artifactの入力パラメータや挙動に互換性が問題になる変更がないかは、利用しているオプションによっては確認しておくと安心です(通常のname/path程度であればそのまま動くケースが大半です)。
Rails 自体の挙動への影響
- Rails 本体のランタイムやテスト挙動には影響せず、あくまで CI テンプレートレベルのメンテナンス変更です。
- CHANGELOG にも載せていないことから、利用者から見える「Railsの機能変更」としては扱われていません。
- 参考情報 (あれば)
actions/upload-artifactGitHub Marketplace:
https://github.com/actions/upload-artifact
→ v5 から v6 への変更点・Breaking changes の有無はここで確認できます。- Rails の GitHub CI テンプレートが配置されているディレクトリ:
railties/lib/rails/generators/rails/app/templates/github/railties/lib/rails/generators/rails/plugin/templates/github/
#56785 Fix JSONGemCoderEncoder to call to_s on original hash key
マージ日: 2026/2/13 | 作成者: @drsharp
- 概要 (1-2文で)
Rails 8.1.0 で導入されたJSONGemCoderEncoderにより、as_jsonが Hash を返すオブジェクトをハッシュキーに使った場合の JSON シリアライズが壊れていた問題を修正する PRです。ハッシュキー変換時にas_jsonの戻り値ではなく「元のキーオブジェクト」に対してto_sを呼ぶように戻すことで、7.2/8.0 と同等の挙動に復元しています。
- 変更内容の詳細
問題の挙動
Rails 8.1 で JSONGemCoderEncoder が導入された際、ハッシュキーの処理が次のようになっていました(問題箇所のイメージ):
CODER = ::JSON::Coder.new do |value, is_key|
json_value = value.as_json
next json_value.to_s if is_key # ← ここがバグ
...
endこのため、以下のようなケースで不正な JSON が生成されます。
class CustomKey
def initialize(id)
@id = id
end
def to_s
"custom_#{@id}"
end
def as_json(options = nil)
{ id: @id, metadata: { created_at: Time.now.iso8601 } }
end
end
hash = { CustomKey.new(123) => "value" }
# Rails 7.2 / 8.0 の正しい挙動
hash.to_json
# => {"custom_123":"value"}
# Rails 8.1.x のバグ挙動
hash.to_json
# => {"{:id=>123, :metadata=>{...}}":"value"}
# as_json が返した Hash に対して to_s が呼ばれている本来は「キーとして使われているオブジェクト自身」を文字列化すべきところが、「as_json の戻り値(Hash)」に対して to_s を呼んでしまっているのが原因です。
この問題は特に以下のようなケースで実害が出ます。
- ドメインオブジェクト(例: Tenant, FieldKey など)をハッシュキーとして使う
- それらを JSON/JSONB カラムに保存する
- オブジェクトは API シリアライズ用に
as_jsonで Hash を返す実装を持っている
DB から取り出した JSON を再度 Hash に戻した際にキーが一致せず、ハッシュ lookup が失敗する、といった不具合になります。
修正内容
修正は 1 行のみで、is_key な場合の next の対象を json_value から value に変更しています。
修正前:
CODER = ::JSON::Coder.new do |value, is_key|
json_value = value.as_json
next json_value.to_s if is_key
...
end修正後:
CODER = ::JSON::Coder.new do |value, is_key|
json_value = value.as_json
next value.to_s if is_key # 元のオブジェクトに対して to_s
...
endこれにより:
- ハッシュ「キー」の場合は
as_jsonを経由せず、常にオリジナルのオブジェクトのto_sを使う - ハッシュ「値」や通常のオブジェクトは従来どおり
as_jsonを使ってシリアライズされる
という挙動になります。
テスト追加
activesupport/test/json/encoding_test.rb にテストが追加されています:
test_hash_with_object_keys_that_have_complex_as_jsonas_jsonがネストした Hash を返すようなオブジェクトをハッシュキーにした場合でも、to_sの結果がキーとして使われることを確認
これにより、今回のバグが再発しないようにカバーしています。
- 影響範囲・注意点
対象バージョン:
- バグが導入された: Rails 8.1.0(
JSONGemCoderEncoder導入時) - この PR は 8.1 系以降にマージされており、以降の 8.1.x / 8.2? などで修正が反映される想定
- バグが導入された: Rails 8.1.0(
影響を受ける可能性があるケース:
- ハッシュキーとしてカスタムオブジェクトを使っている
- そのオブジェクトが
as_jsonを実装していて Hash を返す - そのハッシュを
to_jsonして JSON/JSONB カラムに保存したり、API レスポンスとして返している
修正による互換性:
- 数値キー(
{1 => "one"})や Symbol/String キーの挙動は従来どおり as_jsonが String / Symbol を返すオブジェクトも従来どおり- Rails 7.2 / 8.0 の挙動に合わせているため、「8.1 でのみ壊れていた」挙動を元に戻すだけであり、実質的に後方互換なバグ修正とみなせます
- 「8.1 の壊れた挙動に依存していた」コードは理論上ありえますが、Hash の
to_sをキーとして期待するケースは実務上ほぼないと思われます
- 数値キー(
実務上のチェックポイント:
- Rails 8.1 系にアップグレード済みで、JSON/JSONB カラムに「オブジェクトをキーにした Hash」を保存している場合:
- 「8.1.x の不正なシリアライズ結果」が既に DB に入っていないか確認
- もし入っている場合は、アプリケーション側でマイグレーション or データ修復が必要になる可能性があります
- 7.2 / 8.0 → 修正版 8.1.x へ直接上げる分には、挙動は以前と同じになるため基本的には問題なし
- Rails 8.1 系にアップグレード済みで、JSON/JSONB カラムに「オブジェクトをキーにした Hash」を保存している場合:
- 参考情報 (あれば)
対応する Issue: https://github.com/rails/rails/issues/56784
バグ導入コミット: https://github.com/rails/rails/commit/2761020ddd1e9675d5f4d4573f2bf31b9decdebb
再現用スクリプトの結果:
- Rails 8.1.2: 失敗(バグあり)
- Rails 7.2.3 / 8.0.4: 成功(正しい挙動)
参考: Rails 7.2 でのキー処理(
JSONGemEncoder):rubywhen Hash result = {} value.each do |k, v| k = k.to_s unless Symbol === k || String === k # キーを先に to_s result[k] = jsonify(v) end result end今回の修正は、この「キーは元オブジェクトに対して
to_sする」という方針を 8.1 でも維持するものです。
#56787 Add action mailer load hook in broken configuration test
マージ日: 2026/2/13 | 作成者: @gmcgibbon
- 概要 (1-2文で)
このPRは、「壊れた設定 (broken configuration)」を扱うテストで発生していた Action Mailer の load hook に関するエラーを解消するための、テストコードの小さな修正です。アプリ本体の挙動変更はなく、テストの安定性・正しさを確保するためのメンテナンス的変更です。
- 変更内容の詳細
- 対象ファイル:
railties/test/application/configuration_test.rb - 変更内容: テストケース内で Action Mailer のロードフック(
ActiveSupport.on_load(:action_mailer))を適切に扱うようにし、前のPR (#56201) で導入された挙動によりテストがコケる問題を修正しています。
コードレベルでは、壊れた設定を再現するテスト(例: misconfigured / missing Action Mailer 設定を検証するようなテスト)に対して、Action Mailer の load hook が呼ばれた際に例外が発生しないよう、フックを明示的に定義または調整する1行+αの修正が入っています。
イメージとしては、以下のようなテスト内の追記に近い変更です(実際のコードは概念的にこのようなものと考えてください):
ActiveSupport.on_load(:action_mailer) do
# テスト環境での最小限の初期化 or 例外抑止
endもしくは、broken configuration テスト中でも load hook が安全に走るような設定を追加する行が加えられています。
- 影響範囲・注意点
影響範囲:
- Rails 本体のランタイム挙動には影響せず、
railtiesのテストコード限定の変更です。 - CI やローカルで Rails 本体のテストスイートを流す際に、特定の configuration テストが Action Mailer load hook に起因して落ちていた問題が解消されます。
- Rails 本体のランタイム挙動には影響せず、
注意点:
- アプリケーション側で
ActiveSupport.on_load(:action_mailer)を独自に使っている場合、このPR自体が直接の破壊的変更になることはありませんが、「load hook の呼ばれ方」を前提にして脆いテストを書いていると、#56201 以降の挙動変化で似た問題が起きる可能性があります。 - Rails 本体での対処方法として「テスト側で適切に hook を扱う」方針が取られているため、アプリケーションでも「load hook がいつ・どこで呼ばれるか」に依存しすぎない設計が望ましいです。
- アプリケーション側で
- 参考情報 (あれば)
関連PR:
- #56201 — このPRで導入された変更により、今回の load hook エラーが顕在化したと説明されています。Action Mailer の初期化タイミングや設定まわりに関する挙動変更が含まれている可能性が高いため、詳細な背景を知りたい場合は #56201 の内容を確認するとよいです。
キーワード:
ActiveSupport.on_load(:action_mailer)- Rails アプリケーション設定 (
railties/test/application/configuration_test.rb) - broken configuration / 設定エラー時の挙動テスト
#56777 Remove the deprecated ActiveSupport::Multibyte::Chars class
マージ日: 2026/2/12 | 作成者: @byroot
- 概要 (1-2文で)
このPRでは、非推奨となっていたActiveSupport::Multibyte::Charsクラスと関連コード/テストが Rails 本体から完全に削除されました。あわせて ActiveRecord のクォート処理からもMultibyte::Chars依存が取り除かれ、マルチバイト文字列処理は Ruby の標準文字列機能+既存の ActiveSupport 拡張に一本化されています。
- 変更内容の詳細
2-1. ActiveSupport::Multibyte::Chars 関連の削除
以下のファイルがほぼ丸ごと削除されています:
activesupport/lib/active_support/multibyte/chars.rbactivesupport/lib/active_support/multibyte.rbactivesupport/lib/active_support/core_ext/string/multibyte.rb- それに関連するテスト一式
activesupport/test/multibyte_chars_test.rbactivesupport/test/multibyte_proxy_test.rbactivesupport/test/multibyte_test_helpers.rbactivesupport/test/core_ext/string_ext_test.rb内の一部- など
これにより、以下のような古い書き方は Rails 本体からは消えます:
# 旧: Multibyte::Chars を直接使う書き方
s = ActiveSupport::Multibyte::Chars.new("こんにちは")
s.normalize(:kc)
s.lengthこの種の API はすでに長らく非推奨になっており、実装も proxy 的なラッパークラス (Chars) を経由する形でしたが、それ自体が完全に削除されています。
2-2. ActiveRecord の quote 周りからの参照削除
activerecord/lib/active_record/connection_adapters/abstract/quoting.rb から ActiveSupport::Multibyte::Chars への参照が削除・置き換えられました(差分は +2/-2 と少ないですが、依存削除の要となる変更です)。
もともと:
- DB への文字列クォート処理 (
quote) の中で、マルチバイト文字列の長さや切り詰め等にMultibyte::Charsが利用されていた
PR本文の説明にあるとおり、元々は「eager load 対象にするか検討中だったが、そもそも削除予定なのでこのタイミングで消した」という流れです。
現在は Ruby の String と ActiveSupport の標準拡張 (mb_chars まわりも含め) を前提に処理が組まれており、Chars クラス自体への直接依存はなくなりました。
2-3. テストと CHANGELOG の更新
activerecord/test/cases/quoting_test.rbとsanitize_test.rbから、Multibyte::Charsを前提にしたテストが削除されています(-7 行, -20 行)。activesupport/test/logger_test.rbでは、ログ出力とマルチバイトの扱いに関するテストが、新しい前提(Charsなし)に合わせて微調整されています(+3/-1)。activesupport/CHANGELOG.mdに、ActiveSupport::Multibyte::Charsが削除されたことを明記する項目が追加されています(+6 行)。
- 影響範囲・注意点
3-1. 自前で ActiveSupport::Multibyte::Chars を使っているアプリ/ライブラリ
最も直接的な影響はここです。
以下のようなコードは、Rails のこのバージョン以降では動かなくなります:
# NG: もう存在しないクラスに依存している
ActiveSupport::Multibyte::Chars.new("こんにちは")
ActiveSupport::Multibyte::Chars.u_unpack("あ")
some_string.mb_chars.class == ActiveSupport::Multibyte::Chars対処としては:
- そのまま Ruby の
String/String#length/String#sliceなどで代替できないか検討- 近年の Ruby は UTF-8 前提のマルチバイト文字列処理が十分強力です。
- どうしても
mb_chars系の機能が必要なら:- 現時点では
String#mb_charsがどう扱われるか(互換レイヤが残るか)は PR diff だけでは確定しませんが、既存の ActiveSupport の他の multibyte ユーティリティを利用する/外部 gem に移行する、などの検討が必要です。
- 現時点では
- gem やエンジンで
ActiveSupport::Multibyte::Charsを参照している場合は、この Rails バージョンをサポートするにはコード修正が必須になります。
3-2. ActiveRecord のクォート/サニタイズ周り
quote/sanitize_sql自体の表面 API は変わっていませんが、その内部でMultibyte::Charsを通さなくなったことで、極端なマルチバイト境界の切り詰め挙動が微妙に変わる可能性があります。- 一般的な利用(日本語・絵文字を含む文字列をバインドパラメータで渡す程度)では、実害はほぼ出ないと考えられますが、
- DB の制約ギリギリのカラム長を計算してトリムしているようなコード
- 特定バイト長での切り詰めを前提とした仕様 を持つアプリでは、テストを走らせて挙動確認しておくと安全です。
3-3. 将来バージョンへのアップグレード計画
- 既に
ActiveSupport::Multibyte::Charsは非推奨であるとアナウンスされていたため、**この削除は予定されていた互換性破壊変更(breaking change)**です。 - アップグレードガイド(
guides.rubyonrails.orgの release note / upgrading guide)にも追って記載されるはずなので、そこも合わせて確認するのがおすすめです。 - アプリや利用中の gem のコードベース内で:
"ActiveSupport::Multibyte::Chars""ActiveSupport::Multibyte""mb_chars"を grep しておき、Chars自体に依存していないか・代替可能かを確認しておくと移行がスムーズです。
- 参考情報 (あれば)
- この PR: https://github.com/rails/rails/pull/56777
- 過去の非推奨アナウンス/CHANGELOG (
ActiveSupport::Multibyte::Charsdeprecation)activesupport/CHANGELOG.mdに今回の削除が追記されているため、同じセクション周辺に過去の非推奨告知も記載されている可能性が高いです。
- Ruby 本体のマルチバイト処理:
Encoding.default_externalが UTF-8 前提であることString#each_char,String#size, 正規表現(/uフラグ)などでの Unicode 対応が十分成熟している点
これらを踏まえ、Rails が独自の multibyte ラッパークラスを持ち続ける必要性が減ったことが、この削除の背景にあります。
#56201 Load hook guard
マージ日: 2026/2/11 | 作成者: @gmcgibbon
- 概要 (1–2文で)
Rails のActiveSupport.on_loadフックが「アプリ初期化前」に実行されてしまう問題を検知するため、Rails::Railtieにロードフック用のガード機構guard_load_hooksが追加されました。これにより、フレームワーク自身および Railtie/Engine を持つ gem が、フックの早期実行をログ出力や例外で検知できるようになります。
- 変更内容の詳細
2-1. Rails::Railtie#guard_load_hooks の追加
railties/lib/rails/railtie.rb に新メソッド guard_load_hooks が追加されました。役割は「特定の load hook が Rails アプリケーション初期化前に呼ばれたら、それを検知してログ or 例外を発生させる」ことです。
おおまかなイメージ:
class MyRailtie < Rails::Railtie
# 例: Active Record 用の load hook にガードを付ける
guard_load_hooks :active_record, raise_on_early_load: false
end想定される挙動(概念的なもの):
ActiveSupport.on_load(:active_record)がどこかで評価される。- 実際にそのフックが「実行」されるタイミングで、Rails アプリがまだ
initialized?でなければ、- デフォルトではログに「:active_record がアプリ初期化前にロードされた」旨を警告として出す。
- 設定によっては
raise_on_early_load: trueのようにして例外を投げることも可能。
実装としては、ActiveSupport.on_load 自体を書き換えるのではなく、Railtie 側が hook 名ごとに監視用ラッパを登録し、フック発火時にアプリケーション状態を確認する、という形になっていると考えられます。
2-2. 各フレームワークへのガード適用
以下の Railtie / Engine で guard_load_hooks が呼ばれるようになり、Rails 標準コンポーネントの主要な load hook がガード対象になりました。
action_cable/engine.rbaction_mailbox/engine.rbaction_mailer/railtie.rbaction_controller/railtie.rbaction_dispatch/railtie.rbaction_text/engine.rbaction_view/railtie.rbactive_job/railtie.rbactive_model/railtie.rbactive_record/railtie.rbactive_storage/engine.rbactive_support/railtie.rb
たとえば ActiveRecord::Railtie では、:active_record 用の load hook に対してガードが設定されるようになっています。これにより、Active Record 関連のフックがアプリ初期化前に走った場合、ログや例外で気づけるようになります。
2-3. 設定オプションの追加
railties/lib/rails/application/configuration.rb に設定項目が追加され、guard_load_hooks の挙動をアプリ設定から制御できるようになったと考えられます。
テスト (configuration_test.rb) の差分から推測できる事項:
- 新しい config オプション(名前は PR 全文がないため正確には断言できませんが、例えば
config.guard_load_hooksやconfig.raise_on_early_load_hooksのようなもの)が追加されている。 - それを true/false で切り替えて、早期ロードが
- ログで済むのか
- 例外として扱われるのか を選択できる。
2-4. ドキュメント・ガイドの更新
guides/source/configuring.md に設定オプションの説明が追加されました。
ここに「load hook ガードを有効/無効にする方法」や「早期ロード時に raise させる設定」が記載されているはずです。
- 影響範囲・注意点
3-1. どんなときに影響を受けるか
ActiveSupport.on_load(:active_record)などの load hook 内で、アプリケーション初期化前に実行されるようなコードを書いている場合:- 今までは静かに動いていたものが、ログに警告が出るようになります。
- 設定次第では 例外が発生してブートが失敗する可能性があります。
- gem や社内ライブラリで Railtie/Engine を定義しており、その中で load hook を使っている場合:
- この PR と同様に
guard_load_hooksを組み込むことで、自分たちの hook の早期ロードを検知できるようになります。
- この PR と同様に
3-2. すぐに出そうな問題のパターン
以下のようなケースは、この PR によるガードで顕在化しやすくなります。
# config/initializers/some_initializer.rb
ActiveSupport.on_load(:active_record) do
# ここで Rails.application.config を前提にした処理をしているが、
# 実際にはブートより早く呼ばれてしまっている、など
end- これまでは「たまたま動いていた」ものが、「早期ロードしている」として警告/例外対象になる可能性があります。
- とくに、autoload によってモデルや AR 関連クラスが思わぬタイミングでロードされ、結果として load hook が起動してしまう、という潜在的な不具合・パフォーマンス問題が 検知可能になる という意図です。
3-3. 対応方針
- Rails アップグレード後に、ブート時やテスト時に
- 「XX がアプリケーション初期化前にロードされた」系のログ
- あるいは例外 を見つけた場合は、その load hook をどこから呼んでいるかを追い、初期化完了後に実行されるようにコードを整理するのが望ましいです。
- 一時的に問題を避けたい場合は、ガード設定を「ログのみ」にする・無効にする、といった回避も設定オプション経由でできるはずです(
configオプションのガイドを要確認)。
- 参考情報 (あれば)
- 元 PR: https://github.com/rails/rails/pull/56201
- 参考 PR(作者がインスパイア元として挙げているもの): https://github.com/rails/rails/pull/46047
- こちらも load hook や初期化タイミングに関する議論が含まれているため、あわせて読むと背景理解に役立ちます。
この変更は、「早期ロードによるバグやパフォーマンス劣化」を表面化させるものなので、短期的にはノイズが増える可能性がありますが、中長期的にはアプリや gem の初期化順序を健全に保つ助けになります。
#56778 Deprecate Mail::Address.wrap
マージ日: 2026/2/11 | 作成者: @gmcgibbon
- 概要 (1-2文で)
Mail::Address.wrapが Rails 8.2 で削除予定の非推奨APIとしてマークされました。Action Mailbox 側でこのメソッドを使っている箇所とテスト・CHANGELOGに、非推奨であることが明示されています。
- 変更内容の詳細
※PR本文自体は短く、「未使用パッチなので非推奨にする」という意図のみですが、diff から読み取れる典型的な内容を整理します。
Mail::Address.wrapの非推奨化Action Mailbox が内部で使っている
Mail::Address.wrapに対して、Rails 8.2 で削除予定である旨の deprecation を追加。典型的には以下のような形で警告を出す処理が追加されていると考えられます(実際のコードイメージ):
ruby# action_mailbox/lib/action_mailbox/mail_ext/address_wrapping.rb module ActionMailbox module MailExt module AddressWrapping def wrap(address) ActiveSupport::Deprecation.warn( "Mail::Address.wrap は Rails 8.2 で削除されます。代わりに XXX を利用してください。" ) super end end end end実際の文言や置き換え候補はPR本文には書かれていないため、ここは概念的な例です。
CHANGELOG の更新
actionmailbox/CHANGELOG.mdに以下のようなエントリが追加:Mail::Address.wrapが非推奨であり、8.2 で削除予定であること- Action Mailbox 利用者向けの注意喚起
テストの更新
actionmailbox/test/unit/mail_ext/address_wrapping_test.rbが更新され、非推奨警告が正しく出ること、あるいは新しい挙動に沿ったテストに変更。例としては以下のようなテスト変更が想定されます:
rubydef test_wrap_is_deprecated assert_deprecated do Mail::Address.wrap("user@example.com") end end実際のテストのメソッド名・内容は若干異なる可能性がありますが、行数の増減 (+5, -3) から、既存テストに deprecation 周りのアサーションが足された程度の小さな変更です。
- 影響範囲・注意点
影響を受ける可能性がある人
- アプリケーションやライブラリ側で 直接
Mail::Address.wrapを呼び出している 場合 - Action Mailbox の mail 拡張 (
ActionMailbox::MailExt) を独自に monkey patch している場合
- アプリケーションやライブラリ側で 直接
実行時の変化
- Rails 8.1 系(もしくはこのPRが入る次のマイナーバージョン)から、
Mail::Address.wrap呼び出し時に deprecation warning が出るようになります。
- 8.2 で 完全削除される予定 なので、8.2 に上げると NoMethodError などになる可能性があります。
- Rails 8.1 系(もしくはこのPRが入る次のマイナーバージョン)から、
対応方針
- 自前コードで
Mail::Address.wrapを使っているか検索し、代替実装への置き換えを検討してください。多くの場合、単純には
Mail::Address.newで十分なケースが多いです:ruby# 以前: address = Mail::Address.wrap("user@example.com") # 代替案の一例: address = Mail::Address.new("user@example.com")wrapに固有の挙動(配列を受け取って複数アドレスに対応する、nilを許容する等)に依存している場合は、その部分を自前ロジックで実装する必要があります。
- ライブラリ側で
Mail::Address.wrapを public API として露出している場合は、- ライブラリのドキュメントに「非推奨 API に依存している」旨を明記し、
- 将来の Rails 8.2 対応版で置き換える方針を検討するべきです。
- 自前コードで
- 参考情報 (あれば)
- 該当PR: https://github.com/rails/rails/pull/56778
Mail::Addressはmailgem のクラスであり、Rails Action Mailer / Action Mailbox が内部で利用している。- 近年の Rails コアでは、Action Mailer / Action Mailbox 周りでの「Rails 非依存な mail gem API」や未使用パッチの整理が継続的に行われており、この PR もその一環と考えられます。
#56767 Don't eagerly cache JSON Encoder in ActiveRecord::Type::Json
マージ日: 2026/2/11 | 作成者: @xathien
- 概要 (1-2文で)
ActiveRecord::Type::Json が JSON エンコーダを「クラス読み込み時に自前でキャッシュ」するのをやめ、ActiveSupport::JSON 側のキャッシュ済みエンコーダとwithout_escapingオプションをそのまま使うように変更した PRです。これにより、アプリ側で JSON エンコーダを後から上書き(カスタマイズ)できるようになります。
- 変更内容の詳細
※ 実際の diff は 1 行追加・3 行削除と小さい変更です。
これまでの問題点
ActiveRecord::Type::Json は、クラスが最初に参照されたタイミングで JSON エンコーダ(例: ActiveSupport::JSON::Encoding::JSONGemEncoder など)を取得し、クラスレベルでキャッシュしていました。
疑似コードで表すとおおよそ以下のようなイメージです:
# 変更前イメージ(擬似)
module ActiveRecord
module Type
class Json < ActiveModel::Type::Value
ENCODER = ActiveSupport::JSON::Encoding.json_encoder
def serialize(value)
ENCODER.new.encode(value)
end
end
end
endこのように「クラス読み込み時に固定」してしまうと、
# アプリケーション側であとからエンコーダを差し替えたい
ActiveSupport::JSON::Encoding.json_encoder = MyCustomJsonEncoderのような変更を行っても、Json 型はもともと取得した古い ENCODER を使い続けてしまい、エンコーダの上書きが反映されません。
今回の変更
この PR では、Json 型自身がエンコーダをキャッシュするのをやめて、ActiveSupport::JSON が内部で持つ「キャッシュ済みエンコーダ」と without_escaping オプションを利用するようにしました。
イメージとしてはこんな形に近づきます:
# 変更後イメージ(擬似)
module ActiveRecord
module Type
class Json < ActiveModel::Type::Value
def serialize(value)
ActiveSupport::JSON.encode(value, escape: :without_escaping)
# もしくは:
# ActiveSupport::JSON::Encoding.json_encoder
# .new(without_escaping: true)
# .encode(value)
end
end
end
endポイントは以下の2つです。
- 「エンコーダのインスタンス/クラス」を
ActiveRecord::Type::Json側で固定しない ActiveSupport::JSONが持つ- 「選択されたエンコーダのキャッシュ」
without_escaping(HTML エスケープしない) のビルド済みオプション をそのまま利用する
これにより、アプリ側で
ActiveSupport::JSON::Encoding.json_encoder = MyCustomJsonEncoderのように設定すると、その後の ActiveRecord::Type::Json のシリアライズでも新しいエンコーダが使われるようになります。
- 影響範囲・注意点
影響範囲
- ActiveRecord の JSON 型(
t.jsonカラムやserialize :data, JSONに相当する部分)で使われる JSON シリアライズ処理。 - 特に、「
ActiveSupport::JSON::Encoding.json_encoderをアプリケーション側で差し替えている」ケースに影響します。- これまで: 差し替えても
ActiveRecord::Type::Jsonには反映されなかった。 - これから: 差し替えが正しく反映され、期待どおりカスタムエンコーダが使われる。
- これまで: 差し替えても
- ActiveRecord の JSON 型(
互換性
- ActiveSupport::JSON が提供するキャッシュ済みエンコーダを使うだけなので、デフォルト設定で使っている限りは挙動の変化はほぼありません。
- 逆に「
Json型だけは昔のエンコーダのままでいてほしい」といった、非常に特殊な依存をしていた場合は、挙動が変わる可能性があります(一般的には望ましい修正)。
パフォーマンス
- ActiveSupport 側でエンコーダはすでにキャッシュされており、
Json型で二重にキャッシュする必要はありません。 - そのため、パフォーマンス的な悪化は基本的に想定されず、むしろ実装の一貫性が増しています。
- ActiveSupport 側でエンコーダはすでにキャッシュされており、
テストについて
- PR 本体では新規テスト追加はまだ行われていません(チェックリストでも
Testsが未チェック)。 - 適切なテストとしては、以下のようなケースをカバーするとよい:
ActiveSupport::JSON::Encoding.json_encoderをカスタムクラスに変更したあとで、ActiveRecord::Type::Json#serializeがそのカスタムエンコーダ経由で呼ばれているかを確認する。
- PR 本体では新規テスト追加はまだ行われていません(チェックリストでも
- 参考情報 (あれば)
- 対応 Issue: [Fix #56766]
→ 「ActiveRecord::Type::Json で JSON エンコーダを後から上書きできない」不具合の修正 PR。 - 関連箇所:
activerecord/lib/active_record/type/json.rbActiveSupport::JSONおよびActiveSupport::JSON::Encoding.json_encoderの実装。
カスタムエンコーダを使っているプロジェクトでは、この PR により「ActiveRecord と ActiveSupport の JSON エンコード戦略の一貫性」が向上します。
#56761 EventReporter: filter events before building the payload
マージ日: 2026/2/11 | 作成者: @byroot
- 概要 (1-2文で)
このPRは、ActiveSupport::EventReporterで「イベントペイロードのフィルタリング」を、ペイロード構築の後ではなく前に行うように変更し、無駄なオブジェクト生成コストを削減するものです。これにより、AS::Notifications 由来の「フィルタによる最適化」が、実際にオーバーヘッド削減として意味を持つようになります。
- 変更内容の詳細
背景
- 以前のPR(#55900)の結果として、
- イベント発火時に「とりあえずフルのペイロードを構築 → フィルタで削る → 購読者(subscriber)が必要かどうか確認」という順序になっていました。
- そのため、購読者がそもそもいなくてもペイロード構築コストを丸ごと払う状況になっていました。
ActiveSupport::Notificationsのもともとの設計では、- フィルタや購読状況を見て 不要ならペイロード構築をそもそもしない、という形でオーバーヘッド削減をしていました。
- 現状の EventReporter では、フィルタを登録しても「ペイロード構築の無駄」を避けられないため、フィルタ機能がパフォーマンス観点では意味をなしていない、という問題意識があります。
このPRでのコア変更点
activesupport/lib/active_support/event_reporter.rb への主な変更は次の通りです(概念ベース):
イベントペイロードを組み立てる順序の変更
- 以前:
- フルのペイロードを構築
- フィルタでマスキング・削除等の処理
- 購読者に配信すべきか判定 / 配信
- 変更後:
- 購読者やフィルタ情報を元に「必要な形」を把握
- その要件に沿ってペイロードを構築(あるいは最小限に抑える)
- フィルタを適用しつつ購読者に配信
これにより、「どうせ誰も見ない / ほとんど使わない情報」を構築するコストを省けるようになります。
- 以前:
フィルタの意味づけの修正
- 以前は「ペイロードを後から編集・フィルタするだけ」のロジックだったのに対して、
- 今回の変更により、「ペイロード構築前に、どの情報が必要かを決めるためにフィルタを利用する」という、本来 AS::Notifications が意図していた用途に近づけています。
CHANGELOG への追記
activesupport/CHANGELOG.mdにこの挙動変更が追記されています。- 著者自身も説明文中で「技術的には breaking change(挙動変更)」と認めており、CHANGELOG でもその旨が(互換性への影響として)記録されているはずです。
簡略イメージコード(概念)
※実際のコードとは異なる擬似コードですが、挙動の違いを表します。
# 変更前(イメージ)
def report(name, payload_builder)
full_payload = payload_builder.call # ここでコスト全払い
filtered_payload = apply_filters(full_payload) # 後から削るだけ
notify_subscribers(name, filtered_payload)
end
# 変更後(イメージ)
def report(name, payload_builder)
# フィルタや購読状況から必要な情報を推定(例: keys, fields など)
build_context = compute_build_context(name, filters, subscribers)
# build_context に基づいて「必要なものだけ」構築させる
payload = payload_builder.call(build_context)
filtered_payload = apply_filters(payload)
notify_subscribers(name, filtered_payload)
end実際には build_context 相当の情報の扱いなどはもっと細かいですが、
「フィルタ・購読状況 → それを踏まえてペイロードを作る」という流れに変わった、という理解で概ね問題ありません。
- 影響範囲・注意点
挙動上の breaking change の可能性
- 著者も認めているように、「フィルタの適用タイミング」が変わるため、
- ペイロード生成ロジック(特に副作用を伴うもの)
- フィルタが前提としていたペイロードの内容・形
に影響が出る可能性があります。
- 通常はペイロードを「純粋なデータ生成」として扱っていれば問題になりにくいですが、もし「必ずこの時点でペイロードがフルに構築されている」ことを前提にしたコードがあれば、その前提が崩れる可能性があります。
- 著者も認めているように、「フィルタの適用タイミング」が変わるため、
パフォーマンスへのポジティブな影響
- イベント数が多く、かつペイロード構築が高コストなケース(DBからの追加クエリ、重いオブジェクト生成など)では、不要なイベント・不要なフィールド分のコスト削減が期待できます。
- 特に「本番環境ではサブスクライバをほとんど登録していない」「特定のイベントだけを絞って監視している」ようなケースで効果が出やすいです。
フィルタ実装・利用者への注意
EventReporterのフィルタを使っている場合、- 「ペイロード構築前か後か」に依存した奇抜な実装をしていないか
- フィルタが「存在しないキーや値」を前提としていないか を確認した方が安全です。
- ただし、このPRの主眼は「ペイロード構築順序と無駄の削減」であり、フィルタAPIの表面的なシグネチャが大きく変わるわけではないため、多くの通常利用コードはそのまま動くと考えられます。
- 参考情報 (あれば)
- 対応する前提PR:
- https://github.com/rails/rails/pull/55900
(これにより、いったん「必ずペイロードを作ってからフィルタ」という流れになっていた)
- https://github.com/rails/rails/pull/55900
- 対象ファイル
activesupport/lib/active_support/event_reporter.rbactivesupport/CHANGELOG.md
- 著者のコメント上の論点
- AS::Notifications から移植されたフィルタ機能が、「ペイロード構築をスキップできない」という点で意味をなしていないと判断し、その設計意図(オーバーヘッド削減)に沿うように挙動を修正している。
#56769 Bump Valkey Redis Image version to 9
マージ日: 2026/2/11 | 作成者: @akhilgkrishnan
- 概要 (1-2文で)
Valkey の Redis コンテナイメージのバージョンを、Rails リポジトリ内で使っている devcontainer と CI 設定一式で「9」系の最新版に統一して引き上げた PRです。開発用コンテナ、Rails アプリ/プラグイン ジェネレータのテンプレート、そしてそれらを検証するテストが、すべて Valkey 9 を前提に動くようになります。
- 変更内容の詳細
この PRがやっていることは「Valkey イメージのタグを書き換える」一点にほぼ集約されています。
対象となった場所
- 開発者向け devcontainer 設定
.devcontainer/compose.yamlrailties/lib/rails/generators/rails/devcontainer/templates/devcontainer/compose.yaml.ttrailties/test/fixtures/.devcontainer/compose.yaml
- CI / デプロイ関連テンプレート
railties/lib/rails/generators/rails/app/templates/github/ci.yml.ttrailties/lib/rails/generators/rails/plugin/templates/github/ci.yml.ttrailties/lib/rails/generators/rails/app/templates/config/deploy.yml.tt
- テストコード
railties/test/generators/app_generator_test.rbrailties/test/generators/devcontainer_generator_test.rb
これらのファイルで指定されていた Valkey の Docker イメージが、旧バージョン(例: valkey/valkey:8 など)から最新の valkey/valkey:9 に変更されています。
具体的には、以下のような差分が入っていると考えてよいです:
# 例: devcontainer や CI の redis サービス定義
services:
redis:
image: valkey/valkey:9
# 以前は valkey/valkey:8 などの指定だったテンプレートの変更に合わせて、それを前提としているテスト (app_generator_test.rb, devcontainer_generator_test.rb) 内の期待値も、Valkey 9 のイメージタグを参照するように1行ずつ更新されています。
- 影響範囲・注意点
影響範囲
- Rails 本体のランタイムコードには手は入っていないため、Rails アプリケーションの挙動そのものには影響しません。
- 次のような「テンプレートを使う人」や「公式の devcontainer/CI 設定に従う人」に影響があります:
- Rails の devcontainer テンプレートから新規プロジェクトを作る場合
rails newによって生成される GitHub Actions CI 設定(github/ci.yml)を利用する場合rails plugin newで作るプラグインの CI 設定テンプレートを利用する場合- Rails リポジトリの
.devcontainerを使って開発環境を構築している場合
注意点
- Valkey 9 の互換性
基本的に Redis プロトコル互換なので、通常の Rails + Redis (Valkey) を使ったキャッシュやセッション用途では問題は起きにくいと想定されますが、以下のような場合は要注意です:- 特定の Redis バージョン依存の挙動を前提にしたコードやテストがある
- 古い Valkey/Redis イメージでのみ再現するバグや仕様に依存している
- CI・devcontainer が突然落ちる可能性
- ローカル環境や CI 環境がキャッシュしている古いイメージタグを前提にしている場合、タグ更新後に
docker composeや CI が新しいイメージを pull し直すことで、一時的にビルド時間が伸びる可能性があります。 - 非公式ミラーやカスタムレジストリを使っている場合、
valkey/valkey:9が存在しないと CI で失敗することがあります。その場合はレジストリ側の対応が必要です。
- ローカル環境や CI 環境がキャッシュしている古いイメージタグを前提にしている場合、タグ更新後に
- 参考情報 (あれば)
- Valkey 公式 Docker Hub
- https://hub.docker.com/r/valkey/valkey/
ここから9タグの正式サポート状況や、8からの変更点を確認できます。
- https://hub.docker.com/r/valkey/valkey/
- Rails のジェネレータが生成する CI/devcontainer 設定をベースに自前の設定をカスタマイズしている場合、この PR を機に自分のリポジトリ側でも Valkey イメージのバージョンを揃えておくと、今後の Rails 本体との齟齬が減らせます。
#56768 Fix Marshal deserialisation of Integer type from Rails 8.0
マージ日: 2026/2/11 | 作成者: @RemoteCTO
- 概要 (1-2文で)
Rails 8.0 で Marshal シリアライズしたActiveModel::Type::Integerオブジェクトを Rails 8.1 でデシリアライズするとNoMethodError: undefined method '<=' for nilが発生するリグレッションを修正した PRです。8.0 → 8.1 へのアップグレード時に、キャッシュ等に Marshal された型オブジェクトが残っている環境での互換性問題を解消します。
- 変更内容の詳細
問題の原因
元のパフォーマンス改善 PR (#53470) で、ActiveModel::Type::Integer の初期化処理が以下のように変更されていました:
- 以前:
@rangeという Range オブジェクトで最大/最小値を管理 - 変更後:
@max/@minという 2 つのインスタンス変数で数値として管理
ところが、Marshal.load は initialize を呼ばずにインスタンス変数をそのまま復元します。そのため:
- Rails 8.0.x でシリアライズされたオブジェクト:
@rangeはあるが@max/@minは存在しない状態で保存されている - Rails 8.1.x のコードは
@max/@minを前提にしている
結果として:
def out_of_range?(value)
# イメージ
value < @min || @max < value
endのような処理を行う際に、@min / @max が nil のままになり、nil <= value が呼ばれて NoMethodError になる、という問題が発生していました。
修正内容
@max / @min が nil の場合に、max_value / min_value から「遅延初期化」するガードを追加しています。
概念的には次のようなコードが追加されたイメージです(簡略化した擬似コード):
def out_of_range?(value)
# 8.0 時代に Marshal されたオブジェクトを 8.1 で読むと
# @max / @min が nil のままなので、ここで一度だけ復元する
if @max.nil? || @min.nil?
@max = max_value # limit から計算される
@min = min_value # limit から計算される
end
value < @min || @max < value
endポイント:
max_value/min_valueは親クラス (Value) によってlimitから計算されるもので、limit自体は Marshal によって正しく復元されている- そのため、
limitから改めて@max/@minを計算し直せば、8.0 時代のシリアライズ済みオブジェクトも正しく動く - ガードは一度通れば
@max/@minがセットされるため、通常の実行パスの性能には影響しない(初回だけのデグレードパス)
テスト
activemodel/test/cases/type/integer_test.rb にテストが追加されています。内容的には以下のようなケースをカバーしているはずです:
- Rails 8.0 形式(
@rangeを持ち、@max/@minがない)に相当するオブジェクトを用意し、Marshal シリアライズ → デシリアライズ - その後
castなどout_of_range?を内部的に呼ぶ処理を実行してもNoMethodErrorが起きず、期待通りに動作することを確認
- 影響範囲・注意点
影響を受けるケース
- Rails 8.0.x で
ActiveModel::Type::IntegerオブジェクトをMarshal.dumpしてどこかに保存している(典型例: キャッシュ、スキーマキャッシュ、属性ビルダーのキャッシュなど) - そのまま Rails 8.1.x にアップグレードし、既存の Marshal データを
Marshal.loadして利用している - かつ、デフォルトの marshalling format (6.1) を利用しているケースが特に影響を受ける
- Rails 8.0.x で
ActiveRecord オブジェクトについて:
- marshalling format を 7.1 に上げると、
ActiveRecord::Baseオブジェクトについては問題をある程度回避できるが、Typeオブジェクトは他の文脈(スキーマキャッシュなど)にも現れるため、根本的にはTypeクラス自体の互換性が必要 → 本 PR がその対応
- marshalling format を 7.1 に上げると、
既知のワークアラウンド
- 「キャッシュを全部捨てる(flush)」ことで問題は解消できるが、マイナーバージョンアップでそれを必須にしたくない、という考えからコード側での後方互換性対応が追加された
性能面
- ガードは「
@max/@minがnilの場合のみ」実行され、一度値がセットされれば以降は通常通りの高速パスで動作する - したがって、通常の 8.1 以降ネイティブなオブジェクトに対しては性能劣化はほぼ無視できる
- ガードは「
- 参考情報 (あれば)
- この PR が修正しているリグレッション:
- #53470 (Integer 型のパフォーマンス最適化による副作用)
- 対象バージョン:
- Rails 8.1 系での利用を想定
- PR 内で
8-1-stableへのバックポート希望が述べられているため、8.1.x 系への反映が期待される
- 運用上のヒント:
- すでに 8.1 に上げていて、
undefined method '<=' for nilが Integer 型周りで出ている場合:- このパッチを取り込む(または 8.1.x の該当修正版にアップデートする)
- もしくは暫定的に該当キャッシュ(スキーマキャッシュ / Rails.cache など)を flush することで応急対応可能
- 今後のアップグレード時も「型オブジェクトなど内部オブジェクトの Marshal 互換性」による不具合が起こりうるため、キャッシュと Marshal 利用箇所の洗い出しを行っておくと安全です。
- すでに 8.1 に上げていて、
#56763 Fix rdoc code formatting
マージ日: 2026/2/11 | 作成者: @tjschuck
- 概要 (1-2文で)
Rails のActionView::Helpers::TextHelper#highlightの RDoc コメント内のコード例について、インラインコードのマークアップ方法を+...+から<tt>...</tt>に変更し、公式ドキュメント上でコードが正しく整形・表示されるようにする修正です。機能仕様や挙動には一切変更はなく、ドキュメント表示のみを直す PR です。
- 変更内容の詳細
対象ファイル:
actionview/lib/action_view/helpers/text_helper.rb変更内容:
highlightメソッドの RDoc コメント中に記載されている「少し複雑なコードスニペット」のマークアップを、+code+形式から<tt>code</tt>形式に変更しています。背景:
- RDoc ではインラインコードとして
+code+という書き方ができますが、- 文字列中に
+を含む - あるいは記号・正規表現・ブロック付き呼び出しなどが入り組んだ「複雑なスニペット」
では、+...+だけではパースに失敗したりレイアウトが崩れたりすることがあります。
- 文字列中に
- この PR の説明で挙げられている URL(edgeapi の
TextHelper#highlight)では、まさにその崩れが発生しており、コード例が途中で途切れたり HTML として誤解釈されていました。
- RDoc ではインラインコードとして
修正のイメージ:
実際の行は 1 行だけ差し替えですが、やっていることは以下のような変換です(擬似例):rdoc# 変更前(うまく解釈されていなかった) # +highlight(text, ['rails', 'Ruby'], highlighter: '<strong class="highlight">\1</strong>')+ # # 変更後(<tt> でインラインコードを明示) # <tt>highlight(text, ['rails', 'Ruby'], highlighter: '<strong class="highlight">\1</strong>')</tt>+...+が、RDoc の「インラインコード」かつ「テキストの強調」のショートハンドなのに対し、<tt>...</tt>は HTML タグとしてより直接的・確実に「等幅フォントのコード」として扱われます。そのため、特殊文字を含む複雑なスニペットでも崩れにくくなります。
- 影響範囲・注意点
ランタイム挙動:
- Ruby/Rails の実行時挙動には一切影響しません。
TextHelper#highlightの実装ロジックや API 仕様は変わっていません。
- Ruby/Rails の実行時挙動には一切影響しません。
影響するのはドキュメントのみ:
edgeapi.rubyonrails.orgなど RDoc 生成済み API ドキュメントにおける、highlightの説明セクションの見た目と整形のみが変わります。- これまで崩れていたコードサンプルが、1 行のインラインコードとして正しく表示されるようになります。
プロジェクト側での注意点:
- Rails 本体に限らず、自分のライブラリやアプリで RDoc を利用している場合、
- 複雑なコード例(正規表現、ブロック、ネストした
'/"など)を+...+で囲んでいると同様の問題が起きる可能性があります。 - そうした箇所は
<tt>...</tt>(または複数行なら:markup: rdocのコードブロック)への切り替えを検討するとよいです。
- 複雑なコード例(正規表現、ブロック、ネストした
- Rails 本体に限らず、自分のライブラリやアプリで RDoc を利用している場合、
- 参考情報 (あれば)
該当ドキュメント(修正前の表示例)
https://edgeapi.rubyonrails.org/classes/ActionView/Helpers/TextHelper.html#method-i-highlightRDoc でのインラインコード表現について(概要)
+code+… シンプルなインラインコード・強調向け。単純な識別子や短い式に適している。<tt>code</tt>… HTML として扱われるため、RDoc のパーサを回避したい複雑なスニペットに有効。
この PR は、そのベストプラクティスを Rails 本体のドキュメントにも適用した形になっています。
#56748 Consistently raise deprecation warnings in CI
マージ日: 2026/2/10 | 作成者: @skipkayhil
- 概要 (1-2文で)
Rails の各フレームワーク(Action Pack / Action View / Action Mailer / Active Model / Active Record)で、CI 環境における「非推奨機能(deprecation)の扱い」を、Active Support と同様に「ログではなく例外を投げてテストを落とす」挙動に統一した PR です。
これにより、CI 上で非推奨機能の利用が見逃されにくくなり、非推奨化を導入した PR の時点で対応を強制しやすくなります。
- 変更内容の詳細
2-1. 何をしている PR か
- 以前のコミットで Active Support の deprecator は「CI では
raiseする」ように変更されていたが、他のフレームワークはまだ「フルバックトレース付きでログ出力するだけ」の設定になっていた。 - この PR では以下を行っている:
- Action Mailer / Action Pack / Action View / Active Model / Active Record のテスト環境設定で、deprecator の挙動を「ログ出力 → 例外を投げる」に変更
- Rails 内部で利用している一部依存フレームワークの deprecator 設定も追加し、全体として CI 上では deprecation を「テスト失敗」として扱う方針に揃えた
※実際のコード上では、ActiveSupport::Deprecation.behavior 相当の設定が「:log」や「バックトレース付きログ」から「:raise」や同等機能へ差し替えられているイメージです。
2-2. ファイルごとの変更方向性
差分はすべて「テスト用のセットアップコード」に集中しています。
actionmailer/test/abstract_unit.rb- Action Mailer のテスト全体共通セットアップで、deprecator の出力方法を「バックトレース付きログ」から「例外を投げる」設定へ変更。
actionpack/test/abstract_unit.rbactionpack/test/controller/render*_test.rb系 4 ファイル- Action Pack (特にコントローラの render 系テスト) の共通設定・個別テストで、deprecator の挙動を「raise」に変更。
render_js_test,render_json_test,render_test,render_to_string_test,render_xml_testはいずれも「deprecation を含んだレンダリングパス」が通る可能性があるため、それらが発生したらテストを即座に失敗させるようになる。
actionview/test/abstract_unit.rbactionview/test/actionpack/controller/render_test.rb- Action View 側も同様に、ビュー関連テストで deprecation を例外化する設定に切り替え。
activemodel/test/cases/helper.rb- Active Model のテストヘルパでも deprecator 設定を「例外を投げる」に変更。
activerecord/test/support/global_config.rb- Active Record のグローバルなテスト設定内で、Active Record 依存の deprecator 設定を強化・追加。
- これまで設定されていなかった依存関係(例: Rails 外部の deprecation 機構や、サブコンポーネント用の deprecator)がある場合、それらにも一貫して CI で
raiseさせるようにしている。
- 影響範囲・注意点
3-1. Rails 本体開発・コントリビュータ視点
- 影響対象は Rails 本体のテストスイート およびそれをベースにしている開発環境が主です。
- CI 上で deprecation を発生させるような変更を加えると、ログに流れるだけではなくテストが失敗する ようになります。
- そのため、「古い API を呼ぶだけでテストが落ちる」ケースが増えます。
- 非推奨化を行う PR では、以下のような対応を同時に行う必要があります:
- 既存テストを新しい API に書き換える
- どうしても旧 API を使う必要がある場合は、それを明示的に許容する仕組み(
silence_deprecationなど)で囲む
3-2. Rails アプリケーション開発者への間接的な影響
- この PR 自体は Rails 本体のテスト設定 の話なので、Rails を使うアプリケーションの挙動が直ちに変わるわけではありません。
- ただし、Rails プロジェクトとしての方針は次のように読み取れます:
- 「CI では deprecation を単なる警告でなく、エラーとして扱っていく」方向へ舵を切っている
- 将来的に、
config.active_support.deprecation = :raiseのような設定を CI で有効にすることが、一般的なベストプラクティスとして推奨されていく可能性が高い
アプリケーション側でも、CI での安全性を高めたい場合は、以下のような設定を検討できます:
# config/environments/test.rb または CI 用設定など
config.active_support.deprecation = :raise
# あるいは、フレームワーク個別の deprecator を明示的に raise に設定する3-3. テストの書き方に関する注意
- deprecation を発生させること自体をテストしたいケース(「このメソッドは非推奨であることを確認したい」など)では、
assert_raisesなどで明示的に例外を検証する必要があります。 - 今後 Rails 本体のテストを読む際、「deprecation を期待しているテスト」は、例外を捕捉する形で書き直されている可能性があります。
- 参考情報 (あれば)
PR 説明で参照されている過去コミット(Active Support 側の変更)
- https://github.com/rails/rails/commit/9bf81e4ba99f90566a456326bb9311fa5902d28a
ここで Active Support の deprecator が「CI で raise」するように変更されており、本 PR はそのポリシーを他フレームワークに波及させたものです。
- https://github.com/rails/rails/commit/9bf81e4ba99f90566a456326bb9311fa5902d28a
Rails の deprecation ハンドリングの一般的な設定例
config.active_support.deprecation = :log# ログに出す(従来型)config.active_support.deprecation = :silence# 無視config.active_support.deprecation = :raise# 例外として扱う(本 PR が採用している方向)
#56762 ActiveSupport::ParameterFilter: optimize redundant patterns out
マージ日: 2026/2/10 | 作成者: @byroot
- 概要 (1-2文で)
ActiveSupport::ParameterFilter のプリコンパイル処理で、意味的に重複しているフィルタパターンを取り除く最適化が追加されました。これにより、特に Active Record Encryption によって大量の深いフィルタが自動登録されるケースで、パラメータフィルタリングの性能劣化を抑えられます。
- 変更内容の詳細
背景
- Active Record Encryption は、暗号化カラムごとに「深い」パターンを ActiveSupport::ParameterFilter に登録します(例:
user[credit_card][number]のようなネストされたキーをまとめてマスクできるようなパターン)。 - 暗号化カラムが多いアプリケーションでは、このフィルタパターンのリストが急激に増大し、毎回のパラメータフィルタリング処理がボトルネックになり得ます。
- 特に、カラム名に共通のサフィックス(
*_tokenなど)が付いていて、かつtoken自体がすでにフィルタ対象になっていると、「より広いパターン」と「より狭い(具体的な)パターン」が両方登録されてしまい、後者が事実上「死んだコード」になります(どうせ上位パターンで全部マスクされるため)。
今回の最適化の方向性
- ParameterFilter には「プリコンパイルステップ」があり、渡されたフィルタ条件(文字列/シンボル/正規表現/Proc など)を内部表現に変換しています。
- このプリコンパイル段階で「どうせ上位のフィルタに含まれているパターン」を検出し、リストから削除する「dead code elimination」を行うようにしました。
※ PR本文の例:
- 多くの暗号化カラム:
access_token,refresh_token,reset_password_tokenなど - すでに
tokenがフィルタ対象 (config.filter_parameters << :token) の場合、:access_token,:refresh_tokenなどを深いパターンで登録しても、tokenで包括的にマスク可能- そのため、これらの細かいパターンは冗長であり、フィルタリストから落としてもマスク結果は変わらない
具体的なコードイメージ(擬似例)
たとえば、アプリケーションで次のような設定をしていたとします:
Rails.application.config.filter_parameters += [
:token, # 既に広いパターン
:access_token,
:refresh_token,
/.*_token/, # さらに広い正規表現パターン
]プリコンパイル前は、ほぼ同じ意味のパターンが多数リストに積み上がっていましたが、
この PR 以降は、プリコンパイル段階で次のように削られます(イメージ):
:tokenと/.*_token/でマスク範囲が網羅されている場合、access_token,refresh_tokenなどの個別パターンは「どうせマスクされる」ので削除- 結果として、内部的なフィルタリストは最小限のパターン構成になり、ループやマッチング回数が減って高速化
テストの更新
activesupport/test/parameter_filter_test.rbが更新され、- 冗長なパターンが自動的に除去されても、最終的なフィルタリング結果(マスクされるキー・値)が以前と変わらないこと
- どのような組み合わせのパターンであれば「冗長」と見なされるか
を検証するテストが追加・修正されています。
- これにより、「最適化しても挙動(出力のマスク結果)は同じである」ことが保証されています。
- 影響範囲・注意点
主な影響範囲
ActiveSupport::ParameterFilterを利用するすべての箇所(典型的には Rails のログ出力時のパラメータマスク)。- 特に、Active Record Encryption を多用しており、暗号化カラム名に共通サフィックスが付いているようなアプリケーションで効果が大きいです。
- 例:
*_token,*_ciphertextなど
- 例:
開発者目線でのポイント
機能的な挙動は変わらない前提
- 意図としては、冗長パターンを削除しても「どのパラメータがマスクされるか」は変わりません。
- 最適化の条件判定がバグらない限り、ログの秘匿性(セキュリティ)は維持されます。
パフォーマンス改善が期待できるケース
config.filter_parametersが非常に長くなっている。- Active Record Encryption が大量の「深いフィルタパターン」を自動追加している。
- 大きなネスト構造のパラメータ(API payload、フォーム送信など)を頻繁にログ出力している。
→ こうしたケースで、リクエストログ出力の CPU 使用量や応答時間が多少改善される可能性があります。
カスタムな ParameterFilter 拡張をしている場合の注意
ActiveSupport::ParameterFilterを独自に継承・モンキーパッチしている場合、- プリコンパイルロジックの内部構造に依存しているコードがあれば、衝突の可能性があります。
- ただし、この PR は基本的に「より小さいパターンを大きいパターンに吸収する」だけなので、
- 公開 API を正しく使っている限り、表向きのインターフェース変更はほぼ無いと考えられます。
デバッグ時の見かけの違い
config.filter_parametersに多くの要素を追加していても、- 内部的なプリコンパイル済みフィルタ構造をデバッグした際には、以前より短く見える可能性があります(冗長分が削除されるため)。
- ただし実際のマスク結果は変わらない想定です。
- 参考情報 (あれば)
- 対象 PR: https://github.com/rails/rails/pull/56762
- 関連領域:
- ActiveSupport::ParameterFilter(パラメータログのマスキング機構)
- Active Record Encryption(暗号化属性のための自動フィルタ登録)
- パフォーマンスに悩んでいる場合の確認ポイント:
config.filter_parametersの内容・長さ- 1 リクエストあたりにログに出ているパラメータ構造の複雑さ
ParameterFilter周りに独自の拡張がないかどうか
#56759 Avoid combinatory explosion of Rails.app.config.filter_parameters
マージ日: 2026/2/10 | 作成者: @byroot
- 概要 (1-2文で)
Rails 8.1 でRails.application.config.filter_parametersと Active Record の暗号化属性が組み合わさることで、フィルタ対象の属性リストが爆発的に増え、ログ・イベントのフィルタ処理が極端に遅くなる問題がありました。
このPRは、その「組み合わせ爆発」が起きないようにfilter_attributesの扱いを修正し、フィルタ処理のパフォーマンスを大幅に改善します。
- 変更内容の詳細
問題の背景
- Rails 8.1 で導入された変更(#55251)により、
ActiveRecord::Base.filter_attributesは初期値としてRails.application.config.filter_parametersを引き継ぐようになった。 - Active Record Encryption を使うと、モデルごとの暗号化属性名が
filter_attributesに追加される際に「モデル名でネームスペース」される。- 例:
Userモデルの:emailが暗号化されているとuser.emailのような形で追加される(実際の形式は内部実装に依存)。
- 例:
- 問題は、暗号化属性を追加する処理が
self.filtered_attributes += [attr]のような形で行われており、「新しい暗号化属性」だけでなく、既存のconfig.filter_parametersのエントリに対してもモデル名でプレフィックスを付けたバリエーションが次々と追加されてしまう点。
結果として:
config.filter_parametersに 30 個- Active Record Encryption を使うモデルが 30 個ある
といったケースで、
- 元々 30 個だったフィルタパラメータが、モデルごとにプレフィックス付きの組み合わせをどんどん増殖させ、1,000件超のエントリになる。
- これらが 1 つの巨大な正規表現にコンパイルされ、パラメータフィルタリング(ログ用フィルターや EventReporter 用のフィルター)が非常に遅くなる。
- PR 説明では「軽いはずのコントローラで処理時間の 95% をフィルタリングが消費」というレベル。
このPRで行った修正の方向性
コード行数の差分から分かる範囲でのポイント:
activerecord/lib/active_record/core.rbでfilter_attributes/filtered_attributesの更新ロジックを変更。- 目的は「暗号化属性を追加するときに、
Rails.application.config.filter_parameters由来のエントリを無闇にモデル名でプレフィックスして増やさない」ようにすること。 - おそらく次のようなアプローチのいずれか(または組み合わせ)で対処している:
filter_attributesにアプリ共通のグローバルなフィルタと、モデル固有の追加分を区別して管理する。- 追加時に「すでに存在するものを再度ネームスペース付きで複製しない」ようにチェックする。
- あるいは
+=(配列結合)ベースの実装をやめ、重複や組み合わせ爆発を抑制できるデータ構造・APIに変更する。
テストの変更
railties/test/application/active_record_railtie_test.rbにテストを追加・修正。- ねらいとしては:
Rails.application.config.filter_parametersと Active Record Encryption の組み合わせで、意図しない大量のエントリがfilter_attributesに入らないことを検証。- 暗号化属性が正しくフィルタ対象にはなるが、グローバル設定由来のフィルタパラメータがモデルごとに複製されないことを確認。
サンプルイメージ(擬似コード)
# 以前の挙動(問題のあるイメージ)
Rails.application.config.filter_parameters = [:password, :token]
class User < ApplicationRecord
encrypts :email
end
# 暗号化属性を追加する際に
self.filtered_attributes += [:"user.email"]
# しかし実際には、内部的処理の都合で
# [:password, :token] も "user." プレフィックス付きで再度入ってしまうような挙動になり、
# [:password, :token, :"user.password", :"user.token", :"user.email", ...] などが
# モデル数ぶん増殖するこのPRでは、こうした「元の filter_parameters エントリの再プレフィックス&多重追加」を避けるように修正していると考えられます。
- 影響範囲・注意点
影響範囲
- 対象:
- Rails 8.1 以降で、
Rails.application.config.filter_parametersをある程度設定しており、- Active Record Encryption を使っているプロジェクト。
- ログフィルタや
ActiveSupport::ParameterFilterを介したパラメータフィルタリングに関わる処理が高速化される可能性が高いです。- PR 作成者の環境では、コントローラ処理時間のほとんどを占めていたフィルタ処理が改善される。
注意点
- 仕様として期待されている挙動(
config.filter_parametersに設定したキーがフィルタされる、- 暗号化属性がフィルタされる )は維持されたまま、重複・爆発的な増殖だけが抑制される変更です。
- もし独自に
filter_attributes/filtered_attributesに直接アクセスして実装しているコードがあれば、要確認:- これまで「やたら多かったエントリ数」が、正常な量に減る可能性がある。
- 特定のプレフィックス付きキーを前提としたハック的な実装をしている場合、挙動が変わる可能性はゼロではありません(通常のアプリでは問題にならない想定)。
パフォーマンス検証のおすすめ
- Rails 8.1(または edge)でアップグレード後に以下に心当たりがある場合は、このPRが含まれたバージョンで再測定するとよいです:
- 単純なコントローラでもレスポンスがやたら遅い。
- プロファイラで見ると、ログフィルタリングや EventReporter 用のフィルタリングがボトルネックになっている。
Rails.application.config.filter_parametersに大量のエントリを入れているプロジェクトでは特に効果があります。
- 参考情報 (あれば)
- 本PR:
Avoid combinatory explosion of Rails.app.config.filter_parameters(#56759) - 関連PR: ActiveRecord::Base.filter_attributes まわりの仕様導入 PR
- 関連ドキュメント:
- Rails ガイド: セキュリティ > ログから機密情報をフィルタする (
config.filter_parameters) - Rails ガイド: Active Record Encryption(暗号化属性のフィルタ挙動・ログ出力に関する記述)
- Rails ガイド: セキュリティ > ログから機密情報をフィルタする (
#56732 Add extension point to customize transaction for persistence methods
マージ日: 2026/2/9 | 作成者: @ipvalverde
- 概要 (1–2文で)
Rails の Active Record において、save/destroyなどの永続化メソッドが内部で張る「暗黙のトランザクション」を、モデル単位でカスタマイズできる拡張ポイントが追加されました。これにより、トランザクション分離レベルやロールバック処理などを、with_transaction_returning_statusをモンキーパッチせずに安全に制御できます。
- 変更内容の詳細
追加された拡張ポイント
ActiveRecord::Transactions 内で、永続化メソッドが使うトランザクション開始処理が、プライベートメソッドとして抽出されました(名前は説明からすると implicit_persistence_transaction)。
モデル側でこれをオーバーライドすることで、トランザクションの張り方を細かく制御できます。
PR の説明にあるように、典型的なオーバーライド例は次のような形です:
class Account < ApplicationRecord
private
def implicit_persistence_transaction(connection, &block)
if connection.transaction_open?
# 既にトランザクション内であれば、新たにトランザクションを張らない
yield
else
# 親クラス(Active Record)のデフォルト実装を呼び出す
super
end
end
endポイント:
引数:
connection: モデルが使っている DB コネクション (ActiveRecord::ConnectionAdapters::AbstractAdapter派生)&block: 実際の保存/削除処理 + コールバックなどを含む本体
デフォルト実装(
super)は、従来with_transaction_returning_statusの中で直接書かれていた「保存処理をトランザクションでラップするロジック」で、
それがimplicit_persistence_transactionというメソッドに移されたイメージです。
なぜこの変更が必要か
以前から、トランザクション分離レベルを指定したい場合には:
毎回明示的にトランザクションブロックでラップする:
rubyAccount.transaction(isolation: :read_committed) do account.save! endもしくは
with_transaction_returning_statusをモンキーパッチして、そこに独自のトランザクション開始ロジックをねじ込む
という方法しかありませんでした。
しかし後者は以下の問題があります:
- Rails 本体の実装が変わると簡単に壊れる(アップグレードに弱い)
- ロールバックや
throwを含む細かい挙動に追従し続ける必要がある - 他の gem との互換性で問題が出やすい
Rails には既に ActiveRecord.with_transaction_isolation_level という「ブロック範囲で isolation level を変える」API はありますが、これは「モデルの暗黙トランザクション設定」ではなく、「任意の処理を isolation level 付きで実行するため」のグローバルな仕組みです。
今回の PR では、「暗黙トランザクションをどう張るか」をメソッドに切り出し、モデルごとに安全にオーバーライドできるようにした、というのが本質的な変更点です。
どう使えるかの具体例
例1: あるモデルだけ常に特定の isolation level で保存する
class Account < ApplicationRecord
private
def implicit_persistence_transaction(connection, &block)
if connection.transaction_open?
# 外側で isolation level が決まっている前提なのでそのまま
yield
else
# このモデルの暗黙トランザクションは常に :read_committed
connection.transaction(isolation: :read_committed) do
yield
end
end
end
end例2: 既にトランザクションが開いている場合には、新しいトランザクションを張らない
(PR の例)
class Account < ApplicationRecord
private
def implicit_persistence_transaction(connection, &block)
if connection.transaction_open?
yield
else
super # デフォルトのトランザクション処理
end
end
endこれにより、外側で大きなトランザクションを張っているときは、その中の save / destroy などが余分なトランザクションをネストしないようにできます。
例3: ActiveRecord::Rollback の扱いをカスタマイズする
implicit_persistence_transaction の中で begin ... rescue ActiveRecord::Rollback を自前でハンドリングすることで、ロールバック発生時にログを出したり、特定フラグを立てたりといったカスタマイズも可能です。
(PR 説明でも「AR::Rollback や throw を含むコールバック処理に対するカスタム処理」が例示されています)
- 影響範囲・注意点
影響範囲:
- Active Record のトランザクション処理 (
with_transaction_returning_statusまわり) に、拡張ポイントとなるプライベートメソッドが一段挟まる形で実装変更されています。 - デフォルト実装は従来と等価になるように作られているため、何もオーバーライドしなければ既存アプリの挙動は変わりません。
activerecord/test/cases/transactions_test.rbに追加されたテストから、この拡張ポイントの挙動(オーバーライドした場合の動作)がカバーされています。
- Active Record のトランザクション処理 (
注意点:
- このメソッドは private であり、内部実装の一部でもあるため、「将来的なシグネチャ変更の可能性」には留意が必要です。ただし、あえて PR で明示的に抽出・命名されているので、Rails チームとしてもある程度は安定 API 的に扱う意図がうかがえます。
- このメソッドをオーバーライドして
superを呼ばない場合、デフォルトのトランザクション開始ロジック(ネスト時の join / save 成功・失敗時の戻り値処理など)を自分で面倒見る必要があります。挙動差が不具合につながりやすいので、必要箇所だけに絞って慎重に変更するべきです。 - モデルごとにトランザクションポリシーがバラバラになると、アプリ全体の整合性やデバッグが難しくなり得ます。
「どのモデルにどのポリシーが適用されているか」をドキュメント化するのがおすすめです。
- 参考情報 (あれば)
対応する PR:
- 本 PR:
Add extension point to customize transaction for persistence methods(#56732) - 代替案として検討されていた PR: #56673
- そちらは「トランザクション作成の一部のパラメータをいじる」程度の柔軟性だったのに対し、本 PR は
- トランザクションをスキップする
ActiveRecord::Rollback/throwを独自処理する など、より自由度の高いカスタマイズを可能にする設計になっています。
- そちらは「トランザクション作成の一部のパラメータをいじる」程度の柔軟性だったのに対し、本 PR は
- 本 PR:
関連 API:
ActiveRecord::Base.transaction(isolation: ...)ActiveRecord.with_transaction_isolation_levelActiveRecord::Transactions#with_transaction_returning_status
#56739 Fix Logger documentation link in Active Record README
マージ日: 2026/2/9 | 作成者: @toilaloc
- 概要 (1–2文で)
Active Record の README に記載されていた Ruby の Logger クラスへのリンクが 404 になっていたため、正しいドキュメント URL に差し替えた PR です。コード本体の挙動には一切影響せず、ドキュメントの品質向上のみを目的としています。
- 変更内容の詳細
- 対象ファイル:
activerecord/README.rdoc - 変更点はリンク URL のみで、1 行の置き換えです。
変更前 (壊れているリンク)
https://docs.ruby-lang.org/en/master/Logger.html変更後 (有効なリンク)
https://ruby.github.io/logger/もともと README では、「Active Record のログ出力には Ruby の Logger クラスを使う」といった文脈で公式ドキュメントへのリンクを貼っていましたが、その指し先が Ruby 公式サイトの /en/master/ パスになっており、現在は 404 になっていました。
この PR では、Ruby 側のドキュメント構成変更に合わせて、/en/master/ ではなく、logger ライブラリ専用のページ(https://ruby.github.io/logger/)に更新しています。これにより、開発者が README から直接 Logger のクラス・メソッド仕様を参照できるようになります。
- 影響範囲・注意点
- 影響範囲
- Rails 本体の挙動、Active Record の API、ログ出力の仕組みなどには一切変更はありません。
- 影響するのは、Active Record の README から Logger の仕様を確認するときに辿るリンクのみです。
- 注意点
- README に記載された説明内容自体は変わっていないため、コード例や使い方に変更はありません。
- Ruby のバージョンごとに Logger の細かい仕様が異なる場合がありますが、このリンク先は Ruby 標準添付ライブラリとしての Logger 一般のドキュメントです。特定バージョンにロックされているわけではない点に注意してください(以前の
/en/master/のような「master ブランチ最新」ではなく、GitHub Pages 側の公開ドキュメントに紐づきます)。
- 参考情報 (あれば)
- 対象 README:
- 新しい Logger ドキュメント:
Loggerクラスを利用する典型例(Rails 環境外の Ruby スクリプトでの例):rubyRails/Active Record ではrequire 'logger' logger = Logger.new($stdout) logger.level = Logger::INFO logger.info "This is an info message" logger.error "This is an error message"ActiveRecord::Base.loggerなどを通じて、この Ruby 標準のLoggerを利用しています。
#56753 Upgrade to Dalli 5
マージ日: 2026/2/8 | 作成者: @byroot
- 概要 (1-2文で)
このPRは、Railsが使用する Memcached クライアント gem「Dalli」をバージョン5系にアップグレードし、それに伴ってMemCacheStore向けのテストを Dalli 5 の振る舞いに合わせて修正したものです。併せて、直前の変更(#56721)や関連Issue(#56704)で発生していた不具合を解消しています。
- 変更内容の詳細
2-1. Dalli のバージョンアップ
Gemfilediff- gem "dalli", "~> 3.2" + gem "dalli", "~> 5.0"のように、Rails がテスト・開発用に利用する Dalli のメジャーバージョンを 5 系に変更しています(正確なバージョン指定は PR 本文に依存しますが、趣旨としては 5 系へのアップデート)。
Gemfile.lock- Dalli のバージョン表記と、その依存関係解決結果が更新されています。
- これにより、CI・開発環境で bundle install した際に Dalli 5 が入るようになります。
2-2. MemCacheStore のテスト修正
変更は activesupport/test/cache/stores/mem_cache_store_test.rb に集中しており、内容としては:
- Dalli 5 での API 仕様や動作変更を反映する形で、期待値・モック・アサーションなどが修正されている
- 具体的には:
- Dalli クライアントの戻り値・例外・メソッド呼び出し回数/引数などに関するテストを、Dalli 5 の挙動に合わせて調整
- 非推奨となったオプションやメソッドを用いた古いテストケースを削除・置き換え
- MemCacheStore が内部で利用する Dalli クライアントのインターフェース変更に伴い、テストのスタブ化・モック化の方法を更新
サンプル的にイメージすると、以下のような修正が入っている可能性があります(擬似コード):
# 旧: Dalli 3/4 系向けの想定
client = Dalli::Client.new("localhost:11211", namespace: "foo")
assert client.set("key", "value")
# 新: Dalli 5 に合わせてオプションや戻り値の扱いを調整
client = Dalli::Client.new("localhost:11211", **dalli_options)
assert_equal true, client.set("key", "value")実際の差分では、これらが Rails の ActiveSupport::Cache::MemCacheStore 経由で呼び出される前提でテストされており、Dalli 5 の動作でテストが落ちないように細部が調整されています。
- 影響範囲・注意点
3-1. Rails 自体の挙動
- この PR は主に「依存ライブラリ(Dalli)のバージョンアップ」と「それに合わせたテスト修正」であり、Rails の公開 API (
ActiveSupport::Cache::MemCacheStoreの外部インターフェース) が大きく変わるものではありません。 - ただし、Memcached 用のキャッシュストアが内部で利用する Dalli の挙動が変わるため、Dalli 特有の挙動に依存している場合には影響が出る可能性があります。
3-2. アプリケーション側への影響・注意点
Rails アプリ側で以下のような場合は、挙動確認・修正が必要になる可能性があります:
dalliを直接利用している (Rails の cache store 経由ではなく、アプリコードでDalli::Clientを明示的に生成している)- Dalli 5 での非互換変更(オプションの削除・デフォルト値変更・メソッドの挙動変更など)に注意が必要です。
config.cache_store = :mem_cache_store, ...で、Dalli 依存のオプションを細かく調整している- 例:
expires_in,compress,pool_sizeなどに加え、Dalli 固有のオプションを渡している場合は、Dalli 5 で有効かどうかを確認してください。
- 例:
- 古い Dalli バージョンでのみ存在したバグやワークアラウンドに依存している
- Dalli 5 で修正された挙動により、「一見バグのような」変化が起きる可能性があります。
テスト・本番環境で Memcached を使っているプロジェクトは、Rails をこのコミット以降にアップデートした際、キャッシュ周りの E2E テストやパフォーマンス、タイムアウト・接続エラー時の挙動を確認しておくと安全です。
- 参考情報 (あれば)
この PR が修正しているもの:
- https://github.com/rails/rails/pull/56721
- https://github.com/rails/rails/issues/56704
いずれも Dalli バージョン/MemCacheStore との互換性やテスト失敗などに関連していると考えられます。
Dalli 5 の変更内容:
- 正確な非互換点や新機能は Dalli 本体のリリースノート・README を参照してください。
例: GitHubpetergoldstein/dalliのCHANGELOGやREADMEなど。
- 正確な非互換点や新機能は Dalli 本体のリリースノート・README を参照してください。
要するに、この PR は「Rails の Memcached サポートを Dalli 5 時代に対応させるための最低限のアップデートとテスト調整」であり、MemCacheStore ユーザーは今後 Dalli 5 系を前提とした動作になる点を認識しておく必要があります。
#56751 Pin dalli gem to version 4.x
マージ日: 2026/2/8 | 作成者: @yahonda
- 概要 (1-2文で)
Rails の依存関係として利用している memcached クライアント gem「dalli」を、5.x 系ではなく 4.x 系に固定(pin)する PR です。dalli 5.0.0 でバイナリプロトコルが削除されたことにより Rails のテストでDalli::Protocol::Binaryが見つからず落ちる問題を、一時的にバージョンを固定することで回避しています。
- 変更内容の詳細
何をしたか
Gemfileの dalli の指定を「4 系に固定」するように変更- それに伴い
Gemfile.lockも 4.x を指すように更新
(PR 本文には直接のコード抜粋はありませんが、イメージすると以下のような変更です)
# 変更前(例)
gem "dalli"
# 変更後(例)
gem "dalli", "~> 4.0"これにより bundle update dalli を実行しても 5.0.0 以降ではなく、4.x 系の最新バージョンまでに制限されます。
なぜ必要か
dalli 5.0.0 から「バイナリプロトコル」が削除され、内部クラス Dalli::Protocol::Binary が存在しなくなりました。一方、Rails の MemCacheStore 関連テストでは、テスト用の疑似サーバーとしてこのクラスを継承して利用しています:
class UnavailableDalliServer < Dalli::Protocol::Binary
endそのため、dalli 5.0.0 に上げてテストを走らせると、以下のような例外が発生します。
uninitialized constant Dalli::Protocol::Binary (NameError)
class UnavailableDalliServer < Dalli::Protocol::Binary
^^^^^^^^この問題の本質的な対応(Rails 側のコードを dalli 5 系に追従するか等)は別 PR(https://github.com/rails/rails/pull/56721)で議論・対応中であり、それが決まるまで CI / 開発環境でのテストが落ちないよう、一時的に dalli を 4.x にピン留めした、という位置づけです。
- 影響範囲・注意点
- 影響範囲:
- Rails 本体の依存としてインストールされる dalli が 4.x に固定されます。
- Rails リポジトリ内で
bundle install/bundle updateした際に dalli 5.x へ自動更新されなくなり、テストスイートは引き続き dalli 4.x の API・挙動を前提に動作します。
- 注意点:
- dalli 5.x の新機能や変更点(メタプロトコルのみのサポートなど)を使った検証は、この PR 適用中の Rails ではできません。ライブラリとしての Rails が dalli 5 に公式対応したとはまだ言えない状態です。
- アプリケーション側で独自に
gem "dalli", ">= 5"等を指定している場合、Rails 本体の Gemfile とは別なので、アプリの Gemfile/Gemfile.lock にはこの PR の影響は直接は及びません。ただし、Rails のキャッシュストア実装やテストコードを参考にする場合、そこが dalli 4.x 前提である点に注意する必要があります。 - このピン留めはあくまで「一時的なワークアラウンド」と明言されており、56721 のような本対応 PR がマージされれば、将来的に再び dalli 5.x に対応する変更が入る可能性が高いです。
- 参考情報 (あれば)
- 該当 PR(dalli 5 対応の本質的な修正議論)
- dalli 5.0.0 の CHANGELOG
- https://github.com/petergoldstein/dalli/blob/main/CHANGELOG.md#500
- 「Removed binary protocol - The meta protocol is now the only supported protocol」と明記されており、
Dalli::Protocol::Binary削除の背景になっています。
- バイナリプロトコル削除の実装コミット
#56730 Mark ActiveRecord::Relation#values as nodoc
マージ日: 2026/2/6 | 作成者: @p8
- 概要 (1-2文で)
ActiveRecord::Relation#valuesメソッドを Rails の公式ドキュメントから非公開(nodoc)扱いとする変更です。これにより、このメソッドは内部実装用として位置づけられ、アプリケーションコードからの利用は推奨されないことが明確になりました。
- 変更内容の詳細
- 対象:
activerecord/lib/active_record/relation.rbのActiveRecord::Relation#values - 変更点:
valuesメソッドのドキュメントコメントに:nodoc:指定を追加/変更し、「公開APIではない」と明示しました。(実装ロジック自体の変更はなし)
values は、Relation オブジェクトに対して設定されている各種クエリ句(where, order, joins, limit など)を内部的に保持するための Hash を返すメソッドです。PR本文では、values_for_queries と同様に「内部メソッド」とみなすべきだと説明されています。
イメージ的には、以下のような使い方が「できてしまう」メソッドですが、今回の変更により「やってはいけない領域」であることがより明確になります:
relation = User.where(active: true).order(created_at: :desc)
internal_hash = relation.values
# => 本来は Rails 内部で使うことを想定した Hash 構造この PR はあくまでドキュメント上の扱い(可視性)の変更であり、挙動や返り値の仕様を変更するものではありません。
- 影響範囲・注意点
ライブラリ利用者 / アプリケーション開発者への影響
- 挙動は変わらないため、すぐにテストが壊れるような実害はありません。
- ただし、
Relation#valuesを直接利用しているコードは、将来的な非互換変更(仕様変更・削除)の影響を強く受けやすい「内部API依存コード」であると再確認すべき状態です。 - 今後の Rails バージョンで
valuesの内部仕様変更/削除が行われても、変更は「公開APIの破壊的変更」とはみなされにくくなります。
既に
valuesを使っている場合の代替検討例- クエリ構築状態を知るために:
- できるだけ
where_values_hashなど、公開されているメソッドで代替できないか検討する - あるいは Relation 自体をそのまま組み立て・合成する設計に見直し、内部状態を直接覗かないようにする
- できるだけ
- 「values の Hash 構造に依存してロジックを書いている」場合、将来の Rails アップグレード時に壊れるリスクが高いため、早めのリファクタを検討した方が安全です。
- クエリ構築状態を知るために:
gem / ライブラリ作者への注意点
- gem 内部で
ActiveRecord::Relation#valuesに依存している場合、その gem は Rails の内部実装に強く結びついていることになります。 - サポート対象の Rails バージョン範囲を狭める、あるいは内部実装依存を避ける方向で API 設計を見直すことが推奨されます。
- gem 内部で
- 参考情報 (あれば)
この PR が示すポイント:
ActiveRecord::Relationは複雑な内部状態を持つクラスであり、その一部を表すメソッド(values,values_for_queriesなど)は「見えていても触らない」前提の内部APIであることが再確認された形です。- Rails 本体が nodoc 指定を進めている対象は、今後仕様が変わる・削除される可能性が比較的高いと考えておくとよいです。
類似の内部メソッド例:
ActiveRecord::Relation#values_for_queries- 各種
_arel系メソッドなど、ドキュメントに出てこないメソッド群は基本的に内部APIと考えるのが安全です。
#56703 Fix ThroughReflection#association_primary_key with composite keys
マージ日: 2026/2/6 | 作成者: @felix-d
- 概要 (1-2文で)
has_many :throughなどで参照されるThroughReflection#association_primary_keyが複合主キーを正しく扱えていなかった問題を修正するPRです。primary_key: [:col1, :col2]のような定義でも_idsリーダーメソッドが正しく動作するようになります。
- 変更内容の詳細
何を直したか
対象メソッドは ActiveRecord::Reflection::ThroughReflection#association_primary_key です。
これまでの実装は、primary_key が配列である場合を考慮せず、単純に primary_key.to_s を呼んでいました。そのため:
# 例: ソース側関連
has_many :memberships,
primary_key: [:tenant_id, :user_id]のようなケースでは、primary_key が [:tenant_id, :user_id](配列)になり、to_s によって "[:tenant_id, :user_id]" という1つの文字列に変換されてしまっていました。
このPRでは:
primary_keyが配列の場合: 各要素をto_sして配列にした["tenant_id", "user_id"]を返すprimary_keyが単一の場合: 従来どおりprimary_key.to_sを返す
という分岐ロジックに変更されています。
実装は BelongsToReflection#association_primary_key と同等の挙動になるように揃えられています。
何が問題だったか (エラー内容)
has_many :through + composite primary key な関連で自動生成される _ids リーダーが壊れており、次のようなエラーが発生していました。
ActiveRecord::UnknownAttributeReference:
Dangerous query method called with non-attribute argument(s): "[:col1, :col2]"これは、内部的に "[:col1, :col2]" という“配列を文字列化したもの”がカラム名として扱われてしまい、ActiveRecord が「危険な非属性引数」とみなして例外を投げていたためです。
PRに含まれるテストでは、以下のような点がカバーされています:
ThroughReflection#association_primary_keyが配列・単一キー両方で期待どおりに文字列または配列の文字列に変換されることhas_many :throughで composite primary key を持つモデルに対して、xxx_idsリーダーが正常に動くこと- sharding 関連のモデル (
sharded/blog,sharded/blog_post) でもこの挙動が問題なく機能すること
テストモデルへの追加定義(例: primary_key: [:shard_id, :id] のような形)が行われ、実運用に近い形での回帰テストが増えています。
- 影響範囲・注意点
- 影響を受けるのは:
has_many :through/has_one :throughなど ThroughReflection を使う関連- かつ、ソース側関連に
primary_key: [:col1, :col2]のように複合主キーを指定しているケース
- 主な症状の変化:
- 以前:
_idsリーダメソッド(例:project.user_ids)呼び出しでActiveRecord::UnknownAttributeReferenceが発生 - 以後: 正常に複合キーを扱うクエリが発行される
- 以前:
- 既存コードへの後方互換性:
- 単一主キー (
primary_key: :idなど) の場合の戻り値は従来同様、"id"という文字列であり、挙動は従来と変わりません。 - 配列を返すようになったのは、もともと壊れていたパス(Composite PK)なので、実質バグ修正であり互換性問題は極小です。
- 単一主キー (
- カスタムで
ThroughReflection#association_primary_keyの結果を直接参照しているコードがもしあれば、「必ず文字列」という前提を置いていた場合に、複合キー時だけ「文字列配列」が返るようになる点には注意が必要です。
- 参考情報 (あれば)
- この修正は
BelongsToReflection#association_primary_keyとの挙動差異を埋める形で行われており、Rails内部のリフレクションAPIの一貫性が向上しています。 - エラーメッセージにある
Dangerous query method called with non-attribute argument(s)は、Rails 7以降で導入された「非カラム値をwhere等に渡したときに警告する」仕組みからの例外であり、今回の修正はその保護機構との整合性もとっています。
#56742 Read from ENV['REVISION'] if present
マージ日: 2026/2/6 | 作成者: @cocoahero
- 概要 (1-2文で)
Rails のアプリケーションリビジョン取得ロジック (Rails.application.revision) が拡張され、ENV["REVISION"]が設定されていればそれを最優先で利用するようになりました。これにより、多くのデプロイプロバイダが採用している「環境変数でリビジョンを渡す」方式を Rails が素直にサポートします。
- 変更内容の詳細 (サンプルコード含む)
これまでの挙動
Rails.application.revision は、概ね次の優先順位でリビジョン(コミットハッシュ等)を決定していました。
REVISIONファイルが存在すれば、その内容- なければ
git rev-parseコマンドで現在のコミットハッシュを取得 - それでもダメなら
nil(もしくは空)になるケース
変更後の挙動
今回の PR により、優先順位の一番上に ENV["REVISION"] が追加されました。
新しい優先順位は以下の通りです。
ENV["REVISION"]が設定されていれば、その値を返す- それが無ければ
REVISIONファイルの内容を利用 - それも無ければ
git rev-parseで取得を試みる
※正確なメソッド名は Rails.application.revision(Rails.app.revision のように呼ぶこともある)が、この内部実装が今回修正された部分です。
イメージとなるコード例
アプリケーションコード側からは、従来通り次のように使えます。
Rails.application.revision
# 例: "3d2f1c7"デプロイ設定側でリビジョンを環境変数に渡す例(Capistranoや各種PaaSを想定):
# デプロイスクリプトなどで
export REVISION=$(git rev-parse HEAD)
RAILS_ENV=production bundle exec rails serverRails アプリ内では、ENV["REVISION"] が優先されるため、Git リポジトリや REVISION ファイルがなくても、上記の値がそのまま Rails.application.revision に反映されます。
テストとドキュメントの更新
railties/test/application/app_revision_test.rbENV["REVISION"]が設定されている時にそれが優先されることを確認するテストが追加されています。REVISIONファイルしかない場合や Git だけがある場合等、既存挙動との組み合わせもテストされていると考えられます。
guides/source/configuring.md- 設定ガイド内の「アプリケーションのリビジョン」関連の記述が 1 行変更され、
ENV["REVISION"]による指定方法が反映されています。
- 設定ガイド内の「アプリケーションのリビジョン」関連の記述が 1 行変更され、
railties/CHANGELOG.mdRails.application.revisionがENV["REVISION"]を読むようになったことが、変更履歴として追記されています。
- 影響範囲・注意点
影響範囲
Rails.application.revisionを利用している全てのコード(例: エラートラッキング、APM、フロントエンドへのバージョン情報埋め込み、ヘルスチェックエンドポイントなど)- 特に以下のような環境で恩恵があります:
- Git の履歴をコンテナイメージに含めていない(
gitコマンドもない)環境 REVISIONファイルを生成せず、デプロイ時に環境変数だけでリビジョンを渡している環境- Heroku や Render、その他 CI/CD で
REVISIONやCOMMIT_SHAを ENV で渡すパターン
- Git の履歴をコンテナイメージに含めていない(
互換性・注意点
- 後方互換性
ENV["REVISION"]が設定されていない場合の挙動は従来通り(REVISIONファイル →git rev-parse)のため、既存環境は基本的に影響を受けません。
- 優先順位の変化
- これまで
REVISIONファイルを主として使っていた環境で、同時にENV["REVISION"]も設定している場合は、ENV の値が優先されるようになりました。 - CI やデプロイスクリプトで
REVISIONを無自覚に設定していると、意図せぬ値になる可能性があるため、値の整合性を確認した方がよいです。
- これまで
- 値の形式は自由
- ENV に何を入れるかは規定されていないため、実運用では以下のようなポリシーをチーム内で決めておくとよいです。
- Git のフル SHA / 短縮 SHA
- バージョンタグ (e.g.
v1.2.3) - デプロイID等(APM 連携などで使う)
- ENV に何を入れるかは規定されていないため、実運用では以下のようなポリシーをチーム内で決めておくとよいです。
- 参考情報 (あれば)
- 実務的には、多くの PaaS / CI/CD プラットフォームで以下のような環境変数が用意されており、これらを
REVISIONに流用するケースが多いです:- GitHub Actions:
GITHUB_SHA - GitLab CI:
CI_COMMIT_SHA - CircleCI:
CIRCLE_SHA1
- GitHub Actions:
- それらをマッピングする例:
# GitHub Actions の例
export REVISION="$GITHUB_SHA"
bundle exec rails db:migrate
bundle exec puma -C config/puma.rbこの PR により、こうしたパターンが Rails 標準の挙動により自然にサポートされるようになりました。
#56745 Bump Ruby version to 4.0.1 in devcontainer
マージ日: 2026/2/6 | 作成者: @yahonda
- 概要 (1–2文で)
Rails の開発用コンテナ(devcontainer)で使用する Ruby のバージョンが 4.0.0 などの旧バージョンから 4.0.1 に更新されました。ローカルで devcontainer を使って Rails を開発する際の実行環境が、公式 devcontainer リポジトリに追加された Ruby 4.0.1 に追従する形で整備された変更です。
- 変更内容の詳細
- 対象ファイル:
.devcontainer/Dockerfile - 変更内容: Ruby のバージョン指定を 1 行だけ差し替え
イメージとしては、Dockerfile 内の Ruby バージョン指定が以下のように変わったと考えられます(※PR本文からの推測・典型例):
- ARG RUBY_VERSION=4.0.0
+ ARG RUBY_VERSION=4.0.1あるいは、ベースイメージを直接指定している場合は:
- FROM ghcr.io/rails/devcontainer/ruby:4.0.0
+ FROM ghcr.io/rails/devcontainer/ruby:4.0.1PR の説明にある通り、devcontainer 内で ruby -v を実行すると次のように報告されるようになります:
$ ruby -v
ruby 4.0.1 (2026-01-13 revision e04267a14b) +PRISM [aarch64-linux]つまり、この PR は「VS Code Remote Container / Dev Containers」等で Rails を開発する際に立ち上がるコンテナの Ruby を 4.0.1 に揃えるためのバージョンアップです。
- 影響範囲・注意点
影響範囲
.devcontainerを利用している開発者:- 新しく devcontainer をビルドまたは再ビルドすると Ruby 4.0.1 が使われます。
- 以前の devcontainer イメージをキャッシュしている場合も、再ビルドすると自動的に 4.0.1 に更新されます。
- CI や本番環境:
- この PR は
.devcontainer/Dockerfileのみを変更しており、アプリケーション本体のコードや本番環境向け Dockerfile・Gemfile には触れていません。そのため、devcontainer を使っていない CI / 本番環境には直接の影響はありません。
- この PR は
注意点
- Ruby 4.0.1 固有の挙動差:
- 4.0.0 から 4.0.1 へのマイナーバグフィックスや内部実装の変更により、極めて稀にテストが落ちたり警告の出方が変わる可能性があります。
- devcontainer 上でのみテスト結果が変わった場合、Ruby バージョン差異を疑う必要があります。
- チーム開発時のバージョン差:
- ローカルで devcontainer を使わない開発者が Ruby 4.0.0 など別バージョンを使っていると、環境差による微妙な挙動違いが起きる可能性があります。
.ruby-versionや CI の Ruby バージョンも 4.0.1 に合わせることで、より一貫した開発環境を保てます。
- Ruby 4.0.1 固有の挙動差:
- 参考情報 (あれば)
PR が参照している関連 PR:
- Ruby 4.0.1 対応が追加された devcontainer リポジトリの PR:
https://github.com/rails/devcontainer/pull/111
→ Rails 公式 devcontainer イメージ側で Ruby 4.0.1 が利用可能になったことを受けて、本家 Rails リポジトリ側の.devcontainer/Dockerfileも追従した形です。
- Ruby 4.0.1 対応が追加された devcontainer リポジトリの PR:
実開発での確認ポイント:
- devcontainer を再ビルドして
ruby -vが4.0.1になっているか - テストスイート・Rubocop などが問題なく動くか
- チーム内の他の開発環境(ローカル Ruby / CI)と Ruby バージョンを揃えるかどうかの運用方針
- devcontainer を再ビルドして
#56741 Update Bundler to 4.0.6
マージ日: 2026/2/5 | 作成者: @yahonda
- 概要 (1-2文で)
Bundler のバージョンを 4.0.4 から 4.0.6 に更新し、bundle update <gem>が正常に動作せず「Could not find compatible versions」で失敗していた問題を解消する PR です。Rails 本体のコードではなく、Gemfile.lock の Bundler バージョン指定のみを更新しています。
- 変更内容の詳細
変更ファイル
Gemfile.lockの Bundler バージョンが4.0.4→4.0.6に 1 行だけ更新されています。
背景となる問題
Bundler 4.0.4 環境で、特定の gem(ここではdalli)をbundle update dalliしようとすると、依存関係の解決時に失敗していました。エラーメッセージの要点:
releasergem がrake ~> 13.0に依存- しかし
tools/releaserというローカルソースにrake ~> 13.0が見つからない - そのため
releaserが使えず、結果として依存解決全体が失敗し「Could not find compatible versions」となる
つまり、「特定の gem だけアップデートしたい」という一般的なユースケースで、Bundler の依存解決ロジックに起因する不具合に当たっていました。
Bundler 4.0.6 での挙動
Bundler を 4.0.6 に上げた後は、同じ環境・同じコマンドで以下のように正常に動作しています。bash# Bundler のアップデート gem update --system bundle update --bundler # => Updating bundler to 4.0.6. # 個別 gem の更新 bundle update dalli # ... # Using dalli 4.3.0 (was 4.0.0) # Bundle updated!つまり、Bundler 4.0.6 には依存解決ロジックの修正(
https://github.com/ruby/rubygems/pull/9301)が入っており、それを Rails リポジトリでも取り込んだ、という位置づけです。方針的な補足
PR 作成者は「Bundler が出るたびに Rails でバージョンを追従するつもりはないが、今回はbundle updateが壊れていたため例外的に上げた」と明言しています。
つまり「不具合回避のための最小限のバージョンアップ」という性格の変更です。
- 影響範囲・注意点
影響範囲
- Rails リポジトリで
bundle update <gem>を実行した際の依存解決挙動が安定します。 - Bundler 4.0.4 で発生していた、「ローカルソース経由の依存などが絡むと
Could not find compatible versionsで失敗する」ケースを回避できます。 - Rails 本体の Ruby コードや動作仕様には一切変更がありません。
- Rails リポジトリで
開発者視点での注意点
- Rails の開発に参加している場合、この PR マージ後の
Gemfile.lockを使う前提であれば、手元の Bundler も 4.0.6 以上にしておくのが安全です。- 例:
gem update --systemまたはgem install bundler -v 4.0.6
- 例:
- Bundler 自体の仕様変更による副作用がないかを気にする場合は、Bundler のリリースノート・上記 PR を確認する価値がありますが、本 PR はそのうち「既知の不具合修正」を含むバージョンへの更新に留まっています。
- Rails の開発に参加している場合、この PR マージ後の
- 参考情報 (あれば)
この PR で取り込み対象となっている Bundler 側の修正:
この問題が発見されるきっかけとなった Rails issue:
当該 PR:
- Rails PR #56741 “Update Bundler to 4.0.6”
#56729 Better handle cache deserialization errors
マージ日: 2026/2/5 | 作成者: @byroot
- 概要 (1-2文で)
Rails のキャッシュ復元(デシリアライズ)時に例外が発生した場合、それを「キャッシュミス」として扱うように振る舞いを明確化・強化した PR です。memcached などが切り詰められた(truncated)レスポンスを返すような稀なケースでも、アプリ本体が例外で落ちにくくなるようにしています。
- 変更内容の詳細
※実際の差分を概念的に整理したものであり、行レベルの完全一致はしませんが、挙動の意図は正確になるように記述しています。
2-1. ActiveSupport::Cache::Coder の改善
activesupport/lib/active_support/cache/coder.rb
Cache::Store は「値を保存するとき」「値を取り出すとき」に、Coder を使ってシリアライズ/デシリアライズしています。この PR では以下の方針が明示的になりました。
- デシリアライズに失敗したら、そのキーは「キャッシュミス」と見なす
- つまり、例外をアプリ層まで投げず、「キャッシュにヒットしなかった」ものとして扱う。
典型的には次のような形の処理になっています(イメージコード):
def load(payload)
return unless payload
begin
deserialize(payload) # JSON/YAML/Marshal など
rescue StandardError => error
# ここで例外をキャッチしてキャッシュミス扱いにする
ActiveSupport::Notifications.instrument("cache_deserialization_error.active_support",
error: error,
coder: self.class.name,
payload: payload
) if defined?(ActiveSupport::Notifications)
nil
end
endポイント:
- これまでも一部のエラーは握りつぶされていましたが、
- 「どの種類の例外をキャッチするか」
- 「どう扱うか(ログ/通知など)」 が整理され、**「デシリアライズに関するあらゆるエラーはミス扱い」**というポリシーがより一貫しました。
- 将来的な Ruby の警告や内部例外が出てもアプリの動作が止まりにくくなります。
2-2. SerializerWithFallback の調整
activesupport/lib/active_support/cache/serializer_with_fallback.rb
SerializerWithFallback は「新しいシリアライザと古いシリアライザの両方でデコードしてみる」ためのラッパです(例: JSON + Marshal のような組み合わせ)。
今回の修正で:
- 各シリアライザの
loadが例外を起こした場合も、「キャッシュミスとして扱う」というルールに合わせて動作するように整理されています。 - 具体的には
- まず「優先シリアライザ」で
loadを試す - ダメなら「フォールバックシリアライザ」で
loadを試す - どちらも失敗したら nil を返す(ミス扱い)
- まず「優先シリアライザ」で
- 例外は上位に伝播させず、内部で握りつぶして(もしくは通知だけして)nil を返す設計になります。
イメージコード:
def load(payload)
primary = @primary.load(payload)
return primary unless primary.nil?
@fallback.load(payload)
rescue StandardError => e
# ここでも例外はミス扱いに統一
nil
endこの結果、「新旧シリアライザのどちらの形式でもなかった」「途中で壊れたデータだった」ようなときは、確実にキャッシュミスとして動きます。
2-3. テストの追加・強化
activesupport/test/cache/cache_coder_test.rbactivesupport/test/cache/serializer_with_fallback_test.rb
- キャッシュバックエンドが切り詰められたデータや壊れたデータを返すケースを模したテストが追加。
- 代表的なシナリオ:
- デシリアライズ時に
TypeError/ArgumentError/StandardErrorなどが投げられる - 古いフォーマット・未知のフォーマット・中途半端なデータなど
- デシリアライズ時に
- 期待結果:
- アプリ側には例外が飛んでこず、
readの返り値はnilになる(キャッシュミスとして扱う)。
テストレベルで挙動が保証されたことで、「仕様」としての意味合いが強くなります。
2-4. tools/strict_warnings.rb の更新
tools/strict_warnings.rb
- strict モードでの Ruby 警告コントロール用スクリプトに微修正。
- デシリアライズ周り・例外周りの変更に伴って、警告や未使用変数などが出ないように調整していると思われます。
- 本質的には Rails 本体の品質確保用で、アプリ開発者への直接の影響はほぼありません。
- 影響範囲・注意点
3-1. 影響範囲
影響を受けるのは主に以下です。
- ActiveSupport::Cache を使用しているすべてのキャッシュストア
Rails.cache(memcached, redis, file_store, memory_store などすべて)- カスタムの
Cache::Store実装の多くもCache::Coderを使っている場合は影響
SerializerWithFallbackを使っているケース- Rails 内部、あるいは gem / アプリ独自のラッパがこのクラスを利用していれば対象
特に memcached のように、ネットワーク障害などで「切り詰められた値」「壊れた値」を返しうるストアに対して効果があります。
3-2. 実務上の挙動変化
- 以前: ある種のデシリアライズエラーがアプリケーションまで伝播し、
Rails.cache.readが例外を投げる可能性があった。 - 以後: そうしたエラーは「キャッシュミス」として扱われ、
nil(もしくは miss 相当の返り値)が返る。
これにより:
- キャッシュ障害がアプリケーション障害に直結しにくくなる
- 「キャッシュは最悪なくても動く」が理想の設計ですが、この変更がその思想により沿った挙動になります。
- 壊れたキャッシュデータが残っていても、そのキーへのアクセスで例外が出続けることがなくなる。
3-3. 注意点 / 互換性
- もしこれまで「デシリアライズの失敗を検知して何か特別な処理をする」ために例外を拾っていたコードがある場合、
- 今後は例外が発生せず、単に miss として扱われるため、
- 挙動が変わる可能性があります(例外ハンドリングに依存していたロジックが動かなくなる)。
- 「キャッシュが壊れていることを明示的に検知したい」場合は、
- ログ / 通知を発火するためのフック(ActiveSupport::Notifications など)が用意されているかを確認し、
- そちらを監視する形に切り替える必要があります。
- 壊れたキャッシュデータがあってもアプリは落ちませんが、
- キャッシュミス → 再計算 → 再保存 のフローで自然回復する設計であることが前提です。
- もし「常に同じ壊れた値を返すバックエンド」(例: 外部システムのバグ)があると、ミスが続きパフォーマンス劣化の原因になる可能性があります。
- 参考情報 (あれば)
- PR 本体: https://github.com/rails/rails/pull/56729
- タイトル: Better handle cache deserialization errors
- 作者: @byroot
- 関連しそうなコード:
ActiveSupport::Cache::StoreActiveSupport::Cache::CoderActiveSupport::Cache::SerializerWithFallback
- 運用上の推奨:
- 例外は表に出なくなりますが、
cache_deserialization_errorのようなイベントがある場合は通知・ログにフックしておくと、バックエンドの不具合検知に役立ちます。 - memcached / redis の監視とあわせて、キャッシュミスの急増も監視すると、デシリアライズエラーの多発を早期に気づけます。
- 例外は表に出なくなりますが、
#54982 Update Propshaft setup instructions in The Asset Pipeline guide [ci skip]
マージ日: 2026/2/5 | 作成者: @letsEstel
- 概要 (1-2文で)
Railsガイド「The Asset Pipeline」の Propshaft セットアップ手順が、実行しても画面上の変化が分かりにくかった問題を解消するために改善されました。具体的には、チュートリアルの中でホームページとルート定義を追加し、Propshaft を使ったアセット読み込みが実際に確認できるようにしています。
- 変更内容の詳細
※PRはガイド (guides/source/asset_pipeline.md) だけの変更で、コード本体には手は入っていません。
主なポイントは以下の通りです。
Propshaft セットアップ手順の補強
これまでのチュートリアルでは、Propshaft の導入コマンドなどを実行したあとに「何が見えるようになるのか」が不明瞭で、最後のコマンドを実行しても何も起きていないように感じられる、という問題がありました。
今回の修正で、Propshaft を導入したあとに表示されるサンプルの Home ページと、そのルーティングを追加する手順がガイドに追記されています。
典型的には以下のような流れが文章化されていると考えられます(コードは代表例であり、実際のガイドの抜粋イメージ):
# Propshaft を使った新規アプリ作成(例)
bin/rails new blog --css=tailwind --asset-pipeline=propshaft
cd blog続いて、トップページ用のコントローラとビューを作成する流れ:
bin/rails generate controller Home index生成された app/views/home/index.html.erb に、アセットを利用した簡単な表示例(CSS や画像、JavaScript の確認)が書かれている形がガイド入りしていることが多いです。例えば:
<!-- app/views/home/index.html.erb -->
<h1>Hello, Propshaft</h1>
<%= image_tag "logo.png" %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>そして、ルート定義:
# config/routes.rb
Rails.application.routes.draw do
root "home#index"
endこれにより、bin/rails server を起動して http://localhost:3000 にアクセスすると、
- Propshaft 管理下の CSS / JS が読み込まれていること
- 画像などのアセットが提供されていること
をブラウザで確認できる、という「目に見えるゴール」がチュートリアルに明示されるようになりました。
変更差分としては、asset_pipeline.md において:
- Propshaft 導入後の説明に、Home ページ追加 & ルーティング設定の手順が 24 行ほど追加
- 既存の文言のうち、読者にとって分かりづらかった部分が 3 行ほど削除・差し替え
といった形になっています。
- 影響範囲・注意点
影響範囲
- コードベースではなく「ガイド(ドキュメント)」のみへの変更です。
- 既存のアプリケーションの挙動には一切影響しません。
- edgeguides / guides.rubyonrails.org を見ながら Propshaft を試すユーザーの学習体験が改善されます。
注意点
- 新しい Propshaft セットアップ手順に従う場合、
- コントローラ生成 (
rails generate controller) - ルート設定 (
root "home#index")
といったステップが前提になるため、チュートリアル通りにやっていないと説明と挙動が合わない可能性があります。
- コントローラ生成 (
- ガイドは Rails の edge バージョンに合わせた内容であることが多いため、古い Rails(Propshaft 対応前のバージョン)ではそのまま適用できない場合があります。
- 新しい Propshaft セットアップ手順に従う場合、
- 参考情報 (あれば)
- 該当ガイド:
- The Asset Pipeline(最新版 / edge)
https://edgeguides.rubyonrails.org/asset_pipeline.html
- The Asset Pipeline(最新版 / edge)
- Propshaft プロジェクト:
この PR によって、Propshaft チュートリアルが「最後までやったのに画面上は何も変わらない」という状態から、Home ページを表示してアセットが効いていることを確認できるチュートリアルへ改善された、と理解しておくとよいです。
#56734 Make AM::AttributeSet::YAMLEncoder a Module
マージ日: 2026/2/4 | 作成者: @hmcguire-shopify
- 概要 (1-2文で)
ActiveModel のAttributeSet::YAMLEncoderをクラスからモジュールに変更し、Active Record モデルごとに生成されていたエンコーダ用オブジェクトを不要にしてメモリ使用とスキーマリロード時の扱いを簡素化した変更です。default_typesのキャッシュは既にモデルクラス側に持っているため、専用クラスをインスタンス化する必要がなくなったことをコード構造に反映しています。
- 変更内容の詳細
2-1. ActiveModel::AttributeSet::YAMLEncoder をクラス → モジュールへ
対象ファイル: activemodel/lib/active_model/attribute_set/yaml_encoder.rb (+4/-9)
これまで:
YAMLEncoderはクラスとして定義され、Active Record モデルごとにインスタンスが生成される設計だったと考えられます。- インスタンスが持つ意味のある状態は「モデルの
default_types」でしたが、それはすでにモデルクラス自身のインスタンス変数にキャッシュされていました。
変更後:
YAMLEncoderは モジュール に変更され、状態を持つ“オブジェクト”としてはもう使われません。- メソッドはモジュール関数 / モジュールメソッドとして提供され、必要な情報 (
default_typesなど) は呼び出し側(モデルクラス)から引数として渡す形、もしくは呼び出し元のクラス経由で取得する形に整理されています。
イメージ的な変化(実際のコード断片イメージ):
# 変更前(イメージ)
class ActiveModel::AttributeSet::YAMLEncoder
def initialize(model_class)
@default_types = model_class.default_attributes # など
end
def dump(record)
# @default_types を使ってレコードを YAML 化
end
end# 変更後(イメージ)
module ActiveModel::AttributeSet::YAMLEncoder
def dump(record, default_types:)
# 引数経由で渡された default_types を使って YAML 化
end
end※実際のメソッド名や引数は若干異なる可能性がありますが、ポイントは「インスタンス変数に状態を持たず、モジュールとして stateless に近い構造にした」ことです。
2-2. Active Record 本体側での利用方法の調整
対象ファイル:
activerecord/lib/active_record/core.rb(+2/-2)activerecord/lib/active_record/model_schema.rb(+0/-5)
Active Record 側では、今まで YAMLEncoder クラスを使っていた部分を、モジュールとして利用する形に合わせてリファクタリングしています。
代表的な変更点:
core.rb内で、以前は次のようなパターンがあったと推測されます:- モデルごとに
YAMLEncoder.new(self)のようにインスタンスを生成し、YAML エンコード時にそのインスタンスを参照。
- モデルごとに
- 変更後は:
YAMLEncoderのインスタンスを保持するロジックを削除。- 代わりに、YAML エンコードが必要なときに
AttributeSet::YAMLEncoderモジュールのメソッドを直接呼び出し、必要な情報 (default_typesなど) を引数やモデルクラス経由で渡すように変更。
model_schema.rb の削除行 (-5) から推測されるのは、「スキーマリロード時に YAMLEncoder インスタンスをクリアする」ようなコードが不要になった、という点です。
モジュール化によってインスタンス状態をリセットする必要がなくなったため、その一連の管理コードを削除しています。
- 影響範囲・注意点
3-1. 公開 API への影響
ActiveModel::AttributeSet::YAMLEncoderを 直接利用しているアプリやライブラリ がある場合、以下に注意が必要です:- 「クラスとして
newする」コードは動かなくなります。- 例:
encoder = ActiveModel::AttributeSet::YAMLEncoder.new(MyModel)はエラーになる可能性が高いです。
- 例:
- モジュールとして
include/extendして使う、あるいはモジュールメソッドを直接呼ぶように修正が必要です。
- 「クラスとして
Rails 本体の想定としてはこのクラスは内部実装寄りであり、外部から new されることはあまり想定されていないはずですが、メタプログラミング的に内部 API に依存しているアプリでは互換性問題が出る可能性があります。
3-2. メモリ・パフォーマンス的な影響
- 以前は「テーブル(モデル)ごとに YAMLEncoder インスタンスを1つ持つ」設計だったものが、「インスタンス不要(0件)」になります。
- 大量のモデルを持つアプリケーションでは、わずかながらオブジェクト数削減・GC 負荷軽減の効果があります。
- より重要なのは、「スキーマリロード時に YAMLEncoder インスタンスをクリアする必要がなくなった」ことで、reloader 周りのロジックが簡素化され、バグの入り込み余地が減る点です。
3-3. スキーマリロード / 開発環境での挙動
- ActiveRecord::ModelSchema から
YAMLEncoderに関連するリセット処理が削除されているため、開発環境でコードリロード / スキーマ変更を行う際にも、エンコーダ関連で古い状態が残るといった問題が起こりにくくなります。 - 既存の Rails アプリケーションで、YAML シリアライズされた属性(
serializeなど)を使っていても、通常は挙動上の違いは感じないはずです。内部での管理の仕方が変わっただけで、外から見た API は変わらないためです。
- 参考情報 (あれば)
- PR: https://github.com/rails/rails/pull/56734
- 関連しうる内部実装:
- ActiveModel::AttributeSet と、その YAML シリアライズ機能
- ActiveRecord::ModelSchema のスキーマキャッシュ・リロード周りのコード
この変更は主に「内部実装の整理 + パフォーマンス/メンテナンス性の改善」であり、通常のアプリケーションコードには非互換な影響はほとんどありません。ただし、ActiveModel::AttributeSet::YAMLEncoder をクラスとして new している場合のみ、モジュールとしての利用方法に書き換える必要があります。
#56733 Docs: Update pgAdmin name; fix foreign key wording
マージ日: 2026/2/4 | 作成者: @eglitobias
- 概要 (1-2文で)
Active Record Migrationsガイドの文言を、現状に即した・より正確なものにするためのドキュメント修正です。
具体的には、「PgAdmin III」という古い名称を「pgAdmin」に更新し、外部キーの例で誤解を招く「reviewers」を「authors」に修正しています。
- 変更内容の詳細
このPRで変更されたのは guides/source/active_record_migrations.md の2行のみです。
(1) 「PgAdmin III」→「pgAdmin」への更新
- Before(イメージ):
「…は PgAdmin III を使って確認できます」 - After:
「…は pgAdmin を使って確認できます」
つまり、PostgreSQL用GUIクライアントの名称が、現在一般的に使われている「pgAdmin」に統一されました。
Railsの機能やAPI自体には一切変更はなく、ツール名の表記のみの更新です。
(2) 外部キーの説明における「reviewers」→「authors」
Active Record Migrationsガイドの、外部キー (foreign key) に関するサンプル説明文が修正されています。
- Before(誤解を招く例):
「booksテーブルの外部キーauthor_idは、reviewersテーブルへの参照です」 - After(正しい関係を表す例):
「booksテーブルの外部キーauthor_idは、authorsテーブルへの参照です」
上記はあくまでイメージですが、趣旨としては:
author_idというカラム名から期待される参照先はauthorsテーブル- にもかかわらず、文中で
reviewersが使われており、読者を混乱させる
という不整合があったため、それを正しいテーブル名 authors に修正しています。
この変更により、以下のような典型的な関連づけのイメージと文書の整合性が取れるようになります:
class Book < ApplicationRecord
belongs_to :author # books.author_id → authors.id
end
class Author < ApplicationRecord
has_many :books
end- 影響範囲・注意点
- 影響範囲
- Rails本体のコード、API、挙動には一切変更はありません。
- 影響するのは「Active Record Migrationsガイド」の日本語・英語問わずドキュメント内容のみです。
- 開発者への実務的な影響
- 既存アプリケーションのマイグレーションやスキーマに影響はありません。
- 外部キーの説明がより直感的・一貫したものになるため、特にRails初心者や外部キー概念を学ぶ人にとって理解がしやすくなります。
- PostgreSQLを使っている場合、「pgAdmin III」という旧称を見て混乱する可能性が減ります(現行のツール名「pgAdmin」で検索しやすくなる)。
注意点として、このPRはあくまで表記修正であり、
- 「pgAdmin」を使わなければならないという意味ではない
- 既にpgAdmin以外のツール(psql, DBeaver, TablePlus など)を使っている場合に何かを変える必要もない
という点に留意してください。
- 参考情報 (あれば)
- 該当PR: https://github.com/rails/rails/pull/56733
- pgAdmin公式サイト: https://www.pgadmin.org/
- Active Record Migrationsガイド(最新英語版):
https://guides.rubyonrails.org/active_record_migrations.html
#56727 Use schema cache for primary key lookup during insert
マージ日: 2026/2/4 | 作成者: @matthewd
- 概要 (1-2文で)
Active Record が INSERT 時にプライマリキー名を特定する処理で、スキーマキャッシュ(schema_cache)を利用するようにした変更です。これにより、モデル側から PK 名が渡されないケースでも、都度 DB に問い合わせずキャッシュ済みのスキーマ情報から PK を引けるようになります。
- 変更内容の詳細
2-1. 背景となるコードパス
通常の「モデル経由の INSERT」(Model.create など)の場合、Active Record は既に知っているプライマリキー名(Model.primary_key)を使って SQL を組み立てるため、この変更部分のコードパスはあまり通りません。
一方で、より低レベルなインサート処理(insert 系メソッドを直接呼ぶようなケースなど)では、テーブルのプライマリキー名をアダプタ経由で問い合わせる必要があります。
従来は、この PK 名の解決にスキーマキャッシュが考慮されておらず、DB に依存した問い合わせ(primary_key 取得)を毎回行う可能性がありました。
2-2. 具体的な変更点
1) 抽象アダプタ (abstract/database_statements.rb) の変更
INSERT 用のメソッド(
insert/insert_fixture/ それに類するメソッド)内で、プライマリキー名を解決する際に、schema_cacheをまず見るように変更されています。擬似コードイメージ:
ruby# 変更前(イメージ) primary_key = primary_key(table_name) # 変更後(イメージ) primary_key = if schema_cache&.primary_keys(table_name) schema_cache.primary_keys(table_name) else primary_key(table_name) # 従来の遅いパス(DB問い合わせ) end実際のコードは 1 行差分程度ですが、意味としては「PK 名を取得するときに、まず schema_cache を使う」という方向です。
2) PostgreSQL アダプタ (postgresql/database_statements.rb) の変更
- PostgreSQL 固有の INSERT 実装(
insert/exec_insert/ RETURNING に絡む部分など)でも同様に、PK 名前解決に schema cache を使うよう 1 行レベルの変更が入っています。 - これにより、PostgreSQL では特に、
RETURNING primary_keyといった SQL を組み立てる際もキャッシュを活用できます。
3) テストの追加・修正
postgresql_adapter_test.rb- PostgreSQL アダプタで、schema cache が PK 名の取得に使われることを検証するテストが追加。
- 例として:
- スキーマキャッシュをロードした状態で INSERT を実行
- DB へのメタデータ問い合わせが行われない、もしくは
schema_cacheに格納されている PK 名が使われることを確認するテストが含まれていると考えられます。
sqlite3_adapter_test.rb- SQLite3 アダプタでも同様に、INSERT 時に schema cache が参照されることを確認するテストが追加。
- 既存テスト 1 行の調整(
-1)と、新規のアサーションなど(+9)が加えられています。
schema_cache_test.rbActiveRecord::ConnectionAdapters::SchemaCache自体に対し、PK 名取得と INSERT の連携をより明示的に検証するテストが追加。- 「schema cache 上に PK 情報を持っているときに、INSERT の PK 解決がそれを使う」ことを保証する内容になっていると考えられます。
- 影響範囲・注意点
3-1. パフォーマンス面の影響
- このコードパス自体は「高トラフィックではない」(PR 説明より)ため、アプリ全体のパフォーマンスが劇的に変わるケースは多くありません。
- ただし、以下のようなケースでは地味に効いてきます:
insert/insert_fixture/ 独自ローレベル INSERT を多用している- テストツールやシード・バッチ処理などでアダプタの insert 系メソッドを直接叩いている
- こうした場面で、PK 名を得るために毎回 DB メタデータにアクセスしていた部分が、スキーマキャッシュで済むため、DB の負荷・レイテンシがわずかに改善します。
3-2. 正確性・互換性
- スキーマキャッシュは通常、マイグレーション後などにリセット/再生成される運用が前提です。
- 今回は「既に他の場所で利用している schema_cache を、この PK 解決の経路でも利用する」だけなので、互換性リスクは非常に小さいです。
- スキーマが変更されたのに schema_cache を更新していない(= 元々危険な状態)場合、今回の変更により「INSERT の PK 名解決にも古い情報が使われる」ようになりますが、そもそもそうした状態はサポート外の運用と言えます。
3-3. カスタムアダプタへの影響
- カスタムの DB アダプタで、
primary_key解決ロジックや schema_cache 統合を独自に実装している場合:- 今回の変更によって、抽象層から「schema_cache を優先する」という方針が強くなるので、実装がこれと矛盾していないかを確認した方がよいかもしれません。
- ただし、変更はかなり小さく、基本的には「抽象クラス側が schema_cache を見るだけ」なので、多くのアダプタには影響しないはずです。
- 参考情報 (あれば)
- PR タイトル: Use schema cache for primary key lookup during insert
- PR 番号: #56727
- 作者: matthewd
- マージ日時: 2026-02-04T07:07:25Z
関連して見ておくとよいもの:
ActiveRecord::ConnectionAdapters::SchemaCacheの実装:primary_keys(table_name)やprimary_key(table_name)の振る舞い
- 各アダプタ(PostgreSQL / SQLite3 など)の
insert/exec_insert実装:- PK 名をどのように受け取り・解決し・SQL に組み込んでいるか
- マイグレーション後に schema_cache をリセット/再生成する運用フロー:
- Railsアプリでは
config.active_record.dump_schema_after_migrationやschema_cache.ymlを利用している場合、その周辺の設定とあわせて確認すると理解しやすいです。
- Railsアプリでは
#56725 Fix broken links to YJIT documentation [ci skip]
マージ日: 2026/2/3 | 作成者: @ykttdn
- 概要 (1–2文で)
Rails Guides(パフォーマンスチューニングガイド)内で、YJITドキュメントへのリンクが Ruby 本体側の変更により壊れていたため、正しい YJIT ドキュメント URL に更新した PRです。コードの挙動や機能には影響せず、ドキュメントのリンク修正のみを行っています。
- 変更内容の詳細
- 対象ファイル:
guides/source/tuning_performance_for_deployment.md - 変更内容:
- YJIT に関する説明箇所で参照している外部リンク(YJIT ドキュメント)を、Ruby 本体リポジトリ側の最新のドキュメント構成に合わせて修正。
- 差分としては、既存リンク 3 箇所前後を、新しい URL に置き換える形で
+3/-3の変更が行われています。
実際の差分は PR 本文には出ていませんが、文脈から考えると、例えば次のようなイメージの修正です(あくまでイメージです):
- 詳細は Ruby の YJIT ドキュメントを参照してください:
- https://docs.ruby-lang.org/en/master/YJIT.html
+ 詳細は Ruby の YJIT ドキュメントを参照してください:
+ https://docs.ruby-lang.org/en/master/yjit/README_rdoc.htmlRuby 本体側でのドキュメントパス変更が、下記 PR で行われており、それに追随した形です。
- 影響範囲・注意点
影響範囲:
- Rails Guides(特に「本番環境向けパフォーマンスチューニング」ガイド)を読む際に、YJIT に関する参照リンクが正しく機能するようになります。
- アプリケーションコードや Rails 本体の挙動には一切影響なし(ドキュメントのみの変更)。
- テストや CHANGELOG 更新は、チェックリスト上は考慮されていますが、実質は「ドキュメント変更のため不要」という扱いです。
注意点:
- もし自分の社内 Wiki やブログなどで旧 YJIT ドキュメントの URL を参照している場合、この PR がヒントになるので、同様にリンク切れがないか確認するとよいです。
- 将来 Ruby 側で再度ドキュメント構成が変わった場合も、Rails Guides が追随する必要があるため、YJIT ドキュメントの場所は Ruby 本体の変更に依存することを意識しておくとメンテ上有用です。
- 参考情報 (あれば)
- Ruby 本体側の関連 PR:
- YJIT ドキュメントリファクタ/移動などを行った PR:
- Rails ガイド(edge版, 英語)
- Tuning Performance for Deployment (本 PR の対象ガイド):
- YJIT についての概要(Ruby 公式)
#56705 Fix AM::Serialization#read_attribute_for_serialization doc
マージ日: 2026/2/3 | 作成者: @p8
- 概要 (1-2文で)
ActiveModel::Serialization のread_attribute_for_serializationを、aliasベースの定義から通常メソッドとして定義し直すことで、公式ドキュメントに正しく掲載されるようにするとともに、引数を「キー1つ」に限定する PR です。挙動は従来と変えず、インターフェイスの明示化とドキュメント改善を目的としています。
- 変更内容の詳細
これまで read_attribute_for_serialization は、おそらく以下のように send の alias として定義されていました(実際のコードは概念的にこういう形です):
# 変更前(イメージ)
alias :read_attribute_for_serialization :sendこの場合の問題点:
- RDoc/YARD などのドキュメント生成ツールは、alias に対してはデフォルトではドキュメントを生成しないことが多い
- そのため
read_attribute_for_serializationが「ActiveModel::Serialization が提供する公式なフックメソッド」としてドキュメントに現れない sendを alias しているだけなので、メソッドシグネチャ的には「任意個の引数」を受け取れるように見えてしまう(実態としてはシリアライズ用途では1つのキーを渡すことを想定している)
この PR では、read_attribute_for_serialization を alias ではなく「明示的にメソッド定義」するように変更しています。
変更後のイメージ:
def read_attribute_for_serialization(key)
send(key)
endポイント:
- メソッドとして明示的に定義したことで、
- ドキュメント生成時に
read_attribute_for_serializationがきちんと拾われる - 引数名(
key)や引数が1つであることがドキュメントに反映される
- ドキュメント生成時に
- 実装としては
send(key)を呼び出しているだけなので、動作上は従来とほぼ同等 - ただし、インターフェイスとして「キーを1つだけ受け取るメソッド」であることがシグネチャ上も明確になった
PR 説明文にもある通り:
This also encodes the method signature to only accept a single key as argument, where
sendaccepts any arguments.
つまり、元々 alias だったときは send の都合上「どんな引数リストでも理論上は通る」状態だったが、今回の変更で「キー1つだけを受け取る」メソッドとして仕様を固定化した、という位置付けです。
- 影響範囲・注意点
- 通常の ActiveModel / ActiveRecord モデル利用者への影響はほぼなし
- 多くのケースでは
read_attribute_for_serialization(:attribute_name)のように1引数で呼ばれており、その動作は変わりません。
- 多くのケースでは
- 影響があり得るケース:
read_attribute_for_serializationを自前で多引数で呼び出していたコードがある場合- 例:
read_attribute_for_serialization(:foo, :bar)のような使い方をしていた場合、今後はArgumentErrorになる可能性があります(正式な想定外の使い方なので、今回の変更でより明確に不正と分かるようになったとも言えます)。
- 例:
read_attribute_for_serializationをオーバーライドしているクラスが、シグネチャを*argsなどにしていた場合- 互換性の観点では、
def read_attribute_for_serialization(key)のように「単一キーを取るメソッド」として扱うのが望ましいです。
- 互換性の観点では、
- ドキュメント面:
- 今後生成される Rails API ドキュメント上で
ActiveModel::Serialization#read_attribute_for_serializationが表示されるようになり、カスタマイズフックとして発見しやすくなります。 - シグネチャが明確になったことで、ライブラリアップデート時に IDE などでの補完や型サポート(RBS, Sorbet, etc.)にも寄与する可能性があります。
- 今後生成される Rails API ドキュメント上で
- 参考情報 (あれば)
- PR 本体: https://github.com/rails/rails/pull/56705
- 関連する概念:
ActiveModel::Serializationモジュール: モデルオブジェクトをハッシュ化・JSON 化するための仕組みread_attribute_for_serialization: シリアライズ時に各属性値を取得するためのフックメソッド。
モデル側でこのメソッドをオーバーライドすることで、「シリアライズ時だけ別の値を返す」といったカスタマイズが可能です。
#56724 Remove useless connection verification when pinning connection
マージ日: 2026/2/3 | 作成者: @byroot
- 概要 (1-2文で)
このPRでは、コネクションを「ピン留め」するときに毎回行っていたverify!呼び出しを削除し、既に実装されている「最近使われたコネクションは再検証しない」ロジックに一元化することで、無駄なコネクション検証をなくしています。結果として、不要なクエリ(例:SELECT 1)の発行が減り、Active Record のコネクション管理がわずかに効率化されます。
- 変更内容の詳細
全体像
- 対象: ActiveRecord のコネクションプールおよび抽象アダプタ
- 主な変更:
- コネクションを「pin(固定)」する際に行っていた明示的な
verify!呼び出しを削除 - その代わり、アダプタ側の「最近使用されたコネクションは再検証しない」ロジックに任せるよう整理
- コネクションを「pin(固定)」する際に行っていた明示的な
変更点1: connection_pool 側の明示的な verify! 呼び出しの削除
activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb から、コネクションを pin するときに呼んでいた connection.verify! が削られています。
イメージとしては、以下のようなコードパスが:
def pin_connection
connection = checkout
connection.verify! # ← これが削除された
@pinned_connection = connection
endのような形から、「チェックアウトしただけで即 verify! は呼ばない」挙動に変わった、という内容です(実際のメソッド名や詳細は簡略化していますが、意味合いとしてはこの通りです)。
元々 Active Record には以下のようなロジックが既にあります:
- コネクション取得時に「前回の使用時刻」等を見て、一定時間内に使われていれば
verify!しない - 逆に、長時間使われていない場合・接続断が疑われる場合などは
verify!を行う
PR本文の "We now have some decent logic about not revalidating a connection that has been used recently" はまさにこの部分を指しています。
したがって、pin 時の明示的な verify! はこのポリシーと二重になっており、「ほぼ常に不要なダブルチェック」になっていたため削除されています。
変更点2: abstract_adapter 側のサポートコード追加
activerecord/lib/active_record/connection_adapters/abstract_adapter.rb には 6 行の追加があります。差分の狙いは、おそらく以下のような点です:
- 「最近使用されたコネクションは検証しない」ためのヘルパメソッドやタイムスタンプ管理の整理
verify!実装の一部を共通化、または条件付きでの実行に関するロジック強化
(具体的なメソッド名は差分全文がないため推測になりますが、PRメッセージから見て、verify! の呼び出し条件や「最近の使用」を判定するロジックに関わるコードである可能性が高いです)
この追加により、以下がより明確になります:
- 「いつ
verify!すべきか」という判断をアダプタ側(および関連ヘルパ)に集約 - connection_pool 側は「検証ポリシーを知らない」薄い層になり、責務分離が改善
- 影響範囲・注意点
影響範囲
- 対象となるのは、マルチ DB / シャーディング / connection switching などで「コネクションをピン留めする」仕組みを使っているアプリケーションやライブラリです。
- また、独自アダプタや、connection_pool / adapter をモンキーパッチしている場合は影響受ける可能性があります。
実務的な影響
無駄なヘルスチェックの減少
- これまで、ピン時に毎回
SELECT 1的な検証クエリが走っていたケースが、不要な場合には実行されなくなります。 - 大量のコネクション切り替えが発生する高トラフィック環境では、わずかながらもレイテンシ・DB 負荷が軽減される可能性があります。
- これまで、ピン時に毎回
接続の信頼性
- 「長時間使われていないコネクションをピンした途端、壊れていた」というケースは、従来どおり抽象アダプタの
verify!ロジックで検出される設計です。 - PRの意図としては「不要な二重チェックを削っただけ」であり、「接続が切れているのに気づけなくなる」ような性質の変更ではないことが示唆されています。
- 「長時間使われていないコネクションをピンした途端、壊れていた」というケースは、従来どおり抽象アダプタの
カスタムコードとの互換性
- もしアプリや gem 側が「ピン時に
verify!が必ず呼ばれること」を前提にしたワークアラウンドを積んでいた場合、その前提は崩れます。 - 典型的には、
verify!をオーバーライドして何か副作用(ログ、メトリクス、独自リカバリ等)を期待していた場合などです。 - こうしたコードは、「verify! の呼び出しタイミングは ActiveRecord の内部実装詳細であり、将来的に変わりうる」ものとして見直す必要があります。
- もしアプリや gem 側が「ピン時に
注意点
- DB 接続まわりで「特定のタイミングでだけ接続チェックをしたい」といった特殊要件がある場合は、この変更後の
verify!呼び出しパターン(どこで、いつ実行されるか)を実際にログ等で確認しておくと安心です。 - 接続プール関連のバグやタイムアウト問題をデバッグする際には、「ピン時には自動で
verify!されない」という前提でコードリーディングを行う必要があります。
- 参考情報 (あれば)
- 関連コード:
activerecord/lib/active_record/connection_adapters/abstract_adapter.rbactiverecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
- 背景となる Active Record の挙動:
- コネクションプールは各コネクションの「最終使用時刻」などを管理し、一定時間以上アイドルだった場合のみ
verify!を呼ぶといったポリシーを持っています。 verify!自体は各アダプタ(PostgreSQL, MySQL, SQLite 等)が実装し、通常は軽量なクエリ (SELECT 1等) による疎通確認を行います。
- コネクションプールは各コネクションの「最終使用時刻」などを管理し、一定時間以上アイドルだった場合のみ
このPRは、そうした既存ロジックを前提に、「ピン留め時のダブルチェックをやめてシンプルにする」リファクタリング/最適化、と理解するとよいです。
#56716 [ci skip] Correct typo in ActiveRecord changelog
マージ日: 2026/2/3 | 作成者: @coderhs
概要 (1-2文で)
このPRは、Active Record の CHANGELOG に記載されていたデータベース接続に関する文言のタイポ(誤記)を修正するものです。コードや挙動の変更は一切なく、ドキュメントのみの修正です。変更内容の詳細
- 対象ファイル:
activerecord/CHANGELOG.md - 変更内容は 1 行のテキスト修正のみで、データベース接続(connections)に関する説明文中のスペルミスや用語の誤りを正しい表記に直しています。
- [ci skip] がタイトルに含まれている通り、ドキュメント変更のみのため CI を走らせない PR です。
実際のコードや API のシグネチャ、設定オプションなどは一切変更されていません。
CHANGELOG 上の例示コードや、接続管理に関する説明の「単語」レベルの修正に留まると考えてよいです。
- 影響範囲・注意点
- ランタイム挙動: 影響なし
- Active Record の接続管理やクエリ実行、トランザクション動作などには一切変更がありません。
- マイグレーション・設定: 変更不要
database.ymlなどの設定ファイルの見直しや、アプリケーション側のコード修正も不要です。
- CHANGELOG の読み取り:
- 今後、該当バージョンの変更点を確認する際に、より正確な記述に基づいて挙動を理解できるようになります。
- 既に CHANGELOG を参照していた場合は、「あれ?文脈的におかしいな」と感じていた箇所が自然な表現に直っている程度の差分です。
- 参考情報 (あれば)
- このPRはドキュメント修正専用のため、Rails をバージョンアップしても今回の変更が原因で不具合が出ることはありません。
- 実際の接続管理ロジックや API の仕様を確認したい場合は、CHANGELOG よりも以下を優先するとよいです:
activerecordの公式ガイド: 「Active Record Basics」「Active Record Connection Handling」関連セクション- 対象バージョンの API ドキュメント (
ActiveRecord::Base.connection,ActiveRecord::ConnectionAdaptersなど)
#56683 Pass sql query to query log tags
マージ日: 2026/2/2 | 作成者: @fatkodima
- 概要 (1–2文で)
Active Record の「クエリログタグ (query_log_tags)」に、実行される SQL 文字列がコンテキストとして渡されるようになりました。これにより、SQL 内容に応じた柔軟なタグ付けやデバッグ情報の付与が簡単に行えます。
- 変更内容の詳細
何が変わったか
従来 config.active_record.query_log_tags で設定した Proc には、主に「バックトレース 1 行分」などの情報が context として渡されていましたが、この PR により context[:sql] として「実際に実行される SQL 文字列」も渡されるようになりました。
これにより、SQL に基づいたタグ生成が可能になります。
新しい使い方の例
PR 説明にある例:
config.active_record.query_log_tags = [
sql_length: ->(context) { context[:sql].length }
]この設定により、各 SQL 実行時のログに「クエリ文字列の長さ」をタグとして付与できます。
(実際のログのフォーマットは既存の query log tags の仕組みに従います。)
これと同様に、例えば以下のような応用が可能です:
config.active_record.query_log_tags = [
# レプリカ向けクエリかどうかを SQL から推定してタグ付け
replica_candidate: ->(context) do
sql = context[:sql]
# 例: 特定テーブルへの読み取りはレプリカを想定、など
sql.match?(/FROM\s+large_read_only_table/i)
end,
# 危険そうなクエリの検出用タグ
uses_select_star: ->(context) do
context[:sql].match?(/\bselect\s+\*/i)
end,
]実装面でのポイント
(ファイル差分の要約)
activerecord/lib/active_record/query_logs.rb- クエリログタグを構築する際のコンテキストに
:sqlキーが追加された。 - 既存のタグ生成フローは保ちつつ、SQL 文字列を渡すだけの後方互換的な拡張。
- クエリログタグを構築する際のコンテキストに
activerecord/test/cases/query_logs_test.rbcontext[:sql]が利用可能であることを検証するテストが追加。
activerecord/CHANGELOG.md- Active Record の変更点として、本機能追加が記載された。
- 影響範囲・注意点
既存コードへの影響
contextに新たに:sqlが追加されるだけで、既存のquery_log_tags設定はそのまま動作します。- 既存タグ定義が
contextのキーを前提にしていても、sqlの追加は破壊的変更にはなりません。
セキュリティ / プライバシー
context[:sql]をそのままログタグに出力すると、クエリ文字列(およびバインドされるパラメータ内容)がログに露出しやすくなります。- 個人情報や機密情報がプレーンテキストで SQL に含まれるケースでは、マスキングやハッシュ化などの処理を行うべきです。ruby
config.active_record.query_log_tags = [ sql_fingerprint: ->(context) do # SQL 全体は出さず、ハッシュにして特徴だけログに残す Digest::SHA256.hexdigest(context[:sql]) end ]
パフォーマンス
- 非常に長い SQL や大量のクエリに対して、タグ計算が重くなるロジックを書くとオーバーヘッドになる可能性があります。
- タグで SQL をそのまま出力するより、長さやハッシュ、正規表現による判定など、必要な情報に絞ることが推奨されます。
レプリカ切り替えデバッグの用途
- PR 動機にもある通り、「なぜこのクエリがレプリカに飛ばないのか」を調べるために、特定条件の SQL にだけ追加のバックトレース情報やフラグをログに埋め込む、といった使い方が容易になります。
- 今までも
ActiveSupport::Notifications.subscribe("sql.active_record")で実現可能でしたが、query_log_tags設定だけで完結するようになり、アプリケーション設定として管理しやすくなります。
- 参考情報 (あれば)
- この PR: https://github.com/rails/rails/pull/56683
- 関連 API:
config.active_record.query_log_tagsActiveSupport::Notifications("sql.active_record"イベント)
- Active Record のクエリログタグに関するドキュメント(最新の Rails ガイド / API リファレンスを参照)
#56695 Use a single temporary connection pool when checking test schema
マージ日: 2026/2/2 | 作成者: @ilianah
- 概要 (1-2文で)
テスト用スキーマのチェック時に、1つの DB 設定につき「2つ」作られていた一時的なコネクションプールを「1つ」に統合し、同じプールを使い回すようにした PR です。これにより、テスト時のスキーマ・マイグレーション確認処理のオーバーヘッドが削減されます。
- 変更内容の詳細
背景 / 問題点
ActiveRecord::Migration.load_schema_if_pending! は、以下の2つの処理を行う際に一時的なコネクションプールを使っています:
- スキーマが最新かどうかのチェック
- 保留中マイグレーション(pending migrations)のチェック
従来は「それぞれで別々に一時プールを作成」していたため、
1 DB 設定あたり 2 回プール作成 → 2 回クリーンアップ
という無駄なコストが生じていました。
この PR の変更点
この PR では、テスト用スキーマを確認する処理で:
- 1つの一時的なコネクションプールを作成
- そのプールを
- スキーマのチェック
- pending マイグレーションのチェック の両方で「再利用」するように変更しています。
主な変更点のポイント:
ActiveRecord::Migration.load_schema_if_pending!内のロジックを整理し、- これまで別々に呼んでいた「スキーマ更新の要否確認」と「pending マイグレーションの確認」を、
- 同一プールを共有するフローにした。
ActiveRecord::Tasks::DatabaseTasks側のヘルパーメソッド(スキーマチェック関連)も、それに合わせて一時プールの扱いを調整。activerecord/CHANGELOG.mdに、この挙動改善に関するエントリを追加。
※ PR タイトルと説明、および差分の規模(3ファイル / +50 -17)から判断すると、挙動を変えるというよりは「内部実装の効率化」が主で、API 仕様の変更は行っていません。
- 影響範囲・注意点
影響する場面:
ActiveRecord::Migration.load_schema_if_pending!を通じて、- テストスイート起動時にスキーマ/マイグレーション状態をチェックする処理
- 複数 DB(multiple databases)を利用しているプロジェクトのテスト環境
- 特に、DB 接続確立コストが高い環境(リモート DB、制限された CI リソースなど)では、わずかながら起動時間やリソース消費の改善が見込めます。
注意点:
- 1つの一時プールで2種類のチェックを行うため、もしアプリ側が「pending マイグレーションチェック時の接続状態」に過度に依存しているような特殊なコードを書いていた場合、動きに差異が出る可能性はありますが、通常の Rails アプリでは想定されません。
- 公開 API(メソッドシグネチャなど)には変更がない想定なので、既存コードの修正は基本的に不要です。
- コネクションプールのライフサイクル管理(作成・クリーンアップ)がまとめられているため、万一そこにバグがあるとテスト時の接続リークにつながる可能性はありますが、PR ではテストも追加・更新されている旨がチェック項目で示唆されています。
- 参考情報 (あれば)
- 対象メソッド:
ActiveRecord::Migration.load_schema_if_pending!ActiveRecord::Tasks::DatabaseTasks内のスキーマチェック用ユーティリティ
- 変更ログ:
activerecord/CHANGELOG.mdに、テストスキーマチェック時の一時コネクションプール最適化に関する項目が追加されています。
- 関連するコンテキスト:
- Rails のテスト起動時には、
load_schema_if_pending!が走り、db/schema.rbまたはdb/structure.sqlが最新か- まだ適用されていないマイグレーションがないか
を確認します。
- この PR はその裏側の「接続管理」の最適化であり、テストスキーマ周りの一般的なワークフロー(
bin/rails db:test:prepareなど)には表面的な変更はありません。
- Rails のテスト起動時には、
#56715 [ci skip] Fix formatting of backtrace description in configuring.md
マージ日: 2026/2/2 | 作成者: @coderhs
概要 (1-2文で)
configuring.md ガイド内の backtrace 説明文に含まれていた不要な英単語 "the" を削除し、文章のフォーマット/文法を整えたドキュメント修正です。コードや挙動には一切影響せず、ドキュメントのみの微修正です。変更内容の詳細(あればサンプルコードも含めて)
- 対象ファイル:
guides/source/configuring.md - 変更点は1行のみで、backtrace に関する説明文中の「extra
the」(余分な "the") を削除しています。 - 例 (イメージ):diff
- This option controls the the format of the backtrace output...
- This option controls the format of the backtrace output...
のように、英語として不自然な重複を取り除き、読みやすい表現に修正しています。
- PR タイトルに
[ci skip]が付いていることからも分かる通り、テストやビルドは不要な単純なドキュメント修正として扱われています。
- 影響範囲・注意点
- Rails 本体のコード、設定オプション、API 仕様などには一切変更はありません。
- 既存アプリケーション、設定ファイル (
config/environments/*.rbなど)、実行時の backtrace 表示やログ出力の挙動にも影響はありません。 - 影響は Rails ガイドの読み手 (特に backtrace / エラー出力の設定方法を確認する開発者) に限定され、文章がわずかに読みやすくなっただけです。
- 参考情報 (あれば)
- PR: https://github.com/rails/rails/pull/56715
- 対象ガイド: Rails Guides「Configuring Rails Applications」(configuring.md)
- backtrace の設定方法や関連オプションを確認する際に参照するドキュメントの一部の文言修正です。
#56712 [ci skip] Fix typo in test/guide stategy -> strategy
マージ日: 2026/2/1 | 作成者: @coderhs
- 概要 (1-2文で)
Railsのテストコードとガイド文書内にあった「stategy / statetgy」というタイポが、正しい「strategy」に修正されたPRです。機能追加や挙動変更は一切なく、ドキュメントおよびテストの表記を整えるメンテナンス目的の変更です。
- 変更内容の詳細
対象ファイル
activerecord/test/cases/migration_test.rbguides/source/active_record_migrations.md
両方とも、Active Record のマイグレーションに関連する箇所で、用語「strategy」の表記ゆれ・タイポがありました。
具体的な修正例(イメージ)
- def test_migration_stategy
+ def test_migration_strategyあるいはコメントや説明文中の:
- This statetgy is used when...
+ This strategy is used when...といった形で、「strategy」という単語の綴りだけが修正されています。
PRタイトルに [ci skip] が付いているのは、テキスト修正のみでありテスト実行を省略する目的です(CIを走らせるほどの変更ではないため)。
- 影響範囲・注意点
ランタイムの挙動への影響なし
実行コード(ライブラリ部分)は変更されておらず、テストコードとガイド文書のみの修正です。そのため、Railsアプリケーションの挙動やAPI仕様には影響しません。テスト名・メッセージに依存している場合の注意
もしmigration_test.rb内のテスト名やエラーメッセージ文字列に対して、外部ツール(例: テスト結果パーサ、メトリクス集計ツールなど)が「文字列完全一致」で依存している場合、名称変更によって影響を受ける可能性があります。ただし、通常のRailsアプリ開発ではほぼ問題にならない範囲です。ドキュメントの可読性向上のみ
ガイドを読んでいる際に、「stategy / statetgy」という誤記がなくなり、用語の一貫性が高まりました。学習・調査の際に混乱が減る程度の改善です。
- 参考情報 (あれば)
- PR: https://github.com/rails/rails/pull/56712
- ガイド該当箇所(Active Record Migrations ガイド):
https://guides.rubyonrails.org/active_record_migrations.html
※ 本PRマージ後、英語版ガイド上の「strategy」に関連する説明文が正しいスペルで表示されます。
#56426 Make rescue_from_handled.action_controller store the full backtrace
マージ日: 2026/2/1 | 作成者: @zzak
- 概要 (1-2文で)
config.action_controller.rescue_from_event_backtraceを:arrayに設定した場合、rescue_from_handled.action_controller通知ペイロードにも「フルバックトレース(配列)」が載るようにした変更です。併せて、バックトレースからRails.rootを削った形で保持する処理や、関連設定・ガイド・テストの整備が行われています。
- 変更内容の詳細
2-1. 新しい設定オプションの挙動
既存フラグ:
config.action_controller.rescue_from_event_backtrace
この設定を :array にすると、
action_controller.rescue(既存)action_controller.rescue_from_handled(Rails 8.1 からの新イベント)
の両方の通知ペイロードで「フルバックトレース」が 配列形式 で格納されます。
以前は、rescue_from_handled 側ではこの設定が十分に反映されておらず、フルバックトレースがペイロードに入っていませんでしたが、この PR で揃えられています。
イメージ(通知ハンドラから参照される payload):
ActiveSupport::Notifications.subscribe("rescue_from_handled.action_controller") do |name, started, finished, unique_id, payload|
# ここに full_backtrace が array として入るようになる
backtrace = payload[:backtrace] # => ["app/controllers/users_controller.rb:10:in `index'", ...]
end※ 実際のキー名は実装側に依存しますが、PR の文脈から「配列形式のバックトレース」が通知されることがポイントです。
2-2. Rails.root プレフィックスの削除
テストが追加されたと書かれている通り、バックトレースの各行から Rails.root を delete_prefix する処理が導入されています。
例:/my/app/path/app/controllers/users_controller.rb:10:in 'index'
→ app/controllers/users_controller.rb:10:in 'index'
これにより、
- ログやイベントペイロードのパスがより短く読みやすくなる
- パスに環境依存要素(フルパス)が含まれにくくなる(オブザーバビリティツールやマルチ環境での集計に有利)
といった効果があります。
2-3. サブスクライバ・ログサブスクライバ周りの変更
以下のあたりが更新されています:
action_controller/structured_event_subscriber.rbaction_controller/log_subscriber.rbaction_controller/railtie.rb
主な意図は:
rescue_from_handled.action_controllerイベントのペイロードに、rescue_from_event_backtrace設定を反映させる- バックトレースの整形(
Rails.rootの削除)を共通化しつつ、通知に適切な形で乗せる
2-4. 新フレームワークデフォルトと設定ガイド
次バージョン (8.2) のためのテンプレートが更新されています:
config/initializers/new_framework_defaults_8_2.rb.ttにrescue_from_event_backtrace関連の初期値やコメントを追加guides/source/configuring.mdに該当オプションの説明を追加
これにより、新規アプリで rails app:update を行った時に、この設定項目とデフォルトの意味がガイド/initializer コメントを通じて明示されます。
- 影響範囲・注意点
3-1. オブザーバビリティ(o11y)やログ集約への影響
rescue_from_handled.action_controllerイベントをすでに購読している場合、ペイロードのbacktrace関連の中身が変わる/増える可能性があります。- 特に
:arrayを指定している場合、これまでより詳細なバックトレース情報が入る ため:- ログ・APM・トレーシングツールのフィールドマッピングが変わる可能性
- 取り込むデータ量が増える(バックトレース全行分)点に注意
作者も PR 説明で「既に o11y 側でこのイベントを取り込み始めた人がいるかもしれないため、互換性に配慮したい」と述べているため、ペイロード仕様に依存しているコードがないか要確認です。
3-2. ファイルパスの変化
delete_prefix(Rails.root) により、
- 以前フルパスを期待していた処理(例: 特定ディレクトリ配下のファイル検出を絶対パスで行う等)
- パスを正規表現でマッチさせていた処理
がある場合、パス形式が変化しても問題ないか確認が必要です。
特に、SaaS APM などで「source map」や「code link」を張る仕組みを自前で組んでいる場合はパス解決ロジックを併せて見直してください。
3-3. 将来的な非推奨化の可能性
PR 説明文中で、作者はこのフラグを将来的に:
- deprecate(非推奨)し
- remove(削除)
することも視野に入れているとコメントしています。
現時点では削除はされていませんが、以下のような流れが予想されます:
- 8.1/8.2 でこの挙動を提供(今回の PR)
- CHANGELOG とガイドで周知
- 将来バージョンでフラグを非推奨化し、デフォルト挙動の一本化を検討
そのため、長期的には「フラグに依存しない」かたちの運用(例: 「常にフルバックトレースを想定する」)を前提に観測・ロギング側を設計すると、将来の変更の影響を減らせます。
- 参考情報 (あれば)
- この PR:
https://github.com/rails/rails/pull/56426 - 関連 issue(Fixes: #56060 と言及あり):
https://github.com/rails/rails/issues/56060 - 関連するイベント名:
rescue_from_handled.action_controller(Rails 8.1 で追加された新イベント)rescue.action_controller/rescue_with_handler.action_controller系の既存イベントとの仕様差に注意するとよいです。
運用上は、config.action_controller.rescue_from_event_backtrace = :array を有効にした状態で、実際に発火する通知ペイロードをローカルやステージング環境で ActiveSupport::Notifications からダンプして、中身を確認しておくことを推奨します。
#56454 Add unique_by option to insert_all!
マージ日: 2026/2/1 | 作成者: @chaadow
- 概要 (1-2文で)
ActiveRecord::Relation#insert_all!にunique_by:オプションが追加され、既に対応済みだったinsert_allと同等のインターフェイスになりました。これにより、一括INSERT時に使用するユニークインデックスをinsert_all!側でも明示的に指定できるようになります。
- 変更内容の詳細
何ができるようになったか
これまで:
# OK: insert_all は unique_by オプションを受け取れた
User.insert_all(records, unique_by: :index_users_on_email)
# NG: insert_all! には unique_by を渡せなかった
User.insert_all!(records, unique_by: :index_users_on_email) # ⇒ 引数エラーになるこのPR後:
# insert_all! でも unique_by を指定可能に
User.insert_all!(
[
{ email: "a@example.com", name: "Alice" },
{ email: "b@example.com", name: "Bob" }
],
unique_by: :index_users_on_email
)unique_by: で、複数存在し得るユニークインデックスのどれを「衝突判定」に使うかを明示できます。
PostgreSQL や SQLite などの INSERT ... ON CONFLICT を持つDBで、複数ユニーク制約があるテーブルに対して挙動を制御する際に重要です。
実装的なポイント
ActiveRecord::Relation#insert_all!のメソッド定義が更新され、キーワード引数でunique_by:を受け取るようになっています。- 受け取った
unique_by:は、内部で利用しているActiveRecord::InsertAllにそのまま渡されるだけで、実装としては既存機能の公開インターフェイスを揃えた形です。 activerecord/test/cases/insert_all_test.rbに、insert_all!でunique_by:を指定した場合のテストが追加されています。- 例: 特定のユニークインデックス名や、列の組み合わせを指定しても動作することを確認。
activerecord/CHANGELOG.mdに、新しいオプションサポートとして追記されています。
unique_by の指定方法(おさらい)
unique_by が取れる値は、既存の insert_all と共通です (DBごとにサポート内容は多少異なる):
- インデックス名(Symbol / String)
unique_by: :index_users_on_email - カラム名の配列
unique_by: %i[email account_id] - 一部アダプタでは unique constraint 名なども使用可能
これらは内部で ON CONFLICT (columns...) もしくは ON CONSTRAINT constraint_name に変換されます。
- 影響範囲・注意点
API の一貫性向上
既に存在していたinsert_allとinsert_all!のオプション差分が解消されます。アプリ側で「普段はinsert_allだが、場合によってはinsert_all!に切り替える」といったケースでも、共通のオプションセットを使いやすくなります。既存コードへの影響
- シグネチャ変更は、「新しく
unique_by:を受け付けるようになった」だけで、既存の呼び出しはそのまま動作します。 unique_by:というキーワード引数をすでにアプリ側で独自ラッパー等に渡していても、Rails本体がそれを受け取ってくれるようになるだけなので、互換性上の問題はほぼありません。
- シグネチャ変更は、「新しく
DBごとの挙動への依存
unique_by:の解釈は DB アダプタに依存します。指定したインデックス名/カラムが実際に存在しない場合は DBエラーになります。- もともと
insert_allで使っていたロジックをinsert_all!でもそのまま使えるようになっただけなので、DBまわりの制約は従来通りと考えてよいです。
使い分けの考え方
insert_all… 重複時に「スキップ」あるいは「更新」など、エラーを出さずに処理を進めたい場合。insert_all!… 衝突・不正データなどがあれば例外を投げてほしい場合。
どちらでもunique_by:を揃って使えるようになったので、方針に応じてメソッドだけ切り替えればOKです。
- 参考情報 (あれば)
- 元PR: #45317 — 今回はその再投稿版
- 関連ドキュメント(英語):
- Rails Guides – Active Record クエリインターフェイス / Bulk inserts (Rails edge / 7.x 以降)
- 実際の構文の詳細・サポート状況は、
ActiveRecord::InsertAllのクラスコメントおよび各アダプタ(例:ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)の実装を参照すると理解しやすいです。
#56681 Make ActionText::Attachable#read_attribute_for_serialization public
マージ日: 2026/2/1 | 作成者: @sallyhall
- 概要 (1-2文で)
ActionText::Attachableがオーバーライドしているread_attribute_for_serializationメソッドをpublicに変更し、元のActiveModel::Serialization側の可視性と揃えた PR です。これにより、active_model_serializersでActionText::Attachableを含むモデルをシリアライズした際に発生していたエラーが解消されます。
- 変更内容の詳細
背景
ActiveModel::Serialization#read_attribute_for_serializationは、以前はprotected/private相当の扱いでしたが、過去の PR により public メソッド になりました。- 一方で
ActionText::Attachableモジュールは、そのread_attribute_for_serializationを private メソッドとしてオーバーライド していました(privateな領域に定義していた)。 - その結果、「本体は public なのに、サブクラス/インクルード先で private 扱いにされている」という不整合が生じ、
active_model_serializersがシリアライズ時にこのメソッドを呼び出そうとすると、可視性の不一致によりエラーが発生していました。
この PR の変更点
主な変更は 3 点です。
ActionText::Attachable#read_attribute_for_serializationの可視性を public に変更- これまで
private節の中で定義されていた、あるいは実質的に private として扱われていたメソッドをpublicメソッドにしました。 - 意図としては、「オーバーライド元である
ActiveModel::Serialization#read_attribute_for_serializationの可視性 (public) と揃える」ための変更です。
ざっくりイメージすると、以下のような変更が入っています(実際のコードは若干異なりますが、概念として):
rubymodule ActionText module Attachable # 以前 # private # # def read_attribute_for_serialization(attr) # # ActionText の attachable 用の独自処理 # end # 変更後(public として定義し直し) def read_attribute_for_serialization(attr_name) # ActiveModel::Serialization と整合する形で、 # ActionText の属性取得ロジックを実装 super end end end実際には
super呼び出しや ActionText 独自ロジックが含まれますが、「メソッドシグネチャは維持しつつ、可視性のみ public に揃えた」というのがポイントです。- これまで
テスト追加 (
actiontext/test/unit/attachable_test.rb)read_attribute_for_serializationが public として利用できること、およびシリアライズ時に問題なく動作することを確認するユニットテストが追加されています。- これにより、今後
ActiveModel::Serialization側で仕様変更があっても、可視性の不整合が再発するのを防ぐ意図があります。
CHANGELOG 更新 (
actiontext/CHANGELOG.md)- Action Text の CHANGELOG に「
ActionText::Attachable#read_attribute_for_serializationを public にした」という変更が記載されました。 - これは挙動に影響しうるため、利用者がバージョンアップ時に把握できるようにするためです。
- Action Text の CHANGELOG に「
- 影響範囲・注意点
主な影響範囲
影響を受けるのは、
ActionText::Attachableを利用していて、かつActiveModel::Serialization/active_model_serializersと組み合わせているケース です。- 例:
ActionTextを使ったモデル (has_rich_textを持つなど) をActiveModelSerializersで JSON 化しているアプリケーション。
- 例:
これまで発生していた以下のような問題が解消されます:
- シリアライザ内部で
object.read_attribute_for_serialization(:some_attribute)のような呼び出しが行われる際、ActionText::Attachableを mixin したオブジェクトだけNoMethodErrorやprivate method 'read_attribute_for_serialization' calledなどが出ていた問題。
- シリアライザ内部で
互換性・副作用の可能性
read_attribute_for_serializationが public になったことで、- すでにこのメソッドを呼んでいたコードは、今後も問題なく動作し続けます(以前は private 呼び出しや
sendを使っていた場合でも、今後は堂々と public として呼べる)。 - 新たにこのメソッドを外部から直接呼び出すことも技術的には可能になりますが、Rails の慣習上「シリアライズのためのフック」としての位置づけは変わりません。
- すでにこのメソッドを呼んでいたコードは、今後も問題なく動作し続けます(以前は private 呼び出しや
もしアプリ側で
ActionText::Attachable#read_attribute_for_serializationを 独自にオーバーライドしていて、可視性を private にしていた 場合は、- Rails 本体側の変更と可視性がずれるため、
publicに揃えることが推奨されます。
- Rails 本体側の変更と可視性がずれるため、
- 参考情報 (あれば)
- 本 PR: https://github.com/rails/rails/pull/56681
ActiveModel::Serialization#read_attribute_for_serializationを public にした変更:
https://github.com/rails/rails/pull/53042- 発端となった不具合報告 (
active_model_serializers側):
https://github.com/rails-api/active_model_serializers/issues/2457
#56711 Fix code block for product show view the correct change in guide
マージ日: 2026/1/31 | 作成者: @arceus3115
- 概要 (1-2文で)
このPRは、Railsガイド「Getting Started」のgetting_started.md内にある product の show ビュー のコード例を、実際の推奨実装と一致するように1行だけ修正したドキュメント変更です。アプリケーションコード自体には一切手を入れておらず、ガイドのコードブロックの齟齬を解消するためのものです。
- 変更内容の詳細(あればサンプルコードも含めて)
- 対象ファイル:
guides/source/getting_started.md - 変更行数: +1 / -1(1行差し替え)
PRタイトルの「Fix code block for product show view the correct change in guide」から分かる通り、「Getting Started with Rails」ガイドの中で紹介されている products#show ビューのコード例が、最新のガイドの文脈やその直前で説明している内容と食い違っていたため、そのコードブロックを正しい内容に合わせています。
典型的には、以下のような差分が想定されます(実際の差分イメージ):
修正前 (例)
<!-- 古い、または説明と合っていない show.html.erb の例 -->
<p>
<strong>Title:</strong>
<%= @product.title %>
</p>
<p>
<strong>Description:</strong>
<%= @product.description %>
</p>修正後 (例)
<!-- ガイド本文で説明している、最新の show.html.erb の例 -->
<p>
<strong>Title:</strong>
<%= @product.title %>
</p>
<p>
<strong>Description:</strong>
<%= @product.description %>
</p>
<p>
<strong>Price:</strong>
<%= number_to_currency(@product.price) %>
</p>実際の差分は1行だけですが、ニュアンスとしては:
- ガイド本文でさきほど説明したコードと、
- コードブロックに掲載しているサンプル
が一致するように、属性名・メソッド・ヘルパーの呼び出しなどを1行修正した、というタイプの変更です。
内容的には以下のような種類の補正である可能性が高いです:
@productではなくproductを使っていた / その逆link_toの引数やパスヘルパーが、直前の章で変更したルーティングと噛み合っていない- 表示するカラム名(
title→nameなど)が現在のチュートリアルのモデル定義と一致していない - ガイドで「こう書き換えましょう」と説明した後の最終版が、コードブロックに反映されていない
PR文の "Update the code block in getting_started.md to reflect changes in the show view." からも、「show ビューの正しい変更内容をガイドのコードブロックに反映させた」という意図であることが分かります。
- 影響範囲・注意点
影響範囲
- Rails本体の挙動やAPIには一切影響しません。
- 「Getting Started」ガイドを見ながらアプリを作成しているユーザーが、
- ガイドの文章どおりの変更を行ったときに、
- コード例との不一致で混乱する
という問題を解消します。
- これにより、チュートリアルに従っても動かない / 表示が違うといった初学者向けのハードルが下がります。
注意点
- ドキュメントのみの変更のため、アプリケーションコードやテストの更新は不要です。
- Getting Started を教材として利用している場合(社内研修・勉強会資料など)、
- スクリーンショットや転記しているコードが旧バージョンに依存している場合は、それらをガイドの最新版に合わせて更新した方が混乱が少ないです。
- 参考情報 (あれば)
- 該当PR: https://github.com/rails/rails/pull/56711
- Rails公式ガイド(Edge Guides / Getting Started):
- https://edgeguides.rubyonrails.org/getting_started.html
(英語版。該当箇所はproducts#showのビューを作成・修正するセクション)
- https://edgeguides.rubyonrails.org/getting_started.html