`render?` が false のとき `render_in` が html_safe な文字列を返すよう修正
render? が false を返すコンポーネントを他の SafeBuffer 文字列と連結すると TypeError が発生していた問題が修正されました。render_in の非レンダリングパスが "".html_safe を返すようになり、レンダリングパスとの一貫性が保たれます。
背景
render_in の正常パスと非レンダリングパスで戻り値の型が一致していないことが問題の根本原因でした。Railsのビュー層では、render などのヘルパーが返す文字列は ActiveSupport::SafeBuffer(html_safe な文字列)であることが前提とされています。render_in の正常パス(コンポーネントが実際にレンダリングされる場合)はこの前提を満たしていましたが、render? が false を返すパスは通常の String を返していました。
その結果、render(MyComponent.new) + some_helper_returning_html のようにコンポーネントの出力と他のヘルパーの出力を連結するコードで TypeError が発生していました。SafeBuffer への + 演算は相手が html_safe でない文字列だとエスケープ処理を挟むため、内部で型の不整合が生じます。
技術的な変更
変更箇所は lib/view_component/base.rb の render_in メソッド内、render? が false のときの分岐の1行のみです。
変更前:
else
""
end
変更後:
else
"".html_safe
end
あわせて test/sandbox/test/rendering_test.rb に、render? が false のコンポーネントをレンダリングしたとき @rendered_content が html_safe? であることを確認するテストが追加されています。
def test_render_in_returns_html_safe_string_when_render_is_false
render_inline(ConditionalRenderComponent.new(should_render: false))
assert_predicate @rendered_content, :html_safe?
end
テストでは既存の ConditionalRenderComponent を再利用しており、新たなテスト用コンポーネントの追加は不要です。
設計判断
戻り値の型を常に SafeBuffer に統一するという方針が採られています。render_in の利用者はレンダリングの成否にかかわらず html_safe な文字列を受け取れるという契約が明示されたことになります。
空文字列に .html_safe を付与するだけの最小限の修正にとどめている点も注目できます。render? が false のケースで返す値の意味(「何も出力しない」)は変わらず、表現の型だけを正規化しています。これにより、呼び出し側が戻り値に対して html_safe? チェックや条件分岐を追加する必要がなくなります。
まとめ
1行の修正ですが、render_in の全パスで戻り値の型契約が統一されたことで、コンポーネントの出力をビューヘルパーの結果と自由に連結できるようになります。render? による条件付きレンダリングを多用するコードベースで潜在的に発生していた TypeError が根本から解消されます。