I'm trying to make the following work but I'm obviously missing something:
class Person
def fetch
puts 'Fetch it boy!'
end
def action(data)
data.call
end
end
class Animal
def play
Person.new.action(proc { fetch })
end
end
Animal.new.play # undefined local variable or method `fetch' for #<Animal:0x0000000001b172d0>
My goal is to get Person
execute a method Animal
has chosen. I've also tried using instance_exec
but to no avail.
Now I know I can use eval
and just pass the method name as a string but I'd like to avoid that.
Any ideas?
Thanks in advance
Disclaimer (of sorts):
Please don't propose code restructuring or rewrites. The purpose of the question is to better understand how blocks behave when passed around between objects.
You are looking for:
instance_eval(&data)
object.instance_eval
evaluates block, but replaces self
within that block (which would normally be self
of the context block was created in) with object
:
whoami = proc { self }
whoami.call => main
1.instance_eval(&whoami) => 1
Note however, that instance_eval
also passes an object as an argument to the block, which might be problematic if you want to pass a lambda:
whoami = lambda { self }
1.instance_eval(&whoami) #=> ArgumentError (wrong number of arguments (given 1, expected 0))
There is another similar method: instance_exec
. Not only it does not pass self as an argument:
whoami = lambda { self }
1.instance_exec(&whoami) #=> 1
but it additionally allows you to pass other arguments:
add_number = lambda { |number| self + number }
1.instance_exec(3, &add_number) #=> 4
Naturally, both methods need to be used with extra caution and very sparingly - they are nice when you want to declare class-wide hooks in a declarative manner to be executed on each instance. They should not be used as a mean of interaction between two objects, unless you really know what you are ding and can justify it does not validate encapsulation.