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

  • This trick is used in several places throughout this book; see, for example, Recipe 1.3, "Substituting Variables into an Existing String," and Recipe 10.9, "Automatically Initializing Instance Variables"


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net