Bug #5777
closedclass_eval/module_eval works differently when given a string than when given a block in 1.9.2 and 1.9.3
Description
class_eval/module_eval works differently when passed code as a string than when passed the same code as a block in 1.9.2/1.9.3.
In particular, constant lookup appears to vary. Here's a very short test case (this is on a Mac on 1.9.2, but I see the same behaviour on Ubuntu and on 1.9.3p0):
dyn200:~ George$ ruby -v
ruby 1.9.2p290 (2011-07-09 revision 32553) [x86_64-darwin11.0.1]
dyn200:~ George$ irb
irb(main):001:0> class A; X = 1; end; A.class_eval 'X'
=> 1
irb(main):002:0> class B; Y = 1; end; B.class_eval { Y }
NameError: uninitialized constant Class::Y
from (irb):2:in block in irb_binding' from (irb):2:in
class_eval'
from (irb):2
from /Users/George/.rbenv/versions/1.9.2-p290/bin/irb:12:in `'
It appears this was not the case in 1.9.1:
george@production:~$ ruby -v
ruby 1.9.1p378 (2010-01-10 revision 26273) [i486-linux]
george@production:~$ irb
irb(main):001:0> class A; X = 1; end; A.class_eval 'X'
=> 1
irb(main):002:0> class B; Y = 1; end; B.class_eval { Y }
=> 1
It seems unlikely this in intentional (?) -- it certainly violated 'least surprise' for me!
I note also that with const_get it works OK either way, including with a block:
irb(main):001:0> class B; Y = 1; end; B.class_eval { const_get :Y }
=> 1
Updated by wycats (Yehuda Katz) about 13 years ago
It's important to note that most uses of class_eval with a block are mostly taking existing blocks and modifying their context. For example, take a look at the rspec DSL:
describe "TrueClass" do
it "should not be initializable" do
lambda { TrueClass.new }.should raise_error(NoMethodError)
end
end
Intuitively, users expect this TrueClass to be looked up in the context of the original block, not in the context of the eval. The POLS argument only makes sense in a very synthetic environment where users are the ones making most of the calls to class_eval, when in fact most calls are made on behalf of users.
There was a change in 1.9.x series along the lines of what you are suggested, and it caused serious problems for all kinds of libraries. It was reverted in 1.9.2 once the mayhem became obvious.
Updated by jawj (George MacKerron) about 13 years ago
Yehuda, thanks for the response. I understand that the lookup rules were changed between 1.9.1 and 1.9.2 -- that's not the issue I'm raising.
What I find strange is that class_eval in 1.9.2+ interprets the exact same code differently according to whether it's provided as a block or as a string -- as seen in the first test case in my initial report.
Is this really what's intended? If so, then at the very least the documentation for class_eval might mention it.
Updated by andhapp (Anuj Dutta) almost 13 years ago
As per the documentation,
Evaluates the string or block in the context of mod.
Going by the documentation, it should find Y defined for class B. If it's not a bug and intended behaviour then the documentation needs updating.
Updated by ko1 (Koichi Sasada) almost 13 years ago
- Assignee set to shugo (Shugo Maeda)
Updated by shugo (Shugo Maeda) almost 13 years ago
- Status changed from Open to Closed
- % Done changed from 0 to 100
This issue was solved with changeset r35056.
George, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.
- vm_eval.c (rb_mod_module_eval): fix the documentation of
class_eval to mention constant lookup. [ruby-core:41718]
[Bug #5777]
Updated by shugo (Shugo Maeda) almost 13 years ago
George MacKerron wrote:
Yehuda, thanks for the response. I understand that the lookup rules were changed between 1.9.1 and 1.9.2 -- that's not the issue I'm raising.
What I find strange is that class_eval in 1.9.2+ interprets the exact same code differently according to whether it's provided as a block or as a string -- as seen in the first test case in my initial report.
Is this really what's intended? If so, then at the very least the documentation for class_eval might mention it.
It is intended behavior, because a block closes the environment, but a string doesn't.
I've fixed the documentation.