On Sun, 05 Mar 2006 05:18:39 +0100, Sam Kong <sam.s.kong / gmail.com> wrote:

> The visibility issue is quite confusing.
> See the following example.
>
> X = "top-level"
>
> class C
> 	X = "class-level"
>
> 	class << self
> 		def a
> 			puts X
> 		end
> 	end
> end
>
> def C.b
> 	puts X
> end
>
> class << C
	p [self, self.ancestors] #=> [#<Class:C>, [Class, Module, Object, Kernel]]
> 	def c
> 		puts X
> 	end
> end
>
> C.a #=>class-level
> C.b #=>top-level
> C.c #=>top-level
>
>
> class D
> 	X = "class-level"
> 	def f
> 		puts X
> 	end
> end
>
> obj = D.new
>
> def obj.g
> 	puts X
> end
>
> class << obj
	p [self, self.ancestors] #=> [#<Class:#<D:0xb7f3bda0>>, [D, Object,  
Kernel]]
> 	def h
> 		puts X
> 	end
> end
>
> obj.f #=>class-level
> obj.g #=>top-level
> obj.h #=>class-level
>
> Very inconsistent between a class and an object.
> Can somebody explain this strange behavior?

Well it's not inconsistent, it's just complicated ;-)

As you can see above for C.c the singleton class of C (#<Class:C>) is  
asked for the constant, it doesn't have C in it's ancestors, so the  
constant lookup finds Object's X.
For obj.h the singleton class of obj (#<Class:#<D:0xb7f3bda0>>) is asked  
for the constant, it does have D in it's ancestors, so D::X is found.

But actually it's even more complicated (continuing your code):

$obj=obj
class Object
	class << $obj
		def i
			puts X
		end
	end
end

obj.i #=>top-level

This is because the constant lookup first checks in all the outer lexical  
scopes if the constant is directly defined in one of the classes and then  
does a full const_get on the innermost class. So in this case, the  
following happens:

1. Does #<Class:#<D:0xb7f3bda0>> (without ancestors) have a constant X =>  
no
2. Does Object (without ancestors) have a constant X => yes => constant  
found

If step 2 wouldn't have found the constant then ruby would have checked  
the ancestors of #<Class:#<D:0xb7f3bda0>> for the constant:

class D
	Y = "D::Y"
end

class Object
	class << $obj
		def j
			puts Y
		end
	end
end

obj.j #=>D::Y

Here the following happens:

1. Does #<Class:#<D:0xb7f3bda0>> (without ancestors) have a constant Y =>  
no
2. Does Object (without ancestors) have a constant Y => no
3. Does #<Class:#<D:0xb7f3bda0>> (including ancestors) have a constant Y  
=> yes => constant found


I hope that helps,
Dominik