modelos de Ruby: Como passar variáveis ​​em ERB inlined?

votos
51

Eu tenho um modelo de ERB inlined em código 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

Eu não posso passar a variável corrente para o modelo.

O erro é:

(erb):1: undefined local variable or method `current' for main:Object (NameError)

Como faço para corrigir isso?

Publicado 27/08/2009 em 06:08
fonte usuário
Em outras línguas...                            


9 respostas

votos
57

Para uma solução simples, 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

O código acima é bastante simples, mas tem (pelo menos) dois problemas: 1) Uma vez que se baseia em OpenStruct, um acesso a uma retornos variáveis não-existentes nil, enquanto você provavelmente preferem que falhou estrondosamente. 2) bindingé chamado dentro de um bloco, é isso, em um fechamento, assim que inclui todas as variáveis locais no escopo (na verdade, essas variáveis vai apagar os atributos do struct!).

Então aqui é uma outra solução, mais detalhado, mas sem qualquer um destes 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

Claro, se você estiver indo para usar isso muitas vezes, certifique-se de criar uma String#erbextensão que permite que você escreva algo como "x=<%= x %>, y=<%= y %>".erb(x: 1, y: 2).

Respondeu 28/03/2011 em 17:27
fonte usuário

votos
20

Solução simples usando Binding :

b = binding
b.local_variable_set(:a, 'a')
b.local_variable_set(:b, 'b')
ERB.new(template).result(b)
Respondeu 18/01/2015 em 02:53
fonte usuário

votos
10

Consegui!

I criar uma classe de ligações

class BindMe
    def initialize(key,val)
        @key=key
        @val=val
    end
    def get_binding
        return binding()
    end
end

e passar uma instância de 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

O arquivo de modelo .erb parece com isso:

Key: <%= @key %>
Respondeu 27/08/2009 em 14:35
fonte usuário

votos
5

No código da pergunta original, basta substituir

result = template.result

com

result = template.result(binding)

Que usará o contexto de cada bloco em vez do contexto de nível superior.

(Just extraído o comentário de @sciurus como resposta, porque ele é o único mais curto e mais correto.)

Respondeu 06/01/2016 em 18:32
fonte usuário

votos
5
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/

Respondeu 08/01/2014 em 01:25
fonte usuário

votos
4

Eu não posso lhe dar uma resposta muito boa de por que isso está acontecendo porque eu não estou 100% certo como ERB funciona, mas apenas olhando para a ERB RDocs , ele diz que você precisa de um bindingque está a Binding or Proc object which is used to set the context of code evaluation.Tentando seu código acima novamente e apenas substituindo result = template.resultcom result = template.result(binding)fez funcionar.

Tenho certeza / espero que alguém vai saltar aqui e fornecer uma explicação mais detalhada do que está acontecendo. Felicidades.

EDIT: Para mais algumas informações sobre Bindinge fazer tudo isso um pouco mais clara (pelo menos para mim), confira o RDoc Binding .

Respondeu 27/08/2009 em 06:14
fonte usuário

votos
0

Como outros disseram, para avaliar ERB com um conjunto de variáveis, você precisa de uma ligação adequada. Existem algumas soluções com a definição de classes e métodos, mas eu acho mais simples e dando mais controle e mais seguro é para gerar uma ligação limpo e usá-lo para analisar o ERB. Aqui está a minha opinião sobre ele (rubi 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)

Eu acho que com evale sem **mesmo pode ser feito trabalhando com rubi mais velhas do que 2.1

Respondeu 28/12/2015 em 21:45
fonte usuário

Respondeu 06/04/2015 em 20:59
fonte usuário

votos
0

EDIT : Esta é uma solução suja. Por favor, veja minha outra resposta.

É totalmente estranho, mas a adição de

current = ""

antes do "para-cada" correções de loop o problema.

Deus abençoe linguagens de script e seus "recursos de linguagem" ...

Respondeu 27/08/2009 em 10:37
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more