Eval behaves differently inside a method definition

#lets give strings the ability to evaluate theirselves as if in irb.

class String
def irb
eval self
end
end
=> nil

“puts ‘look at me’”
=> “puts ‘look at me’”

“puts ‘look at me’”.irb
look at me
=> nil

#good looks like strings can interpret theirselves. But how about this
case?

“class Pumpkin;def pie;puts ‘yum’;end;end”.irb
=> nil

Pumpkin.new
NameError: uninitialized constant Pumpkin
from (irb):287
from /usr/local/bin/irb:12:in `’

#not flying. Is it a problem with the string? Try with eval of the
same string:

eval “class Pumpkin;def pie;puts ‘yum’;end;end”
=> nil

Pumpkin.new
=> #Pumpkin:0x109896c

No problem evaluating the class definition in a string. So eval
behaves differently inside a method definition.

Could anyone please explain that?
Thanks in advance,
Tim

Hi Tim.

look at me
from /usr/local/bin/irb:12:in `’
behaves differently inside a method definition.
It’s not that eval behaves differently inside a method definition. It’s
just that it takes into account the scope in which it was called.

When inside the String class, or any other class for that matter, if you
create another class, you can’t call it from outside the parent class
without referring to the parent. So for example:

class String
class Pumpkin
def pie
puts ‘yum’
end
end
end
Pumpkin.new #=> NameError: uninitialized constant Pumpkin
String::Pumpkin.new #=> #Pumpkin:0x109896c

The parent class effectively acts as a namespace for the subclass. BTW,
I really don’t think I should call it a “subclass”, as that seems to
imply inheritence - none of that here. Maybe someone else can tell us
the proper name for this structure/pattern.

So you probably understand the issue by now, eval within the String
class (whether or not inside a method definition) creates a “subclass”
of sorts, and hence you can’t call it directly outside. If you tried to
call “String::Pumpkin.new” instead after using your “irb” method, it
would have worked (try it out). Alternately, you could have done it this
way:

class String
def irb
eval self
end
end
“class ::Pumpkin;def pie;puts ‘yum’;end;end”.irb
Pumpkin.new.pie

Hope that helps.

  • Ehsan

The parent class effectively acts as a namespace for the subclass. BTW, I really don’t think I should call it a “subclass”, as that seems to imply inheritence - none of that here. Maybe someone else can tell us the proper name for this structure/pattern.

I understand–how about nested class or encapsulated class.

Hope that helps.

  • Ehsan

Ehsan, nice explanation!
Hmmm. That means that you could make a binding to main

SPECIAL_BINDING = binding
and add that to the eval argument to get it back to the main scope
where eval seems to work on anything:

class String
def irb
eval(self, SPECIAL_BINDING)
end
end

Now it should all work!

“puts ‘yohoo’”
=> “puts ‘yohoo’”

“puts ‘yohoo’”.irb
yohoo
=> nil

“class What;def cool;puts ‘is totally awesome’;end;end”.irb
=> nil

What.new
=> #What:0x1032a7c

What.new.cool
is totally awesome
=> nil

Mucho Gracias, amigo!!!
Tim

There is the existing TOPLEVEL_BINDING constant you can use for this.

class String; def irb; eval self, TOPLEVEL_BINDING; end; end
=> nil

“class Pumpkin;def pie;puts ‘yum’;end;end”.irb
=> nil

Pumpkin.new.pie
yum
=> nil

Thank you Brian. Even better.