AbstractController::Helper.helper_method を Ractor セーフに
Ractor への対応が進む Rails では、ヘルパーメソッドの登録情報を Ractor 間で安全に共有できるように AbstractController::Helper.helper_method が修正されました。機能的な変化はなく、内部データ構造の不変化とテスト追加により安全性が保証されています。
背景
Ractor でのデータ共有は、オブジェクトが不変(freeze)であることが必須です。従来、helper_method が登録する _helper_methods 配列は可変オブジェクトであり、Ractor 間での共有に問題がありました。この制約は、Ractor の導入に伴い多数のコードで顕在化するリスクとなっていました。AbstractController::Helper の実装を見直すことで、Ractor 安全性を確保しつつ既存のヘルパー機能を維持する必要がありました。
技術的な変更
_helper_methods のデフォルト値を凍結配列に変更し、追加時にも不変オブジェクトを再代入します。class_attribute :_helper_methods, default: Array.new が default: Array.new.freeze に置き換えられ、空状態でも共有可能になりました。
class_attribute :_helper_methods, default: Array.new.freeze
helper_method の実装は、配列結合ではなく集合演算で新しい凍結配列を生成して代入します。元の self._helper_methods += methods から、self._helper_methods = (_helper_methods | methods).freeze へ変更され、結果として常に凍結された配列が保持されます。
def helper_method(*methods)
methods.flatten!
self._helper_methods = (_helper_methods | methods).freeze
location = caller_locations(1, 1).first
file, line = location.path, location.lineno
# …省略…
end
テストスイートに Ractor 共有性を検証するケースが追加されました。ActiveSupport::Testing::RactorsAssertions をインクルードし、assert_ractor_shareable で _helper_methods が Ractor で安全に共有できることを確認しています。
require "active_support/testing/ractors_assertions"
class HelperTest < ActiveSupport::TestCase
include ActiveSupport::Testing::RactorsAssertions
# …省略…
def test_helper_methods_are_ractor_safe
controller = Class.new(ActionController::Base) do
helper_method :foo
end
assert_ractor_shareable controller._helper_methods
end
end
設計判断
変更は既存インターフェースを保持しつつ、内部データを不変化させる最小限の差分で実装されています。_helper_methods を凍結配列とし、代入ロジックだけを更新したため、外部からの呼び出し方や動作は変わりません。代替案として新しい属性や設定キーを導入する検討は行われませんでした。
Ractor 安全性の確保は、Rails のコア設計における不変オブジェクト推奨と一致します。freeze を活用したこの実装は、他のコアコンポーネントでも採用されているパターンと同様で、コードベース全体の一貫性を保ちます。結果として、Ractor 利用時の潜在的な共有バグが事前に防がれます。
まとめ
AbstractController::Helper.helper_method の内部配列を凍結し、集合演算で更新することで、ヘルパーメソッド登録情報が Ractor で安全に共有できるようになりました。動作上の変更はなく、追加されたテストで安全性が検証されているため、開発者は従来通り helper_method を利用できます。