Ruby模板:如何将变量传递给内联ERB?

iva*_*off 53 ruby syntax templates erb

我有一个内联到Ruby代码的ERB模板:

require 'erb'

DATA = {
    :a => "HELLO",
    :b => "WORLD",
}

template = ERB.new <<-EOF
    current key is: <%= current %>
    current value is: <%= DATA[current] %>
EOF

DATA.keys.each do |current|
    result = template.result
    outputFile = File.new(current.to_s,File::CREAT|File::TRUNC|File::RDWR)
    outputFile.write(result)
    outputFile.close
end
Run Code Online (Sandbox Code Playgroud)

我无法将变量"current"传递给模板.

错误是:

(erb):1: undefined local variable or method `current' for main:Object (NameError)
Run Code Online (Sandbox Code Playgroud)

我该如何解决?

tok*_*and 61

对于简单的解决方案,请使用OpenStruct:

require 'erb'
require 'ostruct'
namespace = OpenStruct.new(name: 'Joan', last: 'Maragall')
template = 'Name: <%= name %> <%= last %>'
result = ERB.new(template).result(namespace.instance_eval { binding })
#=> Name: Joan Maragall
Run Code Online (Sandbox Code Playgroud)

上面的代码很简单,但有(至少)两个问题:1)由于它依赖于OpenStruct,对一个不存在的变量的访问返回,nil而你可能更喜欢它失败了.2)binding在一个块中调用,就是它,在一个闭包中,所以它包含了作用域中的所有局部变量(实际上,这些变量将遮蔽struct的属性!).

所以这是另一个解决方案,更详细,但没有任何这些问题:

class Namespace
  def initialize(hash)
    hash.each do |key, value|
      singleton_class.send(:define_method, key) { value }
    end 
  end

  def get_binding
    binding
  end
end

template = 'Name: <%= name %> <%= last %>'
ns = Namespace.new(name: 'Joan', last: 'Maragall')
ERB.new(template).result(ns.get_binding)
#=> Name: Joan Maragall
Run Code Online (Sandbox Code Playgroud)

当然,如果您要经常使用它,请确保创建一个String#erb允许您编写类似内容的扩展名"x=<%= x %>, y=<%= y %>".erb(x: 1, y: 2).


asf*_*fer 25

使用绑定的简单解决方案:

b = binding
b.local_variable_set(:a, 'a')
b.local_variable_set(:b, 'b')
ERB.new(template).result(b)
Run Code Online (Sandbox Code Playgroud)

  • `local_variable_set` 是在 ruby​​ 2.1 中引入的。 (2认同)

iva*_*off 10

得到它了!

我创建了一个绑定类

class BindMe
    def initialize(key,val)
        @key=key
        @val=val
    end
    def get_binding
        return binding()
    end
end
Run Code Online (Sandbox Code Playgroud)

并将实例传递给ERB

dataHash.keys.each do |current|
    key = current.to_s
    val = dataHash[key]

    # here, I pass the bindings instance to ERB
    bindMe = BindMe.new(key,val)

    result = template.result(bindMe.get_binding)

    # unnecessary code goes here
end
Run Code Online (Sandbox Code Playgroud)

.erb模板文件如下所示:

Key: <%= @key %>
Run Code Online (Sandbox Code Playgroud)

  • 这是不必要的.在原始问题的代码中,只需将"result = template.result"替换为"result = template.result(binding)",即使用每个块的上下文而不是顶层上下文. (9认同)

gee*_*ekQ 8

在原始问题的代码中,只需替换

result = template.result
Run Code Online (Sandbox Code Playgroud)

result = template.result(binding)
Run Code Online (Sandbox Code Playgroud)

这将使用每个块的上下文而不是顶级上下文.

(刚刚将@sciurus的评论作为答案提取出来,因为它是最短且最正确的评论.)


小智 6

require 'erb'

class ERBContext
  def initialize(hash)
    hash.each_pair do |key, value|
      instance_variable_set('@' + key.to_s, value)
    end
  end

  def get_binding
    binding
  end
end

class String
  def erb(assigns={})
    ERB.new(self).result(ERBContext.new(assigns).get_binding)
  end
end
Run Code Online (Sandbox Code Playgroud)

REF:http://stoneship.org/essays/erb-and-the-context-object/