Inconsistency in "constantize"

Hi,

I've changed `const_get` in Ruby 2.0. If it were implemented in 1.9, it would look roughly like this:

  def const_get name, inherited = true     name.to_s.split('::').inject(self.class) do |klass, name|       klass.const_get name, inherited     end   end

I was hoping we could use this to replace the implementation of `constantize`, but it seems we have an inconsistency in the way that `constantize` works in regard to constant inheritance.

I've written a test to demonstrate the problem:

  https://gist.github.com/4024158

For some reason `constantize` inherits constants, but not when the constant is defined on Object, where `const_get` in Ruby 2.0 will always honor the inheritance (even if the constant is defined at the top).

Is there any chance we can change this?

Yeah, at first sight they seem to be different in their intention. The new const_get is implemented as a recursive const_get, whereas constantize is closer to emulate the resolution algorithm for constant paths.

The subtle difference is in modules, as the test shows.

As you know, X::String raises a NameError if X is a module, and resolves with a warning if X is a class that inherits from Object, because Object is among its ancestors but the access is considered dubious anyway. That's what constantize kinda emulates. I believe "constantize" also raises if X is a class, and always treats the first constant in the path as if it had a leading double colon "::", that's why I say "kinda".

"Admin::UsersController".constantize fails if such controller does not exist, regardless of the existence of an eventual top-level UsersController, whereas if I understand it correctly Object.const_get("Admin::UsersController", true) would resolve to such top-level controller, because the original const_get name, inherit does check Object by hand for modules, as the algorithm for relative constant names does.

Not sure about changing that behavior.

> Hi, > > I've changed `const_get` in Ruby 2.0. If it were implemented in 1.9, it > would look roughly like this: > > def const_get name, inherited = true > name.to_s.split('::').inject(self.class) do |klass, name| > klass.const_get name, inherited > end > end > > I was hoping we could use this to replace the implementation of > `constantize`, but it seems we have an inconsistency in the way that > `constantize` works in regard to constant inheritance. > > I've written a test to demonstrate the problem: > > annoying.rb · GitHub > > For some reason `constantize` inherits constants, but not when the > constant is defined on Object, where `const_get` in Ruby 2.0 will always > honor the inheritance (even if the constant is defined at the top). > > Is there any chance we can change this?

Yeah, at first sight they seem to be different in their intention. The new const_get is implemented as a recursive const_get, whereas constantize is closer to emulate the resolution algorithm for constant paths.

The subtle difference is in modules, as the test shows.

As you know, X::String raises a NameError if X is a module, and resolves with a warning if X is a class that inherits from Object, because Object is among its ancestors but the access is considered dubious anyway. That's what constantize kinda emulates. I believe "constantize" also raises if X is a class, and always treats the first constant in the path as if it had a leading double colon "::", that's why I say "kinda".

"Admin::UsersController".constantize fails if such controller does not exist, regardless of the existence of an eventual top-level UsersController, whereas if I understand it correctly Object.const_get("Admin::UsersController", true) would resolve to such top-level controller, because the original const_get name, inherit does check Object by hand for modules, as the algorithm for relative constant names does.

That is correct. This implementation const_get would find class.

Not sure about changing that behavior.

Neither am I, that's why I emailed. :wink:

It's a shame, I'm not actually sure what the new `const_get` buys us if we can't replace this method. :cry: