`DestroyAssociationAsyncJob#perform` に `**options` 引数を追加して拡張性を確保
DestroyAssociationAsyncJob#perform メソッドに **_options を追加することで、既存アプリケーションを壊さずにジョブを拡張できるようになりました。
背景
dependent: :destroy_async は、関連レコードを非同期で削除するための仕組みです。この機能は DestroyAssociationAsyncJob を通じて実行されますが、メソッドシグネチャが固定されていたため、引数を追加するたびに後方互換性の問題が生じる懸念がありました。
関連する #42452 では、アソシエーションの置き換え時(parent.has_many = [])にも dependent: :destroy_async が適用されるよう修正が検討されていました。このPRのレビュー中(コメント)で、perform メソッドのシグネチャ拡張が将来のアプリケーションに影響を与えないようにするための仕組みが必要と指摘されました。また #42430 では、has_many アソシエーションにおける dependent: :destroy_async の挙動の非一貫性が問題として報告されており、修正に向けて perform メソッドへの引数追加が避けられない状況でした。
この背景から、将来の引数追加に備えたキーワードスプラット(**_options)の導入が本PRで先行して行われています。
技術的な変更
DestroyAssociationAsyncJob#perform のシグネチャに **_options が追加されました。変更は1行のみと最小限です。
変更前:
def perform(
owner_model_name: nil, owner_id: nil,
association_class: nil, association_ids: nil, association_primary_key_column: nil,
ensuring_owner_was_method: nil
)
変更後:
def perform(
owner_model_name: nil, owner_id: nil,
association_class: nil, association_ids: nil, association_primary_key_column: nil,
ensuring_owner_was_method: nil, **_options
)
**_options はアンダースコアプレフィックスで命名されており、メソッド本体では使用されない引数であることを明示しています。これにより、Rails側が将来 perform に新しいキーワード引数を渡すようになっても、既存のサブクラスや独自実装のジョブは ArgumentError を発生させることなく動作し続けます。
設計判断
キーワードスプラット(`_options`)による吸収** という手法が採用されました。
新しいキーワード引数を個別に定義するのではなく、未知のキーワード引数をまとめて受け取るスプラットを置くことで、将来の引数追加を一括して吸収します。_options と命名することで「意図的に無視するための引数」であることをコードレベルで伝えており、サブクラスでオーバーライドした場合でも同じパターンを引き継げます。引数追加のたびにシグネチャ変更の影響範囲を考慮する必要がなくなるため、dependent: :destroy_async の機能拡張が継続的に行いやすくなります。
まとめ
1行の変更ですが、DestroyAssociationAsyncJob#perform への **_options 追加は、関連する機能改善(#42452、#42430)を後方互換性を保ちながら実現するための基盤です。シグネチャの拡張性を先行して確保することで、今後の引数追加がアプリケーションのアップグレードコストにならないよう設計されています。