ミュータブルな定数の拡張性をテストで保証する

rails/rails

#57323 で導入された定数のフリーズ対応により、フレームワーク利用者が拡張することを想定した定数まで誤って凍結されるケースが発生していました。本PRはその再発防止として、拡張可能であるべき定数に対してテストを追加します。

背景

Ractorセーフ化を目的とした Style/MutableConstant copの有効化(#57323)は、リテラルで定義された定数を一括でフリーズするものでした。しかしこの変更の直後、フレームワーク利用者による拡張を想定した定数まで凍結されてしまう問題が2件報告されています。

具体的には、#57332Date::DATE_FORMATS の意図しないフリーズを修正し、#57333AbstractMysqlAdapter::NATIVE_DATABASE_TYPES のフリーズを修正しています。この定数はフレームワーク利用者がカスタム型を登録するために変更することが想定されており、フリーズにより FrozenError が発生していました。これらの修正はいずれも、定数が拡張可能であることを保証するテストを持たずに行われていました。

このような「ドキュメントや慣習上は拡張可能とされているが、テストで保証されていない定数」の存在が、フリーズ対応のリグレッションを引き起こした根本原因です。

技術的な変更

本PRは2つのテストファイルにそれぞれ1つずつテストメソッドを追加し、対象の定数が変更可能であることを実行時に検証します。

activesupport/test/core_ext/date_ext_test.rb には test_date_formats_can_be_extended が追加されました。カスタムフォーマットキー :custom_date_formatDate::DATE_FORMATS に登録し、to_fs が期待通りの文字列を返すことを検証します。

def test_date_formats_can_be_extended
  Date::DATE_FORMATS[:custom_date_format] = "%B %Y"

  assert_equal "February 2005", Date.new(2005, 2, 21).to_fs(:custom_date_format)
ensure
  Date::DATE_FORMATS.delete(:custom_date_format)
end

activerecord/test/cases/adapters/abstract_mysql_adapter/sql_types_test.rb には test_native_database_types_can_be_extended が追加されました。:custom_mysql_typeAbstractMysqlAdapter::NATIVE_DATABASE_TYPES に登録し、type_to_sql が正しいSQL型名を返すことを確認します。

def test_native_database_types_can_be_extended
  ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::NATIVE_DATABASE_TYPES[:custom_mysql_type] = { name: "mediumtext" }

  assert_equal "mediumtext", type_to_sql(:custom_mysql_type)
ensure
  ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::NATIVE_DATABASE_TYPES.delete(:custom_mysql_type)
end

どちらのテストも ensure ブロックで登録したキーを削除しており、テスト間の副作用を防ぐクリーンアップが徹底されています。

設計判断

「変更可能であることの保証」をテストとして明文化するアプローチが採用されました。

#57323 は拡張用途の定数に rubocop:disable コメントを付ける形で例外を管理していますが、コメントだけではそれが実際に機能していることの実行時保証にはなりません。本PRはテストを追加することで、将来の変更(誤ったフリーズ、構造変更など)がCIで即座に検出できる状態を作っています。ensure によるクリーンアップを含む設計は、テスト自体が他のテストに干渉しないことも保証しており、テストの独立性を維持しています。

まとめ

本PRは新機能の追加ではなく、既存の拡張ポイントを「壊せないようにする」ためのテストを加える変更です。定数のフリーズという広範な自動化の中で見落とされやすい「拡張のための可変性」をテストとして表明することで、今後の同種のリグレッションを未然に防ぐ安全網を構築しています。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
949f52cb

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
リトライ後承認
Review Count:
2回 (改善を経て承認)
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文(総論)→背景・技術的な変更・設計判断(各論)→まとめ(結論)という「総論→各論→結論」の構成が明確に適用されており、非常に分かりやすいです。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```ruby:path/to/file.rb)とGitHubのPR番号リンク記法([#123](URL))が、ガイドライン通りに正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

「Ractorセーフ化」「フリーズ」「リグレッション」などの専門用語を前提としており、対象読者である専門知識を持つエンジニアに適した技術レベルと表現で書かれています。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論の構成になっており、各段落がトピックセンテンスで始まるため、非常に読みやすいです。1段落1トピックの原則も守られています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内で引用されている2つのコードブロックは、提供されたDiff情報と完全に一致しています。ファイル名の指定も正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「Ractorセーフ化」「Style/MutableConstant cop」「FrozenError」「ensure」など、関連する技術用語が文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

定数のフリーズによって `FrozenError` が発生する仕組みや、テストコードが定数の拡張性を保証するという説明は、技術的に正確かつ論理的です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内の主張(過去のPR、問題の発生経緯、本PRの目的など)はすべてPRのDescriptionで裏付けられており、ハルシネーションは検出されませんでした。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#57323, #57332, #57333, #57341)や定数名、メソッド名などの固有名詞はすべて正確に記載されています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトル「ミュータブルな定数の拡張性をテストで保証する」は、PRのタイトル「Add tests for constants that are expected to be mutated」の内容を的確に要約しており、主題と完全に一致しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

記事内容はPR情報とDiffに記載されている事実に限定されており、サポート状況やリリース日程など、PRに記載のない外部知識の追加はありません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

PR Descriptionの「Since ... landed, there have been...」という過去の出来事の記述に対し、記事も「導入された」「発生していました」と過去形で表現しており、時間表現は正確です。