Canh Canh - 1 year ago 64
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 Source

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.