code - erb ruby syntax
Plantillas de Ruby: ¿Cómo pasar variables a ERB en línea? (9)
¡Lo tengo!
Creo una clase de enlaces
class BindMe
def initialize(key,val)
@key=key
@val=val
end
def get_binding
return binding()
end
end
y pasar una instancia a 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
El archivo de plantilla .erb se ve así:
Key: <%= @key %>
Tengo una plantilla de ERB incorporada en el código de Ruby:
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
No puedo pasar la variable "actual" en la plantilla.
El error es:
(erb):1: undefined local variable or method `current'' for main:Object (NameError)
¿Cómo puedo solucionar esto?
Como otros dijeron, para evaluar ERB con un conjunto de variables, necesita un enlace adecuado. Hay algunas soluciones para definir clases y métodos, pero creo que lo más simple y que da más control y lo más seguro es generar un enlace limpio y usarlo para analizar el ERB. Aquí está mi opinión (ruby 2.2.x):
module B
def self.clean_binding
binding
end
def self.binding_from_hash(**vars)
b = self.clean_binding
vars.each do |k, v|
b.local_variable_set k.to_sym, v
end
return b
end
end
my_nice_binding = B.binding_from_hash(a: 5, **other_opts)
result = ERB.new(template).result(my_nice_binding)
Creo que con eval
y sin **
mismo se puede hacer trabajando con ruby más antiguo que 2.1
En el código de la pregunta original, simplemente reemplace
result = template.result
con
result = template.result(binding)
Eso usará el contexto de cada bloque en lugar del contexto de nivel superior.
(Acabo de extraer el comentario de @sciurus como respuesta porque es el más corto y el más correcto).
Este artículo explica esto muy bien.
http://www.garethrees.co.uk/2014/01/12/create-a-template-rendering-class-with-erb/
No puedo darle una muy buena respuesta sobre por qué sucede esto porque no estoy 100% seguro de cómo funciona ERB, pero solo mirando los RDocs de ERB , dice que necesita un binding
que sea a Binding or Proc object which is used to set the context of code evaluation.
Probar el código anterior de nuevo y simplemente reemplazar result = template.result
con result = template.result(binding)
hizo funcionar.
Estoy seguro / espero que alguien salte aquí y brinde una explicación más detallada de lo que está sucediendo. Aclamaciones.
EDIT: para obtener más información sobre Binding
y hacer todo esto un poco más claro (al menos para mí), consulte el Binding RDoc .
Para una solución simple, use 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
El código anterior es bastante simple pero tiene (al menos) dos problemas: 1) Dado que se basa en OpenStruct
, el acceso a una variable no existente devuelve OpenStruct
, mientras que probablemente prefiera que haya fallado ruidosamente. 2) el binding
se llama dentro de un bloque, eso es todo, en un cierre, por lo que incluye todas las variables locales en el alcance (de hecho, estas variables sombrearán los atributos de la estructura!).
Aquí hay otra solución, más detallada, pero sin ninguno de estos problemas:
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
Por supuesto, si vas a usar esto a menudo, asegúrate de crear una extensión String#erb
que te permita escribir algo como "x=<%= x %>, y=<%= y %>".erb(x: 1, y: 2)
.
Solución simple usando Binding :
b = binding
b.local_variable_set(:a, ''a'')
b.local_variable_set(:b, ''b'')
ERB.new(template).result(b)
EDITAR : Esta es una solución sucia. Por favor mira mi otra respuesta.
Es totalmente extraño, pero agregar
current = ""
antes de que el ciclo "for-each" solucione el problema.
Dios bendiga los lenguajes de scripting y sus "características del lenguaje" ...
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
REF: http://stoneship.org/essays/erb-and-the-context-object/