Reputation: 1243
I'm currently working on an interface that allows me to wrap arbitrary method calls with a chain of procs. Without going into too much detail, I currently have an interface that accepts something like this:
class Spy
def initialize
@procs = []
end
def wrap(&block)
@procs << block
end
def execute
original_proc = Proc.new { call_original }
@procs.reduce(original_proc) do |memo, p|
Proc.new { p.call &memo }
end.call
end
def call_original
puts 'in the middle'
end
end
spy = Spy.new
spy.wrap do |&block|
puts 'hello'
block.call
end
spy.wrap do |&block|
block.call
puts 'goodbye'
end
spy.execute
What I'd like to do though is remove the |&block|
and block.call
from my API and use yield
instead.
spy.wrap do
puts 'hello'
yield
end
This didn't work and raised a LocalJumpError: no block given (yield)
error.
I've also tried creating methods by passing the proc the define_singleton_method
in the reduce, but I haven't had any luck.
def execute
original_proc = Proc.new { call_original }
@procs.reduce(original_proc) do |memo, p|
define_singleton_method :hello, &p
Proc.new { singleton_method(:hello).call(&memo) }
end.call
end
Is there another approach I can use? Is there anyway to yield
from a Proc or use the Proc to initialize something that can be yielded to?
Upvotes: 2
Views: 391
Reputation: 17631
Using yield
in your wrap block does not make much sense unless you passed a block to the caller itself:
def foo
spy.wrap do
puts "executed in wrap from foo"
yield
end
end
If you call foo
without a block it will raise the exception since yield
can't find a block to execute. But if you pass a block to foo
method then it will be invoked:
foo do
puts "foo block"
end
Will output
executed in wrap from foo
foo block
In conclusion I think you misunderstood how yield
works and I don't think it is what you want to achieve here.
Upvotes: 1