Canh Canh - 4 months ago 20
Ruby Question

Proc inside ERB

There's something weird going on here that I can not understand when I try to implement multiple

content_for
blocks in ERB using plain Ruby.

Here is my code:

# helper.rb
require 'erb'

def render(path)
ERB.new(File.read(path)).result(binding)
end

def content_for(key, &block)
content_blocks[key.to_sym] = block
end

def yield_content(key)
content_blocks[key.to_sym].call
end

def content_blocks
@content_blocks ||= Hash.new
end


And template:

<% #test.html.erb %>
<% content_for :style do %>
style
<% end %>
<% content_for :body do %>
body
<% end %>
<% content_for :script do %>
script
<% end %>


When I open irb to test, I get

irb(main):001:0> require './helper'
=> true
irb(main):002:0> render 'test.html.erb'
=> "\n\n\n"
irb(main):003:0> content_blocks
=> {:style=>#<Proc:0x005645a6de2b18@(erb):2>, :body=>#<Proc:0x005645a6de2aa0@(erb):5>, :script=>#<Proc:0x005645a6de2a28@(erb):8>}
irb(main):004:0> yield_content :script
=> "\n\n\n\n script\n"
irb(main):005:0> yield_content :style
=> "\n\n\n\n script\n\n style\n"


Why
yield_content :script
got the
\n\n\n
prepended and why
yield_content :style
got the
script\n\n
in result.

Answer

If you do

ERB.new(File.read(path)).src

Then you can see what erb compiles your template to. The template ends up looking like (formatted for readability)

_erbout = ''
_erbout.concat "\n"
#test.html.erb
_erbout.concat "\n"
content_for :style do
  _erbout.concat "\n  style\n"
end

Where _erbout is the buffer that accumulates the output from your template. _erbout is just a local variable,m

When you call your procs they are all just appending to that same buffer, which already contains the result from the template render.