Why can\'t I access \'B\' in the following from \'A\' but can from the main environment?
module A; end
A.instance_eval{B=1}
B #=> 1
A::B #=> uninitial
. . . constants that are not defined within a class or module are given global scope.
What matters for constant definition is the enclosing lexical scope, not the current receiver or the value of self
.
The idiomatic way to do this would be
A.const_set(:B, 1)
A::B #=> 1
As to why it doesn't work, in Ruby 1.8 and 1.9.2+ (it was different in 1.9.1), constant lookup is lexically scoped. I found a good blog post with an explanation. To quote:
Note that these rules apply to constant definition as well as lookup. In 1.8 and 1.9.2, a constant defined in a class_evaluated block will be defined in the enclosing lexical scope, rather than the scope of the receiver.
The same is also true for instance_eval
.
module A; end
A.class_eval{B=1}
B # Undefined
A::B # 1
As for why it works, I'm not really sure. I occasionally use metaprogramming like this when creating very meta frameworks such the Small Eigen Collider, but not in day-to-day work.
From the documentation of instance_eval (emphasis mine):
Evaluates a string containing Ruby source code, or the given block, within the context of the receiver (obj). In order to set the context, the variable self is set to obj while the code is executing, giving the code access to obj’s instance variables.
Nothing more is done here. In particular, the constant assignment runs in the enclosing context of the block. Observe:
irb(main):001:0> module A
irb(main):002:1> module B; end
irb(main):003:1> B.instance_eval { C = 1 }
irb(main):004:1> end
=> 1
irb(main):006:0> A::C
=> 1