The thing that instance_eval
does is that it runs the block in the context of a different instance. In other words, it changes the meaning of self
which means it changes the meaning of instance methods and instance variables.
This creates a cognitive disconnect: the context in which the block runs is not the context in which it appears on the screen.
Let me demonstrate that with a slight variation of @Matt Briggs’s example. Let’s say we’re building an email instead of a form:
def mail
builder = MailBuilder.new
yield builder
# executed after the block
# do stuff with builder
end
mail do |f|
f.subject @subject
f.name name
end
In this case, @subject
is an instance variable of your object and name
is a method of your class. You can use nice object-oriented decomposition and store your subject in a variable.
def mail &block
builder = MailBuilder.new
builder.instance_eval &block
# do stuff with builder
end
mail do
subject @subject
name name # Huh?!?
end
In this case, @subject
is an instance variable of the mail builder object! It might not even exist! (Or even worse, it might exist and contain some completely stupid value.) There is no way for you to get access to your object’s instance variables. And how do you even call the name
method of your object? Everytime you try to call it, you get the mail builder’s method.
Basically, instance_eval
makes it hard to use your own code inside the DSL code. So, it should really only be used in cases where there is very little chance that this might be needed.