ActiveModelオブジェクトに追加ローカル変数を渡すとドロップされる不具合を修正
v2.15.0で発生したリグレッションを修正し、json.partial! @post, highlighted: true のようにActiveModelオブジェクトと追加ローカル変数を組み合わせた呼び出しが正しく動作するようになりました。
背景
v2.15.0で partial! メソッドに渡した追加ローカル変数が、第一引数がActiveModelオブジェクトの場合にサイレントに無視されるリグレッションが発生しました。これは #616 として報告されています。
問題の原因は、v2.15.0で取り込まれたコミット 44b42f9(Remove call to one?)にあります。このコミットは args.one? のチェックを削除し、引数の数に関係なくActiveModelの分岐に入るよう変更しました。その結果、json.partial! @post, highlighted: true のような呼び出しは第二引数以降(追加ローカル変数)を無視したまま _render_active_model_partial args.first へと進むようになりました。
_render_active_model_partial は内部で @context.render object, json: self を呼ぶだけであり、追加ローカル変数を受け取る引数を持たないため、パーシャル内で highlighted を参照すると undefined local variable or method 'highlighted' エラーが発生します。
技術的な変更
修正はコミットのリバートとして、args.one? チェックを復元する1行の変更で構成されています。
変更前(v2.15.0):
def partial!(*args)
if _is_active_model?(args.first)
_render_active_model_partial args.first
else
options = args.extract_options!.dup
変更後(本PR):
def partial!(*args)
if args.one? && _is_active_model?(args.first)
_render_active_model_partial args.first
else
options = args.extract_options!.dup
args.one? を条件に加えることで、ActiveModelの高速パスはオブジェクト単体の場合にのみ適用されます。json.partial! @post, highlighted: true のように追加ローカル変数が存在する場合は args.one? が false となり、else ブランチの汎用処理(extract_options! によるオプション展開)へフォールスルーします。
テストにも変更が加えられています。既存の RACER_PARTIAL に json.highlighted local_assigns.fetch(:highlighted, false) が追加され、追加ローカル変数を受け取るパーシャル定義となりました。さらに、以下のテストケースが追加されています:
test "partial for Active Model preserves extra locals" do
result = render('json.partial! @racer, highlighted: true', racer: Racer.new(123, "Chris Harris"))
assert_equal 123, result["id"]
assert_equal "Chris Harris", result["name"]
assert_equal true, result["highlighted"]
end
このテストにより、ActiveModelオブジェクトと追加ローカル変数の組み合わせが今後リグレッションしないことが保証されます。
設計判断
シンプルなリバートが採用されました。 削除された args.one? チェックを元に戻すだけという最小限の変更です。
ActiveModelの高速パス(_render_active_model_partial)は、追加ローカル変数を扱う仕組みを持っていません。引数が1つだけのときに限定することで、追加ローカル変数がある場合には既存の汎用パスへ委譲する設計が維持されています。汎用パスは extract_options! でオプションHashを取り出し、パーシャルのレンダリングに渡すため、追加ローカル変数を正しく処理できます。
まとめ
本PRは1行の条件式の復元により、v2.15.0のリグレッションを解消しました。ActiveModelオブジェクトへの高速パスは「引数が1つのみの場合」という当初の意図通りの制約に戻り、追加ローカル変数を持つ呼び出しは汎用パスで正しく処理されるようになっています。