https://github.com/rails/rails
`rails query`コマンドに存在した2つのバグを修正。`eval`呼び出しを`with_readonly_connection`で包むことでLLMによる破壊的なActiveRecord式の実行を防ぎ、`ActiveRecord::Base`インスタンスを返す式での出力がオブジェクトのinspect文字列になる問題も解消された。
`in_order_of` メソッドが配列値を受け付けるようになり、複数の値を同一順位グループとして扱えるようになりました。型キャスト・`CASE`式生成・`WHERE`句の3箇所に `is_a?(Array)` 判定を追加する最小限の変更で、既存APIとの後方互換性を維持しながらグループ化ソートを実現しています。
トランザクションなしのテストが混在する環境でフィクスチャキャッシュが無効化されるたびにYAMLを再パースしていた問題を解消するため、FixtureSetにクラス変数ベースのパースキャッシュを導入しました。freeze: trueとrow.dupの組み合わせでキャッシュの安全性を担保しつつ、意図的なキャッシュ無効化のためのwithout_parsing_cacheメソッドも提供されています。
Active Recordのフィクスチャ読み込みで使用するglobパターン `/{**,*}/*.yml` を `/**/*.yml` に簡素化することで、約50%の高速化を達成しました。`**` がゼロ個以上のディレクトリセグメントにマッチするため `*` の指定は冗長であり、1文字の変更でパフォーマンスと可読性を同時に改善しています。
`Date::DATE_FORMATS` と `Time::DATE_FORMATS` の非推奨化メカニズムを改善したPRです。フォーマット定義の実体をコア拡張側から `ActiveSupport::DateFormats` / `ActiveSupport::TimeFormats` 側に移動することで依存関係を逆転させ、`ActiveSupport.deprecator` を使った標準的な警告と移行先API(`ActiveSupport::DateFormats.register`)の明示を実現しています。
PostgreSQLのパーティションテーブルで `ActiveRecord::FixtureSet.create_fixtures` を使用すると、外部キー制約が `NOT VALID` 状態のまま残留するバグを修正。`UPDATE pg_catalog.pg_constraint` 文に `conrelid::regclass` によるテーブルスコープを追加し、同名の制約を持つ親テーブルと全パーティションが一括で無効化される問題を解消した。
`ActiveSupport::TimeFormats` と `ActiveSupport::DateFormats` モジュールが導入され、カスタム日付フォーマットの登録が `Time::DATE_FORMATS` / `Date::DATE_FORMATS` へのグローバルな直接書き込みから `register` メソッドによる名前空間スコープの登録へと刷新されました。旧定数は非推奨化されフォールバックによる後方互換性が維持されており、次バージョンでの削除に向けた段階的な移行パスが提供されています。
Railsの開発環境で `reload_routes!` を繰り返し呼び出すたびに内部ウェルカムルート(`GET /`)が重複登録される問題を修正。`RoutesReloader` に「一度だけ実行するコールバック」のセマンティクスを持つ `run_once_after_load_paths` を導入し、アプリケーション起動時のみウェルカムルートを登録するよう変更しました。
`ActionController::Parameters`に`deep_transform_values`と`deep_transform_values!`が追加されました。これまでネストされたパラメータの全値を変換するには`to_unsafe_h`でStrong Parametersを抜け出す必要がありましたが、新メソッドを使うことで`permitted?`ステータスを保持したまま変換できるようになります。既存の`deep_transform_keys`と対称なインターフェースを持ち、ホワイトスペース除去などの日常的な正規化処理をStrong Parametersの枠組みの中で完結させられます。
ActiveModel::Type::Integer の cast_value メソッドが拡張され、to_i を呼ぶ前に文字列を _limit * 4 バイトに切り詰めるようになりました。デフォルト4バイト整数では16バイト、bigintでは32バイトが上限となり、巨大な文字列を整数キャストさせるDoS攻撃を緩和します。スラグ形式("123-hello-world" など)との互換性を保つため、カラム幅の4倍という係数が採用されています。
Active Recordの `add_foreign_key` と `change_foreign_key` に `enforced:` オプションが追加され、PostgreSQL 18.4以降で外部キー制約の参照整合性チェックをスーパーユーザー権限なしに制御できるようになりました。`enforced: false` を指定すると `NOT ENFORCED` 制約が生成され、スキーマダンプにも `enforced: false` と `validate: false` の両方が出力されます。
`NumericalityValidator`の`:in`オプションがProcとSymbolを受け付けるようになりました。これにより、他の属性値やレコードの状態に依存した動的な数値範囲バリデーションを、カスタムバリデータなしで宣言的に記述できます。型チェックは起動時と実行時の2段階で行われ、誤設定を適切なタイミングで検出します。
`http_cache_forever` ヘルパーに `last_modified:` キーワード引数が追加されました。デフォルトは従来通り2011年1月1日のままですが、リソース固有の日時を渡せるようになります。Active Storage の `ProxyController` ではこの新オプションを使い、`Last-Modified` ヘッダーが `Blob#created_at` に基づいて設定されるようになりました。
`ActiveSupport::SafeBuffer` の引数転送を `*args, &block` から `...` や匿名 `*`・`**` へ置き換え、中間配列オブジェクトの生成を抑制するアロケーション削減を行いました。ブロック内処理の有無など各メソッドの特性に応じて3種類の匿名構文を使い分けています。
`number_to_phone`で区切り文字と同じ文字列から始まる電話番号を渡すと先頭の桁が欠落するバグを修正しました。文字列置換後に`start_with_delimiter?`で事後判定していた設計を廃止し、`gsub`ブロック内でキャプチャグループ`$1`が空かどうかを直接評価する方式に変更することで、誤検知の根本原因を解消しています。
Ruby 3.4の `strict_unused_block` 警告カテゴリへの対応として、`ReflectionProxy#all_includes` に匿名ブロック転送構文 `&` を追加しました。動作変更はなく、ブロックパラメータを宣言することで未使用ブロックの警告を解消しています。
STIで`becomes`メソッドを使ってレコードを別サブクラスに変換した際、未保存のActiveStorage添付ファイル変更が失われるバグ(#45778)が修正されました。`CreateOne`など4つのChangeクラスの`record`属性を`attr_accessor`に変更し、`model.rb`に`becomes`のオーバーライドを追加することで、変換後のインスタンスへ`attachment_changes`を引き継ぐようになりました。
`ActiveRecord::TestCase#check_connection_leaks` が `ConnectionPool::Reaper` のメンテナンス処理と競合し、正常な接続をリーク誤検知していた問題を修正。リーク走査全体を `pool.reaper_lock` ブロックで囲むことで teardown が Reaper のメンテナンス完了を待機するようにし、競合条件を排除しました。テストインフラのみの変更で、プロダクションコードへの影響はありません。
`number_to_phone`で複数文字の`:delimiter`を指定したとき、7桁以下の電話番号の先頭に不正な断片が残るバグを修正。`convert_without_area_code`内の `slice!(0, 1)` を `slice!(0, delimiter.length)` に変更する1行修正で、マルチバイト文字のデリミタも含めて正しく動作するようになりました。このバグは2011年のPR #3314 で導入されて以来、テストスイートが単一文字デリミタのみを対象にしていたため13年間気づかれずにいました。
`insert_all` のシリアライズカラム最適化(PR #56855)において、文字列値が `cast` をスキップして二重エンコードされるバグを修正。`type.serialized? && !value.is_a?(String)` という条件式の1行変更により、Hash/Array のホットパスを維持しつつ文字列のみ `create` と同じ `cast` フルパスを経由するよう修正しています。