Josh Bodah
Josh Bodah

Reputation: 1243

Yielding a block to a proc (or creating a method that accepts a block from a proc that yields)

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

Answers (1)

Pierre-Louis Gottfrois
Pierre-Louis Gottfrois

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

Related Questions