Search code examples
rubyblock

Wrapping a block in method calls that accept blocks by name


I have a number of methods that I call like this:

with_this do
  with_that do
    and_in_this_context do
      yield
    end
  end
end

I remember there was a trick to recursively wrap such a block call. How do I write a method that does block wrapping for me?

def in_nested_contexts(&blk)
  contexts = [:with_this, :with_that, :and_in_this_context]
  # ... magic probably involving inject
end

Solution

  • You can indeed use inject to created nested lambdas or procs, which you can call at the end. You need your given block to be the interior of the nest, so you reverse your array and use that block as the initial value, then wrap each successive function around the result from inject:

    def in_nested_contexts(&blk)
      [:with_this, :with_that, :and_in_this_context].reverse.inject(blk) {|block, symbol|
        ->{ send symbol, &block }
      }.call
    end
    

    If you wrap your with_this, et al methods with before and after puts statements, you can see this in action:

    in_nested_contexts { puts "hello, world" }
    #=> 
      with_this start
      with_that start
      context start
      hello, world
      context end
      with_that end
      with_this end