Lets start with a simple example:
class FooIn the example the Foo class has a method, create_block, that simply creates and returns a block. Despite the fact that this doesn't look very interesting, it actually provides you with the ability to evaluate any string of ruby code in the scope of the block. This gives you the power to evaluate ruby code in the context of an object without directly having reference to it.
proc = Foo.new.create_blockWhen would you ever actually use such a thing? I recently used it on my current project while developing a DSL to represent SQL. Our current syntax looks like this:
eval "self.class", proc.binding #=> Foo
Select[:column1, :column2].from[:table1, :table2].where doWhen the above code is evaluated the  instance method (following
equal table1.id, table2.table1_id
from) saves each table name in an array. Then, when the
wheremethod is executed the
method_missingmethod is called twice, once with :table1 and then with :table2. When method_missing is called we check that table name array includes the symbol argument to verify it is a valid table name. If the table name is valid we return an object that knows how to react to column names. However, if the table name is invalid we call
superand raise NameError.
All of this works perfectly, until you start using sub-queries. For example, the following code will not work with the current implementation.
Delete.from[:table1].where doUnfortunately, this needed to work, and using eval and specifying a binding was how we made it work. The trick is getting the table names array from the outer block into the inner block without explicitly passing it in somehow (which would have made the DSL ugly).
equal table1.column1, table2.column2
To solve this problem, within the where method we added:
tables.concat(eval("respond_to?(:tables) ? tables : ", block.binding))Let's break this statement apart and see what it's doing. The first thing it's going to do is
eval "respond_to?(:tables) ? tables : ", block.bindingWhich simply means "eval that statement within the scope of the block". In this case the scope of that block is:
Delete.from[:table1].where do .. endThis scope is a Delete instance, which does have a tables array (tables #=> [:table1]). Therefore, the statement will evaluate and return the tables array, and the rest of the statement could be pictured as this:
tables.concat([:table1])This is simply going to concatenate all the table names into the tables array available in the inner block. After this one line change, the statement now produces expected results.
delete from table1 where exists (select column2 from table2 where table1.column1 = table2.column2)For more info see eval in the core documentation.