Для простого решения используйте 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
Приведенный выше код достаточно прост, но имеет (как минимум) две проблемы: 1) Так как он опирается на OpenStruct
, доступ к несуществующей переменной возвращает nil
, в то время как вы, вероятно, предпочли бы, чтобы она шумно провалилась , 2) binding
вызывается внутри блока, вот и все, в замыкании, поэтому он включает все локальные переменные в области видимости (на самом деле эти переменные будут скрывать атрибуты структуры!).
Итак, вот другое решение, более подробное, но без каких-либо из этих проблем:
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
Конечно, если вы собираетесь использовать это часто, убедитесь, что вы создали расширение String#erb
, которое позволяет писать что-то вроде "x=<%= x %>, y=<%= y %>".erb(x: 1, y: 2)
.