modèles Ruby: Comment passer des variables dans ERB inline?

voix
51

J'ai un modèle ERB inline dans le code 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

Je ne peux pas passer la variable « courante » dans le modèle.

L'erreur est:

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

Comment puis-je réparer ça?

Créé 27/08/2009 à 06:08
source utilisateur
Dans d'autres langues...                            


9 réponses

voix
57

Pour une solution simple, utilisez 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

Le code ci - dessus est assez simple , mais a (au moins) deux problèmes: 1) Comme il est basé sur OpenStruct, un accès à un des rendements variables non existants nilalors que vous préféreriez probablement qu'il n'a pas grand bruit. 2) bindingest appelé dans un bloc, qui est tout, dans une fermeture, il comprend toutes les variables locales du périmètre (en fait, ces variables ombre les attributs de la struct!).

Voici donc une autre solution, plus bavard, mais sans aucun de ces problèmes:

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

Bien sûr, si vous allez utiliser souvent, assurez-vous de créer une String#erbextension qui vous permet d'écrire quelque chose comme "x=<%= x %>, y=<%= y %>".erb(x: 1, y: 2).

Créé 28/03/2011 à 17:27
source utilisateur

voix
20

Une solution simple à l' aide de reliure :

b = binding
b.local_variable_set(:a, 'a')
b.local_variable_set(:b, 'b')
ERB.new(template).result(b)
Créé 18/01/2015 à 02:53
source utilisateur

voix
10

Je l'ai!

Je crée une classe de liaisons

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

et transmettre une instance 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

Le fichier modèle de .erb ressemble à ceci:

Key: <%= @key %>
Créé 27/08/2009 à 14:35
source utilisateur

voix
5

Dans le code de la question d'origine, il suffit de remplacer

result = template.result

avec

result = template.result(binding)

Cela utilisera le contexte de chaque bloc plutôt que le contexte de haut niveau.

(Extrait juste le commentaire par @sciurus comme réponse car il est le plus court et le plus correct.)

Créé 06/01/2016 à 18:32
source utilisateur

voix
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/

Créé 08/01/2014 à 01:25
source utilisateur

voix
4

Je ne peux pas vous donner une très bonne réponse à pourquoi cela se passe parce que je ne suis pas 100% sûr de savoir comment fonctionne ERB, mais en regardant juste au ERB RDocs , il est dit que vous avez besoin d' un bindingqui est a Binding or Proc object which is used to set the context of code evaluation.Essayant votre code ci - dessus encore et simplement remplacer result = template.resultavec result = template.result(binding)fait le travail.

Je suis sûr que quelqu'un / espoir sautera ici et fournir une explication plus détaillée de ce qui se passe. À votre santé.

EDIT: Pour un peu plus d' informations sur Bindinget en faisant tout cela un peu plus clair (au moins pour moi), consultez la liaison RDoc .

Créé 27/08/2009 à 06:14
source utilisateur

voix
0

Comme d'autres ont dit, d'évaluer ERB avec un certain ensemble de variables, vous avez besoin d'une liaison appropriée. Il existe des solutions à la définition des classes et des méthodes, mais je pense que le plus simple et de donner plus de contrôle et plus sûr est de générer une liaison propre et l'utiliser pour analyser le ERB. Voici mon avis sur la question (2.2.x rubis):

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)

Je pense avec evalet sans **même chose peut être fait de travailler avec les anciennes rubis de 2,1

Créé 28/12/2015 à 21:45
source utilisateur

Créé 06/04/2015 à 20:59
source utilisateur

voix
0

EDIT : Ceci est une solution de contournement sale. S'il vous plaît voir mon autre réponse.

Il est tout à fait étrange, mais en ajoutant

current = ""

avant que la boucle « for-each » résout le problème.

Que Dieu bénisse les langages de script et leurs « caractéristiques linguistiques » ...

Créé 27/08/2009 à 10:37
source utilisateur

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