Search code examples
rubyyield

How to wrap a method that yields in Ruby 1.9


I have a method that prints out a numbered list, yielding to a code block to print a prefix.

arr = %w(a b c)

def print_lines(array)
  array.each_with_index do |item, index|
    prefix = yield index
    puts "#{prefix} #{item}"
  end
end

print_lines(arr) do |index|
  "(#{index})"
end

This produces the following output:

(0) a
(1) b
(2) c

Now I want to wrap print_lines in another method and call it.

def print_lines_wrapped(array)
  puts 'print_lines_wrapped'
  print_lines(array)
end

print_lines_wrapped(arr) do |index|
  "(#{index})"
end

However, this gives me a LocalJumpError

test_yield.rb:5:in `block in print_lines': no block given (yield) (LocalJumpError)
  from test_yield.rb:4:in `each'
  from test_yield.rb:4:in `each_with_index'
  from test_yield.rb:4:in `print_lines'
  from test_yield.rb:16:in `print_lines_wrapped'
  from test_yield.rb:19:in `<main>'

Why do I get a LocalJumpError?

How can I implement print_lines_wrapped such that I can call it like this:

print_lines_wrapped(arr) do |index|
  "(#{index})"
end

and get the following output:

print_lines_wrapped
(0) a
(1) b
(2) c

?


Solution

  • Your wrapper method also has to accept a block and pass it to the wrapped method. There is no implicit passing of the block:

    def print_lines_wrapped(array, &block)
      puts 'print_lines_wrapped'
      print_lines(array, &block)
    end
    

    Example:

    def asdf(&block) puts yield(2) end
    def qwer(&block)
      puts "I am going to call asdf"
      asdf &block
    end
    
    asdf { |x| x * 3 }
    6
    => nil
    qwer { |x| x * 5 }
    I am going to call asdf
    10
    => nil
    

    The & operator converts its operand into a block if possible

     qwer &Proc.new { |x| x * 2 }
     I am going to call asdf
     4