2012-01-21 12 views
48

Debo pasar por alto algo muy simple aquí, pero parece que no puedo entender cómo renderizar una plantilla simple de ERB con los valores de un hash-map.Renderizar una plantilla ERB con valores de un hash

Soy relativamente nuevo en ruby, viniendo de python. Tengo una plantilla ERB (no HTML), que necesito renderizar con el contexto que se tomará de un hash-map, que recibo de una fuente externa.

Sin embargo, la documentación de ERB, establece que el método ERB.result toma una binding. Aprendí que son algo que mantienen los contextos variables en ruby ​​(algo así como locals() y globals() en python, supongo?). Pero, no sé cómo puedo construir un objeto vinculante fuera de mi hash-map.

un poco (un montón , en realidad) me dio esta buscando en Google: http://refactormycode.com/codes/281-given-a-hash-of-variables-render-an-erb-template, que utiliza un poco de magia rubí metaprogramming que se me escapa.

Entonces, ¿no hay una solución simple a este problema? ¿O hay un mejor motor de plantillas (no relacionado con HTML) más adecuado para esto? (Solo elegí ERB porque está en el stdlib).

+0

No sé de ningún motor de plantilla Ruby que están "atados" a HTML; una plantilla es una plantilla. Además, no estoy seguro de qué hay de malo con la solución a la que se vincula: ¿se trata de obtener el hash en el módulo? –

+0

Dave, nada está mal con eso como tal. Solo que pensé que podría haber una solución más elegante para un problema tan simple que ir al punto de utilizar la metaprogramación. –

+1

esto? http: // stackoverflow.com/questions/1338960/ruby-templates-how-to-pass-variables-into-inlined-erb – tokland

Respuesta

52

No sé si esto califica como "más elegante" o no:

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 %>') 

o de un método de la clase:

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 ha Erb, T para la plantilla, y suena como "té de hierbas" Designación de cosas es difícil)

+1

Gracias Dave. Sí, esta solución se ve un poco mejor, aunque no tengo ni idea sobre 'OpenStruct'. Iré a los documentos, no hay problema :) –

+1

@ShrikantSharat en realidad es más o menos lo mismo, pero es el sistema operativo el que hace el meta. –

3

Si quiere hacer las cosas de manera muy simple, siempre puede usar búsquedas explícitas de hash dentro de la plantilla de ERB. Digamos que utiliza "vinculante" para pasar una variable de hash llamado "control" en la plantilla, que se vería así:

<%= hash["key"] %> 
+0

¿Pero cómo se usa 'binding' para pasar la variable hash? –

+0

Fácil. Solo almacena el hash en una variable. Luego, en el mismo ámbito que la variable, llame a 'ERB.new (template) .result (binding) '. 'Kernel # binding' capturará todas las variables que están dentro del alcance, y estarán disponibles dentro de la plantilla ERB. –

+2

Gracias - esta información pertenece más a su respuesta principal que a un comentario. Pero, ¿no adolece este enfoque de las mismas preocupaciones de fuga de datos/seguridad que he señalado anteriormente que la respuesta basada en 'instance_eval' sufre? –

31

Si puede utilizar Erubis usted tiene esta funcionalidad ya:

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" 
+0

Gracias por esta sugerencia, Phrongz. Pero creo que me quedaré con stdlib por este tiempo. –

+5

Esto es útil ya que Rails 4 usa ahora Erubis para ERB para su motor de renderizado. – Unixmonkey

+0

para aquellos que están utilizando Rails, este parece el mejor –

66
require 'erb' 
require 'ostruct' 

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

por ejemplo

1.9.2p290 :008 > erb("Hey, <%= first_name %> <%= last_name %>", :first_name => "James", :last_name => "Moriarty") 
=> "Hey, James Moriarty" 
+3

+1, esa es una manera súper elegante de crear un enlace a partir de un hash. – orip

+11

Se ve bien al principio, pero lamentablemente el enlace también hereda los locales y los métodos del contexto en el que se crea una instancia del 'OpenStruct'. Esto le da a la plantilla acceso mucho más de lo previsto, y podría generar errores o incluso una vulnerabilidad de seguridad, p. https://gist.github.com/aspiers/ad6549058ee423819976 –

6

solución simple usando Binding:

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

La parte difícil aquí no es contaminar el enlace con variables locales redundantes (como yo n respuestas Más mensajes):

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) 
4

Rubí 2.5 tiene ERB#result_with_hash que proporciona esta funcionalidad:

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

Esta respuesta es la mejor respuesta. El rubí más reciente lo hace mucho más fácil que usar un enlace. – BeepDog

Cuestiones relacionadas