Eval vs. class_eval

Howdy everyone,

Can anyone explain to me why

class Fil
def self.add_class name
class_eval “#{name.capitalize} = Class.new(self)”
end
end

behaves differently than

class Fil
def self.add_class name
eval “#{name.capitalize} = Class.new(self)”
end
end

especially with regards the dynamic placement of the classes created on
the fly?

Thank you!
Carl

On Mon, Feb 4, 2008 at 9:34 PM, Carl Y.
[email protected] wrote:

Howdy everyone,

Heya

class Fil
def self.add_class name
eval “#{name.capitalize} = Class.new(self)”
end
end

especially with regards the dynamic placement of the classes created on
the fly?

eval and class_eval work on different scopes. eval works just in its
current scope(here your method), however class_eval works for the
whole class(your Fil)

On Feb 4, 2008 4:16 PM, Thomas W. [email protected]
wrote:

eval and class_eval work on different scopes. eval works just in its
current scope(here your method), however class_eval works for the
whole class(your Fil)

Are you saying there’s some kind of weird scope shift up? If you run
version
two (with eval) and create a child class and a grandchild class, it
looks
like it creates the grandchild within Fil, not within Fil’s child. The
class_eval version creates it so that Fil’s child is within Fil and
Fil’s
grandchild is within Fil’s child. At least… that’s what’s baffling to
ME
about this. Maybe Carl can provide an example.

If I’m guessing totally wrong, what’s going on, exactly, with the scope
here?

Ben

The structure I observe with running the eval version is

class Fil
class NewClass1
end
class NewClass2
end
end

I tried both version all get the first result, I think this is a
reasonable
result(Ruby 1.8.6 on win32). Why do you think the result should be in
the
2nd form?

Day wrote:

On Feb 4, 2008 4:16 PM, Thomas W. [email protected]
wrote:

eval and class_eval work on different scopes. eval works just in its
current scope(here your method), however class_eval works for the
whole class(your Fil)

Are you saying there’s some kind of weird scope shift up? If you run
version
two (with eval) and create a child class and a grandchild class, it
looks
like it creates the grandchild within Fil, not within Fil’s child. The
class_eval version creates it so that Fil’s child is within Fil and
Fil’s
grandchild is within Fil’s child. At least… that’s what’s baffling to
ME
about this. Maybe Carl can provide an example.

If I’m guessing totally wrong, what’s going on, exactly, with the scope
here?

Ben

Right.

The structure I observe with running the eval version is

class Fil
class NewClass1
end
class NewClass2
end
end

but running class_eval I observe

class Fil
class NewClass1
class NewClass2
end
end
end

I believe the second form should be the result for either eval or
class_eval since they both default to a receiver of self, and when
adding NewClass2 the receiver self should be Fil::NewClass1, not Fil.

I’m guessing either what I’m observing is wrong somehow or there is a
special rule about eval and the context it evaluates in?

Carl

Carl and I are working on the same project, here. What he means is this:

Fil.add_class :NewClass1
Fil::NewClass1.add_class :NewClass2.

We expected eval to create scope nesting that mirrors the inheritance,
but
apparently even children of NewClass1 are scoped inside Fil (as opposed
to
inside their parents). This actually appears to be the behavior of
class_eval. It’s confusing as to why eval would do that, though. Is that
more clear?

Ben

Flower Born wrote:

The structure I observe with running the eval version is

class Fil
class NewClass1
end
class NewClass2
end
end

I tried both version all get the first result, I think this is a
reasonable
result(Ruby 1.8.6 on win32). Why do you think the result should be in
the
2nd form?

When I work with the following code -

class Fil
def self.add_class name
#~ eval “#{name.capitalize} = Class.new(self)”
#~ class_eval “#{name.capitalize} = Class.new(self)”
end
end

Fil.add_class ‘newclass1’
Fil::Newclass1.add_class ‘newclass2’

#The test
p Fil::Newclass2 == Fil::Newclass1::Newclass2

When I uncomment the eval (and comment the class_eval) the final line
evaluates to true.

When I uncomment the class_eval (and comment the eval) the final line
throws an error - test.rb:12: uninitialized constant Fil::Newclass2
(NameError)

It seems to me the behavior of the second test with class_eval and
uninitialized constant is correct, but either way I’m most curious why
they behave differently.

I can’t see how the scope of eval can be different than class_eval when
the implicit receiver is self in both cases.

On Feb 5, 2008 2:05 PM, Ryan D. [email protected] wrote:

=> Fil::X

Yes, but that doesn’t answer the question, really, about why the scope
is so counter-intuitive. (Though this is handy and cleaner-looking, so
I’m going to seriously consider using it, as long as it matches up
with how we want this to work). I just hate to fix a problem and not
know why it worked.

I pared down the code we’re puzzling over and pastied it:
http://pastie.caboo.se/148064
I took out some logic (like converting the name of the new class to
camel case), so this is sort of fragile, but I handed it only friendly
values, so that should be alright.

If you execute this as it, you’ll see that the array returned by each
of the three subtypes calls has the full chain of scope which, in this
case, happens to mirror the inheritance. If you toggle off line 18 and
on line 19, you’ll see that everything seems to be scoped directly in
Robert. I included lines 21 and 22 so that I could show that “self”
has the same value with both eval and class_eval. So why is the scope
different? As best I can tell, using const_set behaves like using
class_eval. It may be that that’s really what we want here, but
still… What’s up with eval?

Thanks everyone for your help so far. And thanks in advance for further
help.

Ben

On Feb 4, 2008, at 12:34 , Carl Y. wrote:

class Fil
def self.add_class name
class_eval “#{name.capitalize} = Class.new(self)”
end
end

You can cut out all the confusion by not evaling at all:

class Fil
def self.add_class name
const_set name.capitalize, Class.new(self)
end
end

Fil.add_class “X”
=> Fil::X

Day wrote:

Yes, but that doesn’t answer the question, really, about why the scope
is so counter-intuitive. (Though this is handy and cleaner-looking, so
I’m going to seriously consider using it, as long as it matches up
with how we want this to work). I just hate to fix a problem and not
know why it worked.

I don’t have a clear answer but I think it has something to do with the
fact that constants are lexically scoped.

Here is some code that should hopefully help:

class Foo
def self.add_class_1(name)
puts “from #{self}, create #{name} with class_eval”
class_eval %(
puts " nesting = %s" % Module.nesting.inspect
puts " self = %s" % self.inspect
#{name} = Class.new(self)
)
end
def self.add_class_2(name)
puts “from #{self}, create #{name} with eval”
eval %(
puts " nesting = %s" % Module.nesting.inspect
puts " self = %s" % self.inspect
#{name} = Class.new(self)
)
end
end

Foo.add_class_1(“A”).add_class_1(“B”)
from Foo, create A with class_eval
nesting = [Foo, Foo]
self = Foo
from Foo::A, create B with class_eval
nesting = [Foo::A, Foo]
self = Foo::A
=> Foo::a::B

Foo.add_class_2(“C”).add_class_2(“D”)
from Foo, create C with eval
nesting = [Foo]
self = Foo
from Foo::C, create D with eval
nesting = [Foo]
self = Foo::C
=> Foo::smiley: