🍣
[Bug #19749] define_method で既存のメソッドを再定義したときの可視化性に関するチケット
[Bug #19749] Confirm correct behaviour when attaching private method with #define_method
- 次のように
private
メソッドを#define_method
経由で呼び出した時にエラーにならないのは期待する挙動かどうかの確認のチケット
# これは private メソッドとして定義される
def bar; end
foo = Object.new
# private メソッドを public メソッドとして定義する
foo.singleton_class.send(:define_method, :bar, method(:bar))
# これはエラーになるのかならないのか
foo.bar # No error.
- 起因としては
TruffleRuby
だと上のコードがエラーになるらしい - これは
#define_method
すると新しいメソッド定義しているだけなので可視化性は現在のスコープに依存するとのこと- なので次のような定義だと
private
になる
- なので次のような定義だと
def bar; end
foo = Object.new
foo.singleton_class.class_exec do
private
foo.singleton_class.send(:define_method, :bar, method(:bar))
end
# error: private method `bar' called for #<Object:0x000077436c728fb8> (NoMethodError)
foo.bar # NoMethodError
- 起票されていた内容自体は問題ないが、チケットの中で以下のような挙動が見つかる
class A
def a; end
private def b; end
m = instance_method(:b)
define_method(:c, m) # public メソッドとして定義される
define_method(:b, m) # これも public メソッドとして定義されることを期待するが private メソッドになる
private
m = instance_method(:a)
define_method(:d, m) # private メソッドとして定義される
define_method(:a, m) # これも private メソッドとして定義されることを期待するが public メソッドになる
# [:c, :b] を期待するが [:c, :a] が返ってくる
p public_instance_methods(false) # => [:c, :a] # should be [:c, :b]
# [:a, :d] を期待するが [:b, :d] が返ってくる
p private_instance_methods(false) # => [:b, :d] # should be [:a, :d]
end
- 要するに同名で
define_method(:foo, instance_method(:foo))
を定義するときに可視化性も更新するかどうか、ってところがポイントですね - 1年近く前のチケットだったんですが最終的には『可視化性を更新する』という形になったみたいです
class C
def foo; end
private
define_method(:foo, instance_method(:foo))
p private_instance_methods(false)
# Ruby 3.3 => []
# Ruby 3.4 => [:foo]
end
Discussion