Bug #18561
closedMake singleton def operation and define_singleton_method explicitly use public visibility
Description
Currently nearly all uses of define_singleton_method
or def obj.foo
will ignore the caller's frame/cref visibility and use the default visibility of public. I propose this be made explicit in the code, documentation, and ruby specs.
$ ruby -e 'class Foo; private; define_singleton_method(:foo) {p :ok}; end; Foo.foo'
:ok
$ ruby -e 'class Foo; private; def self.foo; p :ok; end; end; Foo.foo'
:ok
This works because the class in which the method is defined is nearly always different from the calling scope, since we are usually calling define_singleton_method
against some other object. It "accidentally" ends up being public all the time, like def self.foo
.
However, there is at least one (weird) edge case where the frame/cref visibility is honored:
$ ruby -e '$o = Object.new; class << $o; private; $o.define_singleton_method(:foo){}; end; $o.foo'
-e:1:in `<main>': private method `foo' called for #<Object:0x00007fcf0e00dc98> (NoMethodError)
This also works for def $o.foo
but I would argue this is unexpected behavior in both cases. It is difficult to trigger, since you have to already be within the target singleton class body, and the "normal" behavior everywhere else is to ignore the frame/cref visibility.
It would not be difficult to make both forms always use public visibility:
- Split off the actual method-binding logic from
rb_mod_define_method
into a separate functionmod_define_method_internal
that takes a visibility parameter. - Call that new method from
rb_mod_define_method
(with cref-based visibility calculation) andrb_obj_define_method
(with explicit public visibility).