nclbr nclbr - 1 year ago 71
Ruby Question

Ruby ERB - Create a content_for method

I'm currently working on an ERB View class for a gem. With this class I would like to have some helper methods for ERB templates.

It's okay about basic helpers like

. I found
who help me to understand more how context works.

But now I'm trying to create a
method like there is in Rails or Sinatra.

On first time I was using simple
to capture the view block and then just calling
method to print it. It was working enough at the beginning.

But after having completed views I saw wired thinks, some content are printed multiple times.

So I take a look on the Sinatra ContentFor helper to understand how they did it and I copy some methods of this helper. I have no errors, but the block return are always empty... and I don't really know why.

My knowledge about ERB are not good enough to know how ERB buffering works.


Here a gist who explain the status of my code. I extracted the code from my library and simplified it a bit.

What I would like?

I just would like to have
methods works like they do with Rails and Sinatra.


Answer Source

After reading this blog article I finally found why it wasn't working. I don't know if I did it in the best way and cleaner way but it works.

So the bug was mainly from the ERB initilization. By using a property instead a local variable as eoutvar it now works.

erb =, nil, "<>", "@_erbout")

I also change a bit the capture method who is used by content_for helper.

It looks like this now (gist)

def content_for(key, content = nil, &block)
  block ||= proc { |*| content }
  content_blocks[key.to_sym] << capture_later(&block)

def content_for?(key)

def yield_content(key, default = nil)
  return default if content_blocks[key.to_sym].empty?
  content_blocks[key.to_sym].map { |b| capture(&b) }.join

def capture(&block)
  @capture = nil
  @_erbout, _buf_was = '', @_erbout
  result = yield
  @_erbout = _buf_was
  result.strip.empty? && @capture ? @capture : result

def capture_later(&block)
  proc { |*| @capture = capture(&block) }