Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Evaluate a block in a certain scope and pass an argument

Tags:

ruby

block

For example, let's say I have

block = proc { |n| "#{downcase} #{n}" }

Now I want to evaluate that block in the scope of a String, but pass that block a variable. I know how to do the first part:

"Foo".instance_eval(&block)

But how to also pass a variable to that block?

I tried

"Foo".instance_eval { block.call(3) }

But it didn't work, it wasn't in the scope of the String.

like image 471
janko-m Avatar asked Sep 02 '12 20:09

janko-m


Video Answer


1 Answers

Use instance_exec instead:

instance_exec(arg...) {|var...| block } → obj

Executes the given block within the context of the receiver (obj). In order to set the context, the variable self is set to obj while the code is executing, giving the code access to obj’s instance variables. Arguments are passed as block parameters.

So this will make it go:

"Foo".instance_exec(3, &block)

and give you the 'foo 3' that you desire.

The problem with this:

"Foo".instance_eval { block.call(3) }

is that self will be "Foo" inside { block.call(3) } but not inside block, block will retain whatever self was when block was defined; there's nothing in block.call(3) that forces a context so self doesn't change. For example, given this:

class C
  def m
    proc { |n| "#{downcase} #{n}" }
  end
end
c = C.new
"Foo".instance_eval { c.m.call(3) }

When the proc is called, self will be c because that's what self was when the proc was defined (i.e. when m was called). What self is inside the instance_eval block doesn't have any effect on self inside the proc.

like image 144
mu is too short Avatar answered Nov 14 '22 20:11

mu is too short