Bug #5777

class_eval/module_eval works differently when given a string than when given a block in 1.9.2 and 1.9.3

Added by George MacKerron over 2 years ago. Updated about 2 years ago.

[ruby-core:41718]
Status:Closed
Priority:Normal
Assignee:Shugo Maeda
Category:-
Target version:-
ruby -v:ruby 1.9.2p290 (2011-07-09 revision 32553) [x86_64-darwin11.0.1] Backport:

Description

classeval/moduleeval 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) [x8664-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.classeval { 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.classeval '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.classeval { constget :Y }
=> 1

Associated revisions

Revision 35056
Added by Shugo Maeda about 2 years ago

  • vmeval.c (rbmodmoduleeval): fix the documentation of class_eval to mention constant lookup. [Bug #5777]

History

#1 Updated by Yehuda Katz over 2 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.

#2 Updated by George MacKerron over 2 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.

#3 Updated by Anuj Dutta over 2 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.

#4 Updated by Koichi Sasada about 2 years ago

  • Assignee set to Shugo Maeda

#5 Updated by Shugo Maeda about 2 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.


  • vmeval.c (rbmodmoduleeval): fix the documentation of class_eval to mention constant lookup. [Bug #5777]

#6 Updated by Shugo Maeda about 2 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.

Also available in: Atom PDF