Feature #14382
closedMake public access of a private constant call const_missing
Description
Calling obj.foo
where foo
is a private method of obj
calls method_missing
. You would expect klass::FOO
where FOO
is a private constant of klass
to call const_missing
, but currently it doesn't. This makes a small change so that const_missing
will be called in such cases.
In addition to similarity to method_missing
, the main reason for doing this is it offers a way to deprecate public constants. Currently, if you have a public constant and want to make it a private constant, you can't do it without breaking possible callers. With this patch, you can make it a private constant, then override const_missing
in the class, and have const_missing
print a deprecation warning and then return the value of the constant.
Files
Updated by Eregon (Benoit Daloze) about 7 years ago
Isn't Module#deprecate_constant specifically for this usage?
Updated by jeremyevans0 (Jeremy Evans) about 7 years ago
Eregon (Benoit Daloze) wrote:
Isn't Module#deprecate_constant specifically for this usage?
Module#deprecate_constant
deprecates all usage of the constant, both public and private. If you want to keep a constant but change the visibility from public to private, it isn't appropriate.
Updated by matz (Yukihiro Matsumoto) about 7 years ago
The idea seems OK to me. Can you experiment?
Matz.
Updated by jeremyevans0 (Jeremy Evans) about 7 years ago
matz (Yukihiro Matsumoto) wrote:
The idea seems OK to me. Can you experiment?
With the attached patch, both test-all and test-spec pass.
The only time I can see this causing an issue is in a class/module that overrides const_missing
to do something, but also has private constants, and wants an exception raised instead of const_missing
being called for public access to a private constant. That seems very unlikely to me. Usually if const_missing
is used, it is designed to handle all constants to do something. For example, let's say you overrode const_missing
to call a singleton method of the same name, so you could call singleton methods that start with a capital letter using klass::Method
syntax without parentheses:
class Foo
Bar = 1
private_constant :Bar
def self.const_missing(const)
send(const)
end
end
In this case, assume Foo::Bar
constant is an implementation detail. It is probably desired that external use of Foo::Bar
call Foo.Bar()
, but currently this is not possible as an exception is raised before calling Foo.const_missing
.
Unless const_missing
is overridden, this will not change any behavior. I don't usually use const_missing
in any of my applications/libraries, but assuming this is accepted I plan to use this in the deprecate_public gem (https://github.com/jeremyevans/ruby-deprecate_public) to add a Module#deprecate_public_constant
method, which will issue a deprecation warning if a constant is accessed through the public interface (no deprecation warning if accessed through the private interface). This will allow libraries with public constants that are implementation details to deprecate public use of the constants in a way that alerts users of the libraries.
If you have specific ideas for experimentation, I will definitely try them.
Updated by nobu (Nobuyoshi Nakada) about 7 years ago
In the case it is accessed from an inherited class, the receiver class/module may differ from class/module which defines the constant as private.
So I think that the hook method will need another parameter.
And as a private constant is not accessible but not "missing", different name may be better.
Updated by jeremyevans0 (Jeremy Evans) about 7 years ago
nobu (Nobuyoshi Nakada) wrote:
In the case it is accessed from an inherited class, the receiver class/module may differ from class/module which defines the constant as private.
So I think that the hook method will need another parameter.
Can you please let me know which hook method needs another parameter? Also, can you provide example code showing the problem, as I'm not sure I understand?
And as a private constant is not accessible but not "missing", different name may be better.
Are you referring to calling a method other than const_missing
? Then it breaks the similarity with method_missing
. Do you think we should also change ruby so that obj.foo
where foo
is a private method should call a method other than method_missing
?
Updated by jeremyevans0 (Jeremy Evans) almost 7 years ago
- File 0001-Make-public-access-of-a-private-constant-call-const_.patch 0001-Make-public-access-of-a-private-constant-call-const_.patch added
nobu (Nobuyoshi Nakada) wrote:
In the case it is accessed from an inherited class, the receiver class/module may differ from class/module which defines the constant as private.
So I think that the hook method will need another parameter.
Attached is an updated patch against current trunk with an additional test showing use with nested constants, where the superclass calls private_constant
and the subclass accesses the constant publicly. It shows that const_missing
is still called on the subclass in that case, which I think is the most consistent behavior as that mirrors how method_missing
works.
If this isn't the behavior you would expect in this case, can you please explain what behavior you expect?
Updated by nobu (Nobuyoshi Nakada) over 6 years ago
- Status changed from Open to Closed