Constant lookup starting in superclass, not derived class

I'm no doubt missing something obvious but I found this a little
confusing. I would have expected the constant lookup to start in Bar
and therefore succeed. Instead, it appears to start in Base where Foo
is not defined.

class Base
  def foo
    p Foo
  end
end

class Bar < Base
  class Foo
  end
end

Bar.new.foo
# ~> -:3:in `foo': uninitialized constant Base::Foo (NameError)
# ~> from -:12

Can anyone explain what is happening please?

Cheers,

Chris

Hi,

···

In message "Re: Constant lookup starting in superclass, not derived class" on Mon, 11 Sep 2006 21:59:21 +0900, "Chris Roos" <chrisjroos@gmail.com> writes:

I'm no doubt missing something obvious but I found this a little
confusing. I would have expected the constant lookup to start in Bar
and therefore succeed. Instead, it appears to start in Base where Foo
is not defined.

class Base
def foo
   p Foo
end
end

use "self.class::Foo" instead of plain "Foo".

              matz.

Constants, unlike methods and instance variables, are "quasi"-lexicaly
scoped. (I say "quasi" because a sub class can assign to a constant
without munging its parent class's constant) One way of getting around
this particular situation is to use #const_get. e.g.:

class Base
  def foo
    p self.class.const_get('Foo')
  end
end

···

On Mon, Sep 11, 2006 at 09:59:21PM +0900, Chris Roos wrote:

I'm no doubt missing something obvious but I found this a little
confusing. I would have expected the constant lookup to start in Bar
and therefore succeed. Instead, it appears to start in Base where Foo
is not defined.

class Base
def foo
   p Foo
end
end

class Bar < Base
class Foo
end
end

Bar.new.foo
# ~> -:3:in `foo': uninitialized constant Base::Foo (NameError)
# ~> from -:12

Can anyone explain what is happening please?

Chris Roos wrote:

I'm no doubt missing something obvious but I found this a little
confusing. I would have expected the constant lookup to start in Bar
and therefore succeed. Instead, it appears to start in Base where Foo
is not defined.

class Base
def foo
   p Foo
end
end

class Bar < Base
class Foo
end
end

Bar.new.foo
# ~> -:3:in `foo': uninitialized constant Base::Foo (NameError)
# ~> from -:12

Can anyone explain what is happening please?

Foo is not in the scope of Base so it cannot be looked up the way you did it. See Matz's explanation how to access it nevertheless. Generally this form of dependency is not desirable, because the base class should be self contained and not depend on any sub classes. In Java you could make Base abstract and add an abstract method that will return a Class instance - but in Ruby there are no abstract classes.

Kind regards

  robert

Do you have any suggestions of alternative implementation?

I'd rather not use the self.class.const_get method as it clutters the
code somewhat.

I wonder if I'm trying to refactor two seemingly identical methods
that are in fact different.

class Main1
  def Foo; end
  def foo
    Foo.new
  end
end

class Main2
  def Foo; end
  def foo
    Foo.new
  end
end

At this point, I created a superclass to hold the common foo method
definition. However, if I had the fully qualified namespace for Foo
(Main1::Foo and Main2::Foo) in each method they would be different and
I wouldn't have moved them. Maybe that is part of my problem.

I understand the point about the base class depending on child classes
not being desirable. What about a module relying on constants defined
elsewhere (does the same apply)?

module Base
  def foo
    p Foo.new
  end
end

class Foo
  include Base
  class Foo
  end
end

Thanks again,

Chris

···

On 9/11/06, Robert Klemme <shortcutter@googlemail.com> wrote:

Chris Roos wrote:
> I'm no doubt missing something obvious but I found this a little
> confusing. I would have expected the constant lookup to start in Bar
> and therefore succeed. Instead, it appears to start in Base where Foo
> is not defined.
>
> class Base
> def foo
> p Foo
> end
> end
>
> class Bar < Base
> class Foo
> end
> end
>
> Bar.new.foo
> # ~> -:3:in `foo': uninitialized constant Base::Foo (NameError)
> # ~> from -:12
>
> Can anyone explain what is happening please?

Foo is not in the scope of Base so it cannot be looked up the way you
did it. See Matz's explanation how to access it nevertheless.
Generally this form of dependency is not desirable, because the base
class should be self contained and not depend on any sub classes. In
Java you could make Base abstract and add an abstract method that will
return a Class instance - but in Ruby there are no abstract classes.

Kind regards

        robert

Chris Roos wrote:

Do you have any suggestions of alternative implementation?

Depends on what you want to do. If you just want to create instances then you can just use a method.

class Base
   def something
     f = create_foo
     # use f
   end
end

class Derived < Base
   class Foo
   end

   def create_foo(*a,&b)
     Foo.new(*a,&b)
   end
end

Or, if you want to do it more fancy you can define create_foo in the base class and have it return nil or raise an exception.

Kind regards

  robert

PS: Please don't top post and trim quotings - that way others can easier follow threads here.

I was bumping against the same wall you are for a while. Later, i
learned how to use meta classes and then realized that they gave me
exactly what i was looking for.

Bret

Or to put it more clearly, 'please trim quotations and don't top post.'

Drifting wildly away from the topic, the ambiguity in the original
request reminds me of the signs posted by Southwark Council on some of
their properties: 'Do not exercise or allow your dog to foul the
estate.' (People seem to follow the first half better than the
second.)

Paul.

···

On 11/09/06, Robert Klemme <shortcutter@googlemail.com> wrote:

PS: Please don't top post and trim quotings - that way others can easier
follow threads here.