What's the diff between puts y and puts "#{y}" in class_eval

Look at the code.

y = " another hi"

class Wes
end

Wes.class_eval <<-END
def hello
puts “#{y}”
puts y # I get wrong number of arguments error
end
END

While puts “#{y}” works puts y fails. And I am not sure what’s the
explanation.

Any thoughts.

Hi,

In message “Re: what’s the diff between puts y and puts “#{y}” in
class_eval”
on Mon, 28 Jan 2008 16:22:33 +0900, Raj S. [email protected]
writes:

|Look at the code.

Just because #{y} is expanded at compile time, so that class_eval
receives

puts " another hi"

whereas mere y cannot be referenced since its scope is different.

          matz.

Hi Matz,

Thanks for the answer but I couldn’t quite follow it.

It seems that you are suggesting that the scope of “#{y}” is different
from the scope of plain vanilla ‘y’ in “puts y”.

Could you please further elaborate on that. To me it seems like both of
them have the same scope because they are both being used in the same
method.

“R” == Raj S. [email protected] writes:

R> Could you please further elaborate on that. To me it seems like both
of
R> them have the same scope because they are both being used in the same
R> method.

Well, when you write this

y = " another hi"

class Wes
end

Wes.class_eval <<-END
def hello
puts “#{y}”
puts y # I get wrong number of arguments error
end
END

  1. ruby create the string at toplevel (where is defined `y’) and this
    give
def hello
   puts " another hi"
   puts y # I get wrong number of arguments error
end
  1. the string is given to class_eval, which create the method #hello
    in Wes
def hello
   puts " another hi"
   puts y # I get wrong number of arguments error
end

when the method is compiled, y' is resolved as a call to a method (no local variable y’ was defined in the def #hello)

When you call Wes.new.hello, ruby try to call this method and it give
you
an error.

Guy Decoux

On 1/28/08, Raj S. [email protected] wrote:

Could you please further elaborate on that. To me it seems like both of
them have the same scope because they are both being used in the same
method.

Compare to this:

class T
def first
“hello”
end
def second
“chunky bacon”
end
end

y = “first”

T.class_eval <<-END
def hello
puts #{y}
end
END

T.new.hello # prints “hello”

The key is that the “<<-END” is creating an interpolated string in the
original context; the interpolation of #{y} isn’t happening during the
call to “hello”, but rather as part of the string passed to
class_eval.

You are probably confused by the fact that in the sample code, the
interpolation is happening inside of another string constant, which
makes it look like it’s happening as part of “hello”. It’s not.

– ryan

Ryan Ingram wrote:

On 1/28/08, Raj S. [email protected] wrote:

Could you please further elaborate on that. To me it seems like both of
them have the same scope because they are both being used in the same
method.

Compare to this:

class T
def first
“hello”
end
def second
“chunky bacon”
end
end

y = “first”

T.class_eval <<-END
def hello
puts #{y}
end
END

T.new.hello # prints “hello”

The key is that the “<<-END” is creating an interpolated string in the
original context; the interpolation of #{y} isn’t happening during the
call to “hello”, but rather as part of the string passed to
class_eval.

You are probably confused by the fact that in the sample code, the
interpolation is happening inside of another string constant, which
makes it look like it’s happening as part of “hello”. It’s not.

– ryan

Thanks for the explanation, I’ve been watching this thread and waiting
for an explanation that I could comprehend. If I understand your
explanation correctly, the steps are:

  1. The substitution in the string:

str =<<STR
def hello
puts #{y}
end
STR

is performed. The scope is the global scope, so the string becomes:

str =<<STR
def hello
puts first
end
STR

  1. The string is class_eval’ed in the scope of class T, producing:

class T
def first
“hello”
end

def second
“chunky bacon”
end

def hello
puts first
end
end

  1. When the method hello is called, the line:

puts first

outputs the return value of the first method.

Applying those steps to the op’s example:

y = " another hi"

class Wes
end

Wes.class_eval <<-END
def hello
puts “#{y}”
puts y # I get wrong number of arguments error
end
END

  1. The string:

<<-END
def hello
puts “#{y}”
puts y # I get wrong number of arguments error
end
END

becomes:

<<-END
def hello
puts “another hi”
puts y # I get wrong number of arguments error
end
END

  1. The class Wes is transformed into:

class Wes
def hello
puts “another hi”
puts y
end
end

  1. Then a statement such as:

w = Wes.new
w.hello

produces an error because there is no variable or method named ‘y’
inside class Wes:

rb:4:in hello': undefined local variable or method y’ for
#Wes:0x25328 (NameError)

Yep, you’ve got it exactly.