Evaluating Code in an Earlier Context

Problem

You've written a method that evaluates a string as Ruby code. But whenever anyone calls the method, the objects referenced by your string go out of scope. Your string can't be evaluated within a method.

For instance, here's a method that takes a variable name and tries to print out the value of the variable.

def broken_print_variable(var_name) eval %{puts "The value of #{var_name} is " + #{var_name}.to_s} end

The eval code only works when it's run in the same context as the variable definition. It doesn't work as a method, because your local variables go out of scope when you call a method.

tin_snips = 5 broken_print_variable('tin_snips') # NameError: undefined local variable or method 'tin_snips' for main:Object var_name = 'tin_snips' eval %{puts "The value of #{var_name} is " + #{var_name}.to_s} # The value of tin_snips is 5

 

Solution

The eval method can execute a string of Ruby code as though you had written in some other part of your application. This magic is made possible by Binding objects. You can get a Binding at any time by calling Kernel#binding, and pass it in to eval to recreate your original environment where it wouldn't otherwise be available. Here's a version of the above method that takes a Binding:

def print_variable(var_name, binding) eval %{puts "The value of #{var_name} is " + #{var_name}.to_s}, binding end vice_grips = 10 print_variable('vice_grips', binding) # The value of vice_grips is 10

 

Discussion

A Binding object is a bookmark of the Ruby interpreter's state. It tracks the values of any local variables you have defined, whether you are inside a class or method definition, and so on.

Once you have a Binding object, you can pass it into eval to run code in the same context as when you created the Binding. All the local variables you had back then will be available. If you called Kernel#binding within a class definition, you'll also be able to define new methods of that class, and set class and instance variables.

Since a Binding object contains references to all the objects that were in scope when it was created, those objects can't be garbage-collected until both they and the Binding object have gone out of scope.

See Also

Категории