57 Stimmen

Ruby-Vorlagen: Wie kann man Variablen in inlined ERB übergeben?

Ich habe eine ERB-Vorlage in den Ruby-Code eingefügt:

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

Ich kann die Variable "current" nicht an die Vorlage übergeben.

Der Fehler ist:

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

Wie kann ich das beheben?

69voto

tokland Punkte 63238

Für eine einfache Lösung, verwenden Sie 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

Der obige Code ist einfach genug, hat aber (mindestens) zwei Probleme: 1) Da er sich auf OpenStruct ein Zugriff auf eine nicht existierende Variable ergibt nil während Sie es wahrscheinlich vorziehen würden, wenn es geräuschvoll scheitern würde. 2) binding wird innerhalb eines Blocks aufgerufen, d.h. in einer Schließung, so dass alle lokalen Variablen in den Geltungsbereich einbezogen werden (diese Variablen schatten die Attribute der Struktur!).

Hier ist also eine andere Lösung, die zwar ausführlicher ist, aber keines dieser Probleme aufwirft:

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

Wenn Sie dies häufig verwenden, sollten Sie natürlich eine String#erb Erweiterung, mit der Sie etwas schreiben können wie "x=<%= x %>, y=<%= y %>".erb(x: 1, y: 2) .

0 Stimmen

Haben Sie das getestet? Auf meinem System erzeugt dein genauer Code "NameError: undefined local variable or method `name' for main:Object." (Edit: Scheint ein 1.9.2 Problem zu sein stackoverflow.com/questions/3242470/ )

0 Stimmen

@Ryan. In der Tat, ich testete es nur 1.8.7, aktualisiert. Ich werde eine Antwort in der Frage hinzufügen, die Sie verlinken, ich denke instance_eval ist die einfachste Lösung. Danke für den Hinweis auf das Problem.

0 Stimmen

Was bedeutet die namespace.instance_eval { binding } tun?

31voto

asfer Punkte 3449

Einfache Lösung mit Verbindlich :

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

2 Stimmen

local_variable_set wurde in Ruby 2.1 eingeführt.

11voto

ivan_ivanovich_ivanoff Punkte 18553

Ich hab's!

Ich erstelle eine Bindungsklasse

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

und eine Instanz an ERB übergeben

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

Die .erb-Vorlagendatei sieht wie folgt aus:

Key: <%= @key %>

9 Stimmen

Das ist unnötig. Ersetzen Sie im Code aus Ihrer ursprünglichen Frage einfach "result = template.result" durch "result = template.result(binding)". Dadurch wird der Kontext des jeweiligen Blocks verwendet und nicht der Kontext der obersten Ebene.

8voto

geekQ Punkte 28189

Ersetzen Sie im Code der ursprünglichen Frage einfach

result = template.result

mit

result = template.result(binding)

Dabei wird der Kontext der einzelnen Blöcke verwendet und nicht der Kontext der obersten Ebene.

(Ich habe den Kommentar von @sciurus als Antwort extrahiert, weil er der kürzeste und korrekteste ist).

7voto

alvin2ye Punkte 137
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/

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X