`Style/MutableConstant` cop の有効化による Ractor 安全性への前進

rails/rails

Rails コードベース全体で、リテラル定数への .freeze 付与を強制する Style/MutableConstant RuboCop cop が有効化されました。これは Rails の Ractor 安全化に向けた取り組みの一環であり、フレームワーク起動後に変更されない静的定数を不変オブジェクトとして明示します。

背景

この変更は、Rails を Ractor 対応にするための継続的な取り組みの一部として行われました。Ractor 間で共有されるクラスやモジュールは、そのすべての定数も共有可能(shareable)である必要があります。定義時に .freeze を付与することで、定数をイミュータブルオブジェクトとして宣言でき、Ractor 安全性の要件を満たせます。

これまで Rails コードベースには、配列・ハッシュ・文字列リテラルで定義された定数に .freeze が付いていないケースが多数存在していました。これらはフレームワークのライフタイムを通じて事実上変更されない静的な値ですが、Rubyのオブジェクトとしては可変(mutable)な状態でした。

技術的な変更

.rubocop.ymlStyle/MutableConstant cop が追加され、EnforcedStyle: literals で有効化されました。この設定により、リテラル値(Array・Hash・String 等)として定義された定数に .freeze が付いていない場合に警告が出るようになります。

変更前:

Style/FrozenStringLiteralComment:
  ...

Style/MapToHash:
  Enabled: true

変更後:

Style/FrozenStringLiteralComment:
  ...

Style/MutableConstant:
  Enabled: true
  EnforcedStyle: literals
  Exclude:
    - '**/*.md'

Style/MapToHash:
  Enabled: true

コードベース全体では、Array・Hash・String リテラルで定義された多数の定数に .freeze が追加されました。代表的な変更例を示します。

配列リテラルの例(action_dispatch/http/request.rb):

# 変更前
RFC2616 = %w(OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT)
RFC2518 = %w(PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK)

# 変更後
RFC2616 = %w(OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT).freeze
RFC2518 = %w(PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK).freeze

ハッシュリテラルの例(action_cable/lib/action_cable.rb):

# 変更前
  protocols: ["actioncable-v1-json", "actioncable-unsupported"].freeze
}

# 変更後
  protocols: ["actioncable-v1-json", "actioncable-unsupported"].freeze
}.freeze

文字列リテラルの例(action_dispatch/http/mime_type.rb):

# 変更前
MIME_NAME = "[a-zA-Z0-9][a-zA-Z0-9#{Regexp.escape('!#$&-^_.+')}]{0,126}"
MIME_PARAMETER_VALUE = "(?:#{MIME_NAME}|\"[^\"\r\\\\]*\")"
MIME_PARAMETER = "\s*;\s*#{MIME_NAME}(?:=#{MIME_PARAMETER_VALUE})?"

# 変更後
MIME_NAME = "[a-zA-Z0-9][a-zA-Z0-9#{Regexp.escape('!#$&-^_.+')}]{0,126}".freeze
MIME_PARAMETER_VALUE = "(?:#{MIME_NAME}|\"[^\"\r\\\\]*\")".freeze
MIME_PARAMETER = "\s*;\s*#{MIME_NAME}(?:=#{MIME_PARAMETER_VALUE})?".freeze

また、action_dispatch/http/request.rb には require "active_support/core_ext/enumerable" の追加も含まれています。

一方、キャッシュや外部からの拡張を意図した定数については、.freeze を付与せず # rubocop:disable Style/MutableConstant コメントで明示的に除外しています。該当する定数は以下のとおりです:

  • Mime::EXTENSION_LOOKUP / Mime::LOOKUP(MIMEタイプの登録テーブル)
  • ActionDispatch::Journey::Path::Pattern::REGEXP_CACHE(正規表現キャッシュ)
  • ActionDispatch::Journey::Visitors::Visitor::DISPATCH_CACHE / FunctionalVisitor::DISPATCH_CACHE(ディスパッチキャッシュ)
  • ActionDispatch::Http::Request::HTTP_METHOD_LOOKUP(HTTPメソッドルックアップキャッシュ)

設計判断

EnforcedStyle: literals が採用された点が重要です。literals スタイルは、Array・Hash・String などのリテラル値で定義された定数のみを対象とします。メソッド呼び出しの結果や動的に構築される定数は対象外であるため、既存コードへの影響を最小限に抑えながら、静的な定数の不変性を保証できます。

変更不可能にすべきでない定数(キャッシュや外部拡張ポイント)については、修正を後回しにして rubocop:disable コメントで明示しています。これにより、「この定数はミュータブルであることが意図的である」という設計意図がコード上に残ります。一括適用と段階的修正を組み合わせたアプローチは、大規模コードベースでの静的解析導入の実践例としても参考になります。

また、actionpack/lib/action_controller/strong_parameters.rbEMPTY_ARRAYEMPTY_HASH が freeze されたことは特筆に値します。これらは名前のとおり空の配列・ハッシュであり、フリーズすることでオブジェクトの再利用時の安全性が高まります。

まとめ

このPRは、RuboCop の Style/MutableConstant cop を literals スタイルで有効化し、Rails 全体の静的定数に .freeze を付与することで、Ractor 安全化の基盤を整備しました。ミュータブルであることが意図的な定数を rubocop:disable で明示的に区別する方針は、コードの設計意図の可視化にも貢献しています。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
2bc3a7bb

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

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

リード文(総論)→セクション群(各論)→まとめ(結論)の3部構成が明確で、読者が内容を理解しやすい構成になっています。「背景」「技術的な変更」「設計判断」のセクション分けも論理的です。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きシンタックスハイライト(```yaml:.rubocop.yml)やGitHubのPRへのリンク記法([PR #57323](URL))が正しく使用されており、技術記事としての体裁が整っています。

対象読者への適合性 ✓ PASS

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

RactorやRuboCopに関する専門用語を適切に使用しており、Rails開発者という対象読者にふさわしい技術レベルで記述されています。初心者向けの過度な説明はありません。

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

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

各セクションが総論→各論の構成になっており、各パラグラフもトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られています。これにより、記事の要点を素早く把握できます。

Diff内容との照合 ✓ PASS

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

`.rubocop.yml`の変更や各ファイルの定数への`.freeze`付与など、Diffの内容を正確に引用・解説できています。`# rubocop:disable`で除外された定数のリストも正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`Ractor-safe`, `shareable`, `immutable`, `EnforcedStyle: literals`といった技術用語を、PRの文脈に沿って正確に使用しています。

説明の技術的正確性 ✓ PASS

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

Ractor安全性のために定数のfreezeが必要であるという変更の技術的な理由や、`EnforcedStyle: literals`という設定がもたらす影響についての説明が正確かつ論理的です。

事実の突合 ✓ PASS

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

記事内のすべての主張は、PRのタイトル、説明、そしてDiff内のコード変更に明確に裏付けられており、ハルシネーション(捏造)は見られません。

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

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

PR番号(#57323)が正確に記載されています。

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

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

記事のタイトルはPRの主題「`Style/MutableConstant` copの有効化」とその目的「Ractor安全性への前進」を的確に要約しており、内容と完全に一致しています。

外部知識の正確性 ✓ PASS

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

記事の内容は提供されたPR情報に完全に準拠しており、バージョン情報やリリース日程など、PR外の知識を不適切に追加している箇所はありません。

時間表現の正確性 ✓ PASS

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

「有効化されました」といった過去形・完了形の表現が適切に使われており、事実を報告する記事として時間表現に誤りはありません。