如何创建新的绑定并为其分配实例变量以在 ERB 中可用?

How to create new binding and assign instance variables to it for availability in ERB?

我正在 Ruby 项目 (non-rails) 中实施 HTML 模板。为此,我将使用 ERB,但我对绑定的内容有些担忧。

首先,这是我目前得到的方法:

def self.template(template, data)
  template = File.read("#{ENV.root}/app/templates/#{template}.html.erb")

  template_binding = binding.clone

  data.each do |k, v|
    template_binding.local_variable_set(k, v)
  end

  ERB.new(template).result(template_binding)
end

我就这么叫

Email.template('email/hello', {
  name: 'Bill',
  age:  41
}

虽然当前的解决方案有两个问题。

首先,我正在克隆当前绑定。我想创建一个新的。我尝试 Class.new.binding 创建一个新的,但由于 binding 是一个私有方法,因此无法通过这种方式获得。 我想要一个新的原因是我想避免实例变量泄漏到 ERB 文件中或从中泄漏出来的风险(克隆只处理后一种情况)。

其次,我想让传给ERB文件的变量暴露为实例变量。在这里,我尝试使用 template_binding.instance_variable_set,传递普通散列键 k,它抱怨它不是一个有效的实例变量名称和 "@#{k}",它没有抱怨但也没有可用在 ERB 代码中。 我想使用实例变量的原因是依赖此代码的人熟悉它。

我在 Stack Overflow 上查看了一些主题,例如 Render an ERB template with values from a hash,但提供的答案并未解决我正在讨论的问题。

简而言之,就像标题一样:如何创建新的绑定并为其分配实例变量以便在 ERB 中可用?

1) 无需克隆,每次都会为您创建新的绑定。

我已经在 irb 中测试过了:

class A; def bind; binding; end; end
a = A.new
bind_1 = a.bind
bind_2 = a.bind

bind_1.local_variable_set(:x, 2)
=> 2
bind_1.local_variables
=> [:x]
bind_2.local_variables
=> []

2) 打开对象 Eigenclass 并向其添加 attr_accessor

class << template_binding  # this opens Eigenclass for object template_binding
  attr_accessor :x
end

所以在 ruby 中,您可以打开任何 class 并为其添加方法。 Eigenclass 表示单个对象的 class - 每个对象都可以有自定义的 class 定义。来自 C#,直到现在我无法想象会使用它的情况。 :)

对每个哈希执行此操作

data.each do |k, v|
  class << template_binding; attr_accessor k.to_sym; end
  template_binding.k = v
end