Render an ERB template with values from a hash

让人想犯罪 __ 提交于 2019-11-28 15:56:35

问题


I must be overlooking something very simple here but I can't seem to figure out how to render a simple ERB template with values from a hash-map.

I am relatively new to ruby, coming from python. I have an ERB template (not HTML), which I need rendered with context that's to be taken from a hash-map, which I receive from an external source.

However, the documentation of ERB, states that the ERB.result method takes a binding. I learnt that they are something that hold the variable contexts in ruby (something like locals() and globals() in python, I presume?). But, I don't know how I can build a binding object out of my hash-map.

A little (a lot, actually) googling gave me this: http://refactormycode.com/codes/281-given-a-hash-of-variables-render-an-erb-template, which uses some ruby metaprogramming magic that escapes me.

So, isn't there a simple solution to this problem? Or is there a better templating engine (not tied to HTML) better suited for this? (I only chose ERB because its in the stdlib).


回答1:


I don't know if this qualifies as "more elegant" or not:

require 'erb'
require 'ostruct'

class ErbalT < OpenStruct
  def render(template)
    ERB.new(template).result(binding)
  end
end

et = ErbalT.new({ :first => 'Mislav', 'last' => 'Marohnic' })
puts et.render('Name: <%= first %> <%= last %>')

Or from a class method:

class ErbalT < OpenStruct
  def self.render_from_hash(t, h)
    ErbalT.new(h).render(t)
  end

  def render(template)
    ERB.new(template).result(binding)
  end
end

template = 'Name: <%= first %> <%= last %>'
vars = { :first => 'Mislav', 'last' => 'Marohnic' }
puts ErbalT::render_from_hash(template, vars)

(ErbalT has Erb, T for template, and sounds like "herbal tea". Naming things is hard.)




回答2:


require 'erb'
require 'ostruct'

def render(template, vars)
  ERB.new(template).result(OpenStruct.new(vars).instance_eval { binding })
end

e.g

render("Hey, <%= first_name %> <%= last_name %>", first_name: "James", last_name: "Moriarty")
# => "Hey, James Moriarty" 

Update:

A simple example without ERB:

def render(template, vars)
  eval template, OpenStruct.new(vars).instance_eval { binding }
end

e.g.

render '"Hey, #{first_name} #{last_name}"', first_name: "James", last_name: "Moriarty"
# => "Hey, James Moriarty

Update 2: checkout @adam-spiers comment below.




回答3:


If you can use Erubis you have this functionality already:

irb(main):001:0> require 'erubis'
#=> true
irb(main):002:0> locals = { first:'Gavin', last:'Kistner' }
#=> {:first=>"Gavin", :last=>"Kistner"}
irb(main):003:0> Erubis::Eruby.new("I am <%=first%> <%=last%>").result(locals)
#=> "I am Gavin Kistner"



回答4:


Ruby 2.5 has ERB#result_with_hash which provides this functionality:

$ ruby -rerb -e 'p ERB.new("Hi <%= name %>").result_with_hash(name: "Tom")'
"Hi Tom"



回答5:


The tricky part here is not to pollute binding with redundant local variables (like in top-rated answers):

require 'erb'

class TemplateRenderer
  def self.empty_binding
    binding
  end

  def self.render(template_content, locals = {})
    b = empty_binding
    locals.each { |k, v| b.local_variable_set(k, v) }

    # puts b.local_variable_defined?(:template_content) #=> false

    ERB.new(template_content).result(b)
  end
end

# use it
TemplateRenderer.render('<%= x %> <%= y %>', x: 1, y: 2) #=> "1 2"

# use it 2: read template from file
TemplateRenderer.render(File.read('my_template.erb'), x: 1, y: 2)



回答6:


Simple solution using Binding:

b = binding
b.local_variable_set(:a, 'a')
b.local_variable_set(:b, 'b')
ERB.new(template).result(b)



回答7:


If you want to do things very simply, you can always just use explicit hash lookups inside the ERB template. Say you use "binding" to pass a hash variable called "hash" into the template, it would look like this:

<%= hash["key"] %>


来源:https://stackoverflow.com/questions/8954706/render-an-erb-template-with-values-from-a-hash

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!