Here is updated article on the same topic .
Following code will print 99 as the output.
1class Klass 2 def initialize 3 @secret = 99 4 end 5end 6puts Klass.new.instance_eval { @secret }
Nothing great there. However try passing a parameter to instance_eval .
1puts Klass.new.instance_eval(self) { @secret }
You will get following error.
1wrong number of arguments (1 for 0)
So instance_eval does not allow you to pass parameters to a block.
How to get around to the restriction that instance_eval does not accept parameters
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.
1class Klass 2 def initialize 3 @secret = 99 4 end 5end 6puts Klass.new.instance_exec('secret') { |t| eval"@#{t}" }
Above code works. So now we can pass parameters to block. Good.
Changing value of self
Another feature of instance_exec is that it changes the value of self. To illustrate that I need to give a longer example.
1module Kernel 2 def singleton_class 3 class << self 4 self 5 end 6 end 7end 8 9class Human 10 proc = lambda { puts 'proc says my class is ' + self.name.to_s } 11 12 singleton_class.instance_eval do 13 define_method(:lab) do 14 proc.call 15 end 16 end 17end 18 19class Developer < Human 20end 21 22Human.lab # class is Human 23Developer.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 .
1module Kernel 2 def singleton_class 3 class << self 4 self 5 end 6 end 7end 8 9class Human 10 proc = lambda { puts 'proc says my class is ' + self.name.to_s } 11 12 singleton_class.instance_eval do 13 define_method(:lab) do 14 self.instance_exec &proc 15 end 16 end 17end 18 19class Developer < Human 20end 21 22Human.lab # class is Human 23Developer.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.