So, say that I want to have a class, and I want to send a block when creating the instance of this class, and then be able to invoke that block later, by calling a method. The idiomatic way of doing this would be to use the ampersand and save away the block in an instance variable. But maybe you don't want to do this for some reason. An alternative would be to create a new singleton method that yields to the block. A first implementation might look like this:
class DoSomethingBut this code will not work. Why not? Because as I mentioned in my post about defining methods, "def" will never create a closure. Why is this important? Well, because the current block is actually part of the closure. The yield keyword will use the current frames block, and if the method is not defined as a closure, the block invoked by the yield keyword will actually be the block sent to the "call" method. Since we don't provide a block to "call", things will fail.
def initialize
def self.call
yield
end
end
end
d = DoSomething.new do
puts "hello world"
end
d.call
d.call
To fix this is quite simple. Use define_method instead:
class DoSomethingAs usual with define_method we need to open the singleton class, and use send. This will work, since the block sent to define_method is a real closure, that closes over the block sent to initialize.
def initialize
(class << self; self; end).send :define_method, :call do
yield
end
end
end
d = DoSomething.new do
puts "hello world"
end
d.call
d.call
Inga kommentarer:
Skicka en kommentar