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
Optimization for Finding Perfect-Square Algorithm
How to Specify Local .Gem Files in My Gemfile
Rails Active Record Find(:All, :Order => ) Issue
Check Whether a String Contains One of Multiple Substrings
How to Redefine a Ruby Constant Without Warning
Are There Good Reasons for 'Private' to Work the Way It Does in Ruby
Ruby Greed Koan - How to Improve My If/Then Soup
Modifying Database Ids from Rails Console
Can't Install Ruby-Debug-Base19X Gem
Where to Put Common Code Found in Multiple Models
Open_Id_Authentication - "Openidauthentication.Store Is Nil. Using In-Memory Store." Problem
Good Cucumber Examples in the Wild
Ruby Method Calls Declared in Class Body
How to Check the Gem Version in Ruby at Runtime
Relative Path to Your Project Directory
Ruby 1.9, Force_Encoding, But Check