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:
>
> 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.

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: