Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby erb templates with yield

Tags:

yield

ruby

erb

I can't understand why this code works fine

def func
  ERB.new('<%= yield %>').result(binding)
end
func { 123 } # => it prints 123 as expected

but this one doesn't work and raises an exception

ERB.new('<%= yield %>').result(binding) { 123 } # => LocalJumpError: no block given (yield)

Any thoughts?

like image 595
user3309314 Avatar asked Sep 16 '26 22:09

user3309314


2 Answers

This problem is independent of ERB and is because of the way yield works. Yield expects to be called within the message body and expects a block to yield it. Let's take this example

# This is equivalent to 
# def func
# ERB.new('<%= yield %>').result(binding)
# end

def test_print
  yield
end

If we call the method without a block

irb(main):038:0> test_print
LocalJumpError: no block given (yield)
    from (irb):36:in `test_print'
    from (irb):38
    from /Users/agupta/.rvm/rubies/ruby-2.4.0/bin/irb:11:in `<main>'
irb(main):039:0>

If we call the method with block

irb(main):039:0> test_print { "hello world" }
=> "hello world"
irb(main):040:0>

In the latter case

ERB.new('<%= yield %>').result(binding) { 123 } 

Your block is not being passed as yield is outside the message body and you cannot do

irb(main):042:0> yield.tap { "hello world" }
LocalJumpError: no block given (yield)
    from (irb):42
    from /Users/agupta/.rvm/rubies/ruby-2.4.0/bin/irb:11:in `<main>'
irb(main):043:0>
like image 127
AnkitG Avatar answered Sep 19 '26 22:09

AnkitG


You should pass a block to the method context, where is binding called, e.g.:

def foo
  binding
end

ERB.new('<%= yield %>').result(foo { 123 })
#=> "123"

Note, that you cannot use yield outside of a method body. ERB#result just executes ruby code in context of passed binding, so binding in your should be inside the method anyway, because of yield.

like image 28
Ilya Avatar answered Sep 19 '26 22:09

Ilya