9

instance_eval method change self in its block, eg:

class D; end
d = D.new
d.instance_eval do
  puts self  # print something like #<D:0x8a6d9f4>, not 'main'!
end

If we define a method ourself(or any other methods(other than instance_eval) which takes a block), when print self, we will get 'main', which is different from instance_eval method.eg:

[1].each do |e|
  puts self  # print 'main'
end

How can i define a method(which takes a block) like instance_eval? Thanks in advance.

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
gaols
  • 341
  • 5
  • 13

2 Answers2

10

You can write a method that accepts a proc argument, and then pass that as a proc argument to instance_eval.

class Foo
  def bar(&b)
    # Do something here first.
    instance_eval &b
    # Do something else here afterward, call it again, etc.
  end
end

Foo.new.bar { puts self }

Yields

#<Foo:0x100329f00>
Steve Jorgensen
  • 11,725
  • 1
  • 33
  • 43
  • 1
    I understand what you mean, but not solved my problem. i want to define a method like instance_eval instead of using instance_eval. I want implementation not a wrapper.Thank you all the same! – gaols Feb 27 '12 at 07:58
  • 2
    You'll need to be more specific about what you want to achieve, then. If you simply want a method that can accept a block, and then execute it one or more times within the the same context as your method, then this does that. If I had to guess, perhaps you want to pass arguments to the block as well? I know that's possible because RSpec does it, and it's true that this example does not. I should take a look at the RSpec code, and see how they do it. – Steve Jorgensen Feb 27 '12 at 08:17
  • Actualy, I see that RSpec doesn't pass params to block, and it uses instance_eval to call blocks. See http://rubydoc.info/gems/rspec-core/RSpec/Core/Example#run-instance_method and click "View Source". – Steve Jorgensen Feb 27 '12 at 08:29
  • I'm a Chinese, i can't express myself well in English! Actually i want to know the implementation detail of instance_eval, may be it cannot implemented by pure ruby, maybe only C can achieve the goal(change a block context in which it is evaluated).I will close the question soon if there is no appropriate answers. – gaols Feb 27 '12 at 09:36
  • 2
    I still can't see why it's more useful to re-implement instance_eval than to simply obtain its behavior by invoking it. If you want to see how anything in Ruby is implemented, a good place to start is at apidock.com/ruby. Find the method you're interested in (http://apidock.com/ruby/Object/instance_eval in this case), and click "Show Source". Sure enough, this method's implementation is in C, so you'll need to download source code for Ruby to explore further. – Steve Jorgensen Feb 27 '12 at 10:00
3

It's obvious:

class Object
  def your_method(*args, &block)
    instance_eval &block
  end
end

receiver = Object.new

receiver.your_method do
  puts self  #=> it will print the self of receiver
end
megas
  • 21,401
  • 12
  • 79
  • 130
  • Sorry, what i want is a method which takes a block and in that block self is changed to receiver, just like instance_eval! – gaols Feb 27 '12 at 07:54