`method`リーダーを持つオブジェクトでのrendering不具合を修正
render_inのアリティチェックにKernel#methodを明示的に使うことで、methodという名前のアクセサを持つオブジェクトでもrenderingが正しく動作するようになりました。
背景
#50623でrenderableオブジェクトのrender_inシグネチャ拡張が導入された際、アリティチェックのために@renderable.method(:render_in)という呼び出しが実装されました。この実装は、methodがRubyの組み込みメソッド(Kernel#method)を指すことを前提としていましたが、その前提が崩れるケースが見落とされていました。
具体的には、attr_reader :methodのようにオブジェクト自身がmethodという名前のインスタンスメソッドを定義している場合、@renderable.method(:render_in)はKernel#methodではなくそのオブジェクト独自のmethodリーダーを呼び出してしまいます。結果として、render_inのアリティを正しく取得できずrenderingが失敗します。
技術的な変更
actionview/lib/action_view/template/renderable.rbにおいて、@renderable.method(:render_in)の呼び出しをKernel.instance_method(:method).bind_call経由に変更し、オブジェクトのメソッド解決に依存しない形でアリティを取得するようにしました。
変更前:
def render(context, locals)
if @renderable.method(:render_in).arity == 1
変更後:
def render(context, locals)
render_in_method = Kernel.instance_method(:method).bind_call(@renderable, :render_in)
if render_in_method.arity == 1
Kernel.instance_method(:method)でKernelモジュールが持つmethodメソッドの非束縛メソッド(UnboundMethod)を取得し、.bind_call(@renderable, :render_in)で@renderableに束縛しながら即座に呼び出す構造です。これにより、@renderable自身がmethodを再定義していてもKernel#methodが確実に実行され、render_inのアリティ(引数の数)が正しく取得されます。
リグレッションテストもactionview/test/template/render_test.rbに追加されています。attr_reader :methodを持つクラスを作成し、render_inが正しく呼び出されることを確認します。
def test_render_renderable_object_with_method_reader
renderable = Class.new do
attr_reader :method
def initialize
@method = :get
end
def render_in(view_context, **options)
view_context.render plain: "Hello, #{options[:locals][:name]} with #{method}!"
end
end.new
assert_equal "Hello, Renderable with get!", @view.render(renderable, name: "Renderable")
end
設計判断
Kernel.instance_method(:method).bind_callによるメソッド直接束縛が採用された点が本修正の核心です。
Kernel.instance_methodを使ってRubyのメソッド探索をバイパスし、モジュール階層の頂点から直接束縛する手法は、名前衝突に対する確実な解決策です。変更の差分は実質1行の置き換えであり、既存の動作(アリティが1の場合に非推奨警告を出す、そうでなければオプション付きで呼び出す)はそのまま維持されています。
まとめ
この修正は、render_inのアリティチェックにRubyのメソッド名前空間の安全な参照方法を適用した小さなバグフィックスです。Kernel.instance_method(:method).bind_callというイディオムは、メソッド名の衝突をバイパスする汎用的なテクニックとして、ライブラリ実装者が参考にできる設計パターンを示しています。