How to change self in a block like instance_eval method do?
Asked Answered
F

2

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.

Facture answered 27/2, 2012 at 6:9 Comment(2)
What to do you want to achieve?Kienan
I don't think it's possible to do what you want. Probably because it'd be too confusing if you could change self yourself.Gustative
T
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>
Thermoluminescence answered 27/2, 2012 at 7:30 Comment(5)
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!Facture
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.Thermoluminescence
Actualy, I see that RSpec doesn't pass params to block, and it uses instance_eval to call blocks. See rubydoc.info/gems/rspec-core/RSpec/Core/… and click "View Source".Thermoluminescence
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.Facture
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 (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.Thermoluminescence
K
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
Kienan answered 27/2, 2012 at 7:13 Comment(1)
Sorry, what i want is a method which takes a block and in that block self is changed to receiver, just like instance_eval!Facture

© 2022 - 2024 — McMap. All rights reserved.