Instance_Eval Block Not Supplied

Instance_eval does not work with do/end block, only with {}-blocks

It's because when you pass block with curly braces, it is passed to instance_eval method. But if you pass it with do-end, it's passed to puts method, so instance_eval doesn't get block and raises an error.

Why does instance_eval succeed with a Proc but not with a Lambda?

instance_eval is yielding self (User) to the lambda. Lambdas are particular about their parameters - in the same way methods are - and will raise an ArgumentError if there are too few/many.

class User
code1 = Proc.new { |x| x == User } # true
code2 = lambda { |x| x == User } # true

define_method :test do
self.class.instance_eval &code1
self.class.instance_eval &code2
end
end

Relevant: What's the difference between a proc and a lambda in Ruby?

Instance_eval does not work with do/end block, only with {}-blocks

It's because when you pass block with curly braces, it is passed to instance_eval method. But if you pass it with do-end, it's passed to puts method, so instance_eval doesn't get block and raises an error.

Block not called in Ruby

Add parentheses to puts

puts(m do
x = 2
y = 3
x * y
end)

The output is 6.

Your code is equivalent to

puts(m) do 
x = 2
y = 3
x * y
end

Why does Ruby evaluate these two expressions differently? The only difference is {...} and do...end

Precedence matters. {} has a higher precedence over method call. do-end has a lower precedence.

pp [1, 2, 3].map { |r| r + 1 }

is parsed as pp ([1, 2, 3].map { |r| r + 1 }), which is:

pp([1, 2, 3].map { |r| r + 1 })

pp [1, 2, 3].map do |r| r + 1 end

is parsed as (pp [1, 2, 3].map) do |r| r + 1 end, which is:

pp([1, 2, 3].map, &->(r){ r + 1 })

In the latter case passing block to pp is a NOOP.

Is 'yield self' the same as instance_eval?

Your two pieces of code do very different things. By using instance_eval you're evaluating the block in the context of your object. This means that using def will define methods on that object. It also means that calling a method without a receiver inside the block will call it on your object.

When yielding self you're passing self as an argument to the block, but since your block doesn't take any arguments, it is simply ignored. So in this case yielding self does the same thing as yielding nothing. The def here behaves exactly like a def outside the block would, yielding self does not actually change what you define the method on. What you could do is:

class Foo
def initialize
yield self if block_given?
end
end
x = Foo.new {|obj| def obj.foo() 'foo' end}
x.foo

The difference to instance_eval being that you have to specify the receiver explicitly.

Edit to clarify:

In the version with yield, obj in the block will be the object that is yielded, which in this case is is the newly created Foo instance. While self will have the same value it had outside the block. With the instance_eval version self inside the block will be the newly created Foo instance.



Related Topics



Leave a reply



Submit