You can use eval to run code from a string, but things are not as simple as they seem. The eval method executes code, but where in the program is that code executed? The context in which code is executed is called a binding. Code is bound to a context of instance variable, local variables, and global variables. If you were to reference the variable x, it's up to the binding to decide which variable named x you're referring to. Examine the following code.
x = 1337
x = 10
binding = func()
puts eval("x", binding)
If you run this program, it will print two numbers. First 1337, then 10. When the Kernel#binding method is called, it will return a Binding object. Even though the x variable in func is a local variable, and should disappear as soon as the method is finished, keeping a copy of the method invocation's binding will make it stick around.
You can't do much with a binding except using it with eval. So, evaluating the value of x with the binding returned by the method, you'll get 1337. Evaluating the value of x in the current binding (without passing a second argument to eval), you'll get 10.
But there's a bit more to explore here. Remember that when returning a binding, you're forcing the state of the method invocation to stay in memory. Examine the following code.
number = a
bind1 = func(1337)
bind2 = func(404)
puts eval("number", bind1)
puts eval("number", bind2)
This program works just as you would expect. Two invocations of the func method, and two numbers printed out. Bindings are an interesting feature, but just like eval, their use is controversial. While they can be useful at times, there's often a better way to achieve the same result.