MySQLアダプタの `NATIVE_DATABASE_TYPES` 定数のフリーズを無効化
#57323 で有効化された Style/MutableConstant Cop が NATIVE_DATABASE_TYPES をフリーズしたことで、この定数を拡張しているサードパーティgemが実行時エラーを起こすリグレッションが発生しました。本PRはその修正として、MySQL アダプタの NATIVE_DATABASE_TYPES に対して rubocop:disable コメントを付与してフリーズを取り除きます。
背景
#57323 は Ractor 安全性向上を目的として Style/MutableConstant Cop をリテラル定数に対して有効化した変更です。この Cop はミュータブルなリテラル(HashやArray)を定数に代入する際に .freeze を付加し、複数の Ractor 間で安全に共有できるようにします。
ただし NATIVE_DATABASE_TYPES は、フレームワーク利用者が型マッピングを拡張するために意図的にミュータブルなまま公開されている定数です。neighbor gem はMySQL向けにベクトル型を追加するため、起動時に Neighbor::MySQL.initialize! 内でこの定数を直接書き換えています。フリーズされた状態でこのコードが実行されると、以下のような FrozenError が発生してアプリケーションが起動不能になります。
can't modify frozen Hash: {...} (FrozenError)
from neighbor-1.0.0/lib/neighbor/mysql.rb:9:in 'Neighbor::MySQL.initialize!'
このパターンはRailsが意図的に許容してきたものであり、PostgreSQLアダプタでも同様に NATIVE_DATABASE_TYPES はフリーズされていません。
技術的な変更
abstract_mysql_adapter.rb の NATIVE_DATABASE_TYPES から .freeze を取り除き、代わりに # rubocop:disable Style/MutableConstant コメントを付与しました。
変更前:
NATIVE_DATABASE_TYPES = {
primary_key: "bigint auto_increment PRIMARY KEY",
string: { name: "varchar", limit: 255 },
# ...
json: { name: "json" },
}.freeze
変更後:
NATIVE_DATABASE_TYPES = { # rubocop:disable Style/MutableConstant
primary_key: "bigint auto_increment PRIMARY KEY",
string: { name: "varchar", limit: 255 },
# ...
json: { name: "json" },
}
変更はハッシュ定義の冒頭に rubocop:disable コメントを追加し、末尾の .freeze を削除する2行のみです。
設計判断
rubocop:disable コメントによる明示的な意図の記録 が選択されました。単に .freeze を削除するだけでなく、この定数がミュータブルで公開されていることをコードレベルで明示します。これはPostgreSQLアダプタの NATIVE_DATABASE_TYPES が採用している同一のアプローチと一貫しています。
Style/MutableConstant Cop を literals スタイルで有効化した #57323 の動機はRactor安全性の向上でした。しかし NATIVE_DATABASE_TYPES のようなフレームワーク拡張ポイントは、Ractor間共有よりも拡張性の確保が優先されるケースであり、Cop の適用除外が妥当な判断です。この disable コメントは、将来のコントリビュータに対して「このフリーズの欠如は意図的なものである」というシグナルを明示的に伝える役割を担います。
まとめ
この修正は Style/MutableConstant Cop の一括適用が引き起こしたリグレッションへの対処であり、「Ractor安全性のための定数フリーズ」と「フレームワーク拡張ポイントの維持」というトレードオフを明確に解決しています。rubocop:disable コメントを活用することで、機械的なルール適用と設計上の意図を共存させる手法がRails内で一貫して採用されていることが確認できます。