user2012677
user2012677

Reputation: 5745

Ruby: LocalJumpError

Why am I getting an error:

ERROR:

LocalJumpError
# ~> no block given (yield)

CODE:

module M
  def hello(text = 'bba')
    puts "yo-#{text}"      # => nil
  end                      # => :hello

  instance_methods  # => [:hello]

  m = instance_method(:hello)  # => #<UnboundMethod: M#hello>

  define_method(:bye) do |*args, &block|  
    yield                                 # ~> LocalJumpError: no block given (yield)
    m.bind(self).(*args, &block)
  end                                     # => :bye

end  # => :bye

class A
  include M  # => A
end          # => A


A.new.hello('vv')       # => nil
A.new.bye('zz') do |p|  # => #<A:0x00007fa8c401e090>
  puts "ggg"
end

# >> yo-vv

# ~> LocalJumpError
# ~> no block given (yield)

Upvotes: 1

Views: 179

Answers (1)

Amadan
Amadan

Reputation: 198324

It's the difference in the semantics of def and define_method. See this:

module M
  def outer(&block)
    puts "outer: #{yield}"

    def inner1
      puts "inner1: #{yield}"
    end

    M.define_method(:inner2) do
      puts "inner2: #{yield}"
    end

    M.define_method(:inner3) do |&block|
      puts "inner3: #{block.call}"
    end

    inner1 { 1 }
    inner2 { 2 }
    inner3 { 3 }
  end
end

class A
  include M
end

A.new.outer { 0 }
# => outer: 0
#    inner1: 1
#    inner2: 0 (!!!)
#    inner3: 3

yield only works inside def.

Thus, inner1 calls its own block; but inner2 uses the block of the def it is in. The correct way to invoke the block inside define_method is to capture it in the parameter list (as you did), and then use #call or #[] on it, like inner3 demonstrates.

In your code, there is no def around, thus no block is available when you yield. You can use the above method, and replace yield with block.call.

Upvotes: 4

Related Questions