Ruby ERB - 创建一个 content_for 方法

Ruby ERB - Create a content_for method

我目前正在为 gem 开发 ERB 视图 class。有了这个 class 我想为 ERB 模板提供一些辅助方法。
h(string) 这样的基本助手没问题。我发现 erbh gem 帮助我更多地了解上下文的工作原理。
但现在我正在尝试创建一个 content_for 方法,就像 Rails 或 Sinatra 中的方法一样。

我第一次使用简单的 Proc 来捕获视图块,然后调用 call 方法来打印它。刚开始的时候还可以。
但是看完之后我看到wired thinks,有些内容打印了很多次。
所以我看了一下 Sinatra ContentFor helper 以了解他们是如何做到的,并且我复制了这个助手的一些方法。我没有错误,但是块 return 总是空的......我真的不知道为什么。

我对 ERB 的了解还不足以了解 ERB 缓冲的工作原理。

代码

这是解释我的代码状态的要点。我从我的库中提取了代码并稍微简化了它。

https://gist.github.com/nicolas-brousse/ac7f5454a1a45bae30c52dae826d587f/66cf76c97c35a02fc6bf4a3bc13d8d3b587356de

我想要什么?

我只是想让 content_for 方法像 Rails 和 Sinatra 那样工作。

谢谢!

读完这篇 blog article 我终于找到了为什么它不起作用。我不知道我是否以最佳方式和更简洁的方式完成了它,但它确实有效。

所以错误主要来自 ERB 初始化。通过使用 属性 而不是局部变量 eoutvar 它现在可以工作了。

erb = ERB.new(str, nil, "<>", "@_erbout")

我也稍微改变了 content_for 助手使用的 capture 方法。

现在看起来像这样(gist)

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

def content_for?(key)
  content_blocks[key.to_sym].any?
end

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
end

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

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