Bug #19269
closedConstant lookup and #instance_eval
Description
I've noticed a confusing behaviour of #instance_eval
(and #instance_exec
as well). In some cases it doesn't see constants defined in the object class.
Examples:
C = 1
class A
C = 2
end
When #instance_eval
is called with a String - A::C
constant is visible, that is pretty expected:
A.new.instance_eval("C") # => 2
But when it's called with a block - A::C
isn't visible:
A.new.instance_eval { C } # => 1
If we define a method that returns a constant (defined in the class), then A::C
is visible in both cases:
C = 1
class A
C = 2
def c; C; end
end
A.new.instance_eval("c") # => 2
A.new.instance_eval { c } # => 2
So we see that when #instance_eval
called with a block and a constant is accessed directly is the only case when a class constant isn't visible.
Wondering whether it's an expected behaviour and the reason to behave this way.
In the examples above I've added a top-level declaration C=1
only for readability. Without this declaration in all the cases when C
value 1
is returned - a NoName exception is raised (uninitialized constant C (NameError)
)
Updated by zverok (Victor Shepelev) over 2 years ago
As far as I understand, name lookup in block is performed related to the context where the block is defined.
The example with c
(local name) works, because in instance_eval
, it is implicitly self.c
. But wouldn't work if you'll shadow the name with local variable:
C = 1
class A
C = 2
def c; C; end
end
c = 1
A.new.instance_eval("c") # => 2
A.new.instance_eval { c } # => 1
The block could've come from completely different context, and the block is "closure": it remembers the context from where it came from. It is not specific to instance_eval
or constants, that's just how the blocks work.
(With instance_eval(string)
, there is no contexts it carries with itself, so it is interpreted in the target context.)
This would work unambiguously (but is tedious to write:
C = 1
class A
C = 2
def c; C; end
end
c = 1
A.new.instance_eval { self.c } # => 2
A.new.instance_eval { self.class::C } # => 2
Updated by nobu (Nobuyoshi Nakada) over 2 years ago
- Has duplicate Bug #19270: Constants lookup and a singleton class issue added
Updated by andrykonchin (Andrew Konchin) over 2 years ago
- Description updated (diff)
Updated by andrykonchin (Andrew Konchin) over 2 years ago
- Description updated (diff)
Updated by Eregon (Benoit Daloze) over 2 years ago
- Status changed from Open to Closed
This is expected because constant lookup is lexicial, i.e., it's only changed by module/class
keywords, and by eval'ed strings.
Updated by andrykonchin (Andrew Konchin) over 2 years ago
Thank you for clarification 🙇.