57 votes

Modèles Ruby: comment passer des variables à un ERB intégré?

J'ai un modèle ERB intégré 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 "actuelle" 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?

69voto

tokland Points 29813

La solution la plus Simple, l'utilisation OpenStruct:

require 'erb'
require 'ostruct'
namespace = OpenStruct.new(name: 'Joan', last: 'Maragall')
template = 'Name: <%= name %> <%= last %>'
puts 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), Car elle s'appuie sur OpenStruct, un accès à un non-existant des rendements variables en nil tandis que vous préfèrerez sûrement qu'il n'a pas bruyamment. 2) binding est appelée à l'intérieur d'un bloc, c'est en la fermeture, de sorte qu'il inclut toutes les variables locales dans le champ d'application (en fait, ils se font de l'ombre aux attributs de la structure!).

Voici donc une autre solution, plus détaillé, 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 un String#erb extension qui vous permet de vous écrire quelque chose comme "x=<%= x %>, y=<%= y %>".erb(x: 1, y: 2).

11voto

Je l'ai!

Je crée un cours de liaison

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

et passer une instance à 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 de modèle .erb ressemble à ceci:

 Key: <%= @key %>
 

7voto

Alvin Ye Points 15
 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/

4voto

theIV Points 12999

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

Je suis sûr/j'espère que quelqu'un va sauter dans et fournir une explication plus détaillée de ce qui se passe. Des acclamations.

EDIT: Pour plus d'information sur Binding et de rendre tout cela un peu plus clair (du moins pour moi), découvrez la Liaison RDoc.

0voto

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

C'est totalement étrange, mais en ajoutant

 current = ""
 

avant la boucle "pour-chaque" résout le problème.

Que Dieu bénisse les langages de script et leurs "fonctionnalités linguistiques" ...

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X