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 `<main>'

#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.

#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 `<main>'

#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.

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

···

_________________________________________________________________
Windows 7: It works the way you want. Learn more.

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.

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

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

···

--
Posted via http://www.ruby-forum.com/\.

Thank you Brian. Even better.

···

On Nov 20, 1:33 am, Brian Candler <b.cand...@pobox.com> wrote:

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

>> class String; def irb;evalself, TOPLEVEL_BINDING; end; end
=> nil
>> "class Pumpkin;def pie;puts 'yum';end;end".irb
=> nil
>> Pumpkin.new.pie

yum
=> nil

--
Posted viahttp://www.ruby-forum.com/.