I'm working my way through Pickaxe 1.9, and I'm a bit confused by constant-lookup in instance/class_eval blocks. I'm using 1.9.2.
It seems that Ruby handles constant-lookup in *_eval blocks the same way it does method-lookup:
- look for a definition in receiver.singleton_class (plus mixins);
- then in receiver.singleton_class.superclass (plus mixins);
- then continue up the eigenchain until you get to
#<Class:BasicObject>
; - whose superclass is Class;
- and then up the rest of the ancestor chain (including
Object
, which stores all the constants you define at the top-level), checking for mixins along the way
Is this correct? The Pickaxe discussion is a bit terse.
Some examples:
class Foo
CONST = 'Foo::CONST'
class << self
CONST = 'EigenFoo::CONST'
end
end
Foo.instance_eval { CONST } # => 'EigenFoo::CONST'
Foo.class_eval { CONST } # => 'EigenFoo::CONST', not 'Foo::CONST'!
Foo.new.instance_eval { CONST } # => 'Foo::CONST'
In the class_eval example, Foo-the-class isn't a stop along Foo-the-object's ancestor chain!
And an example with mixins:
module M
CONST = "M::CONST"
end
module N
CONST = "N::CONST"
end
class A
include M
extend N
end
A.instance_eval { CONST } # => "N::CONST", because N is mixed into A's eigenclass
A.class_eval { CONST } # => "N::CONST", ditto
A.new.instance_eval { CONST } # => "M::CONST", because A.new.class, A, mixes in M
In 1.9.2 the constant lookup has changed again to be equivalent to the 1.8.7 behavior.
Basically, the constants are quasi-lexically scoped. This USED to be different between 1.9.x and 1.8.x branches, and it made library cross compatibility a pain, so they changed it back.
Yehuda Katz's (successful) appeal to restore 1.8 behavior
Constants are effectively lexically scoped so you cannot access them short hand outside of the module hierarchy within which they are defined. There is a good explanation here and slightly off topic but a good read here.