May 28, 2010
Here is updated article on the same topic .
Following code will print 99 as the output.
class Klass
def initialize
@secret = 99
end
end
puts Klass.new.instance_eval { @secret }
Nothing great there. However try passing a parameter to instance_eval .
puts Klass.new.instance_eval(self) { @secret }
You will get following error.
wrong number of arguments (1 for 0)
So instance_eval does not allow you to pass parameters to a block.
instance_exec was added to ruby 1.9 and it allows you to pass parameters to a
proc. This feature has been backported to ruby 1.8.7 so we don't really need
ruby 1.9 to test this feature. Try this.
class Klass
def initialize
@secret = 99
end
end
puts Klass.new.instance_exec('secret') { |t| eval"@#{t}" }
Above code works. So now we can pass parameters to block. Good.
Another feature of instance_exec is that it changes the value of self. To
illustrate that I need to give a longer example.
module Kernel
def singleton_class
class << self
self
end
end
end
class Human
proc = lambda { puts 'proc says my class is ' + self.name.to_s }
singleton_class.instance_eval do
define_method(:lab) do
proc.call
end
end
end
class Developer < Human
end
Human.lab # class is Human
Developer.lab # class is Human ; oops
Notice that in that above case Developer.lab says "Human". And that is the
right answer from ruby perspective. However that is not what I intended. ruby
stores the binding of the proc in the context it was created and hence it
rightly reports that self is "Human" even though it is being called by
Developer.
Go to
http://facets.rubyforge.org/apidoc/api/core/index.html
and look for instance_exec method. The doc says
Evaluate the block with the given arguments within the context of this object, so self is set to the method receiver.
It means that instance_exec evaluates self in a new context. Now try the same
code with instance_exec .
module Kernel
def singleton_class
class << self
self
end
end
end
class Human
proc = lambda { puts 'proc says my class is ' + self.name.to_s }
singleton_class.instance_eval do
define_method(:lab) do
self.instance_exec &proc
end
end
end
class Developer < Human
end
Human.lab # class is Human
Developer.lab # class is Developer
In this case Developer.lab says Developer and not Human.
You can also checkout this page (Link is not available) which has much more
detailed explanation of instance_exec and also emphasizes that instance_exec
does pass a new value of self .
instance_exec is so useful that ActiveSupport needs it. And since ruby 1.8.6
does not have it ActiveSupport has code to support it.
I came across instance_exec issue while resolving
#4507 rails ticket
. The final solution did not need instance_exec but I learned a bit about it.
If this blog was helpful, check out our full blog archive.