2012-01-21 9 views
48

Qui devo trascurare qualcosa di molto semplice qui ma non riesco a capire come eseguire il rendering di un semplice modello ERB con valori da una mappa hash.Rendering di un modello ERB con valori da un hash

Sono relativamente nuovo al rubino, proveniente da pitone. Ho un modello ERB (non HTML), che ho bisogno di rendering con contesto che deve essere preso da una mappa hash, che ricevo da una fonte esterna.

Tuttavia, la documentazione di ERB indica che il metodo ERB.result prende uno binding. Ho imparato che sono qualcosa che mantiene i contesti variabili in ruby ​​(qualcosa come locals() e globals() in python, presumo?). Ma non so come posso costruire un oggetto vincolante dalla mia mappa hash.

un po '(un sacco , in realtà) mi ha dato questa Googling: http://refactormycode.com/codes/281-given-a-hash-of-variables-render-an-erb-template, che utilizza una magia rubino metaprogrammazione che mi sfugge.

Quindi, non esiste una soluzione semplice a questo problema? O c'è un motore di template migliore (non legato all'HTML) più adatto a questo? (Ho scelto solo ERB perché è nello stdlib).

+0

Non so di nessun motori di modello di Ruby che sono "legati" in HTML; un modello è un modello. Inoltre non sono sicuro di cosa c'è di sbagliato nella soluzione a cui si collega: il problema è ottenere l'hash nel modulo? –

+0

Dave, non c'è niente di sbagliato in questo. Solo che pensavo che ci potesse essere una soluzione più elegante per un problema così semplice piuttosto che usare la metaprogrammazione. –

+1

questo? http: // StackOverflow.com/domande/1338960/ruby-templates-how-to-pass-variabili-in-inline-erb – tokland

risposta

52

Non so se questo si qualifica come "più elegante" o no:

require 'erb' 
require 'ostruct' 

class ErbalT < OpenStruct 
    def render(template) 
    ERB.new(template).result(binding) 
    end 
end 

et = ErbalT.new({ :first => 'Mislav', 'last' => 'Marohnic' }) 
puts et.render('Name: <%= first %> <%= last %>') 

O da un metodo di classe:

class ErbalT < OpenStruct 
    def self.render_from_hash(t, h) 
    ErbalT.new(h).render(t) 
    end 

    def render(template) 
    ERB.new(template).result(binding) 
    end 
end 

template = 'Name: <%= first %> <%= last %>' 
vars = { :first => 'Mislav', 'last' => 'Marohnic' } 
puts ErbalT::render_from_hash(template, vars) 

(.. ErbalT ha Erb, T per il modello, e suona come "tisana" Naming cose è difficile)

+1

Grazie, Dave. Sì, questa soluzione sembra un po 'meglio, anche se non ho idea di 'OpenStruct'. Prendo ai documenti, non è un problema :) –

+1

@ShrikantSharat in realtà è più o meno la stessa cosa, ma è sistema operativo che fa la meta. –

3

Se si desidera fare le cose in modo molto semplice, è sempre possibile utilizzare solo ricerche esplicite di hash all'interno del modello ERB. Diciamo che utilizza "vincolante" per passare una variabile hash chiamato "hash" nel modello, sarebbe simile a questa:

<%= hash["key"] %> 
+0

Ma come si usa 'binding' per passare la variabile hash? –

+0

Facile. Basta memorizzare l'hash in una variabile. Quindi, nello stesso scope della variabile, chiama 'ERB.new (template) .result (binding)'. 'Kernel # binding' catturerà tutte le variabili che sono nel campo di applicazione e saranno disponibili all'interno del modello ERB. –

+2

Grazie - questa informazione appartiene più alla tua risposta principale che a un commento. Ma questo approccio non soffre delle stesse preoccupazioni di perdita di dati/sicurezza che ho notato sopra che la risposta basata su 'instance_eval' soffre? –

31

Se è possibile utilizzare Erubis hai questa funzionalità già:

irb(main):001:0> require 'erubis' 
#=> true 
irb(main):002:0> locals = { first:'Gavin', last:'Kistner' } 
#=> {:first=>"Gavin", :last=>"Kistner"} 
irb(main):003:0> Erubis::Eruby.new("I am <%=first%> <%=last%>").result(locals) 
#=> "I am Gavin Kistner" 
+0

Grazie per questo suggerimento, Phrongz. Ma penso che resterò fedele a stdlib per questa volta. –

+5

Questo è utile dal momento che Rails 4 utilizza Erubis per ERB per il suo motore di rendering ora. – Unixmonkey

+0

per coloro che utilizzano Rails, questo sembra il migliore –

66
require 'erb' 
require 'ostruct' 

def erb(template, vars) 
    ERB.new(template).result(OpenStruct.new(vars).instance_eval { binding }) 
end 

esempio

1.9.2p290 :008 > erb("Hey, <%= first_name %> <%= last_name %>", :first_name => "James", :last_name => "Moriarty") 
=> "Hey, James Moriarty" 
+3

+1, questo è un modo super elegante per creare un'associazione da un hash. – orip

+11

Sembra bello all'inizio, ma sfortunatamente il binding eredita anche i locals e i metodi dal contesto in cui è stato istanziato 'OpenStruct'. Ciò consente al modello di accedere in modo più che intenzionale e potrebbe portare a bug o persino a una vulnerabilità della sicurezza, ad es. https://gist.github.com/aspiers/ad6549058ee423819976 –

6

Soluzione semplice utilizzando Binding:

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

La parte difficile qui non è associazione di polluting con variabili locali ridondanti (come i n risposte top-rated):

require 'erb' 

class TemplateRenderer 
    def self.empty_binding 
    binding 
    end 

    def self.render(template_content, locals = {}) 
    b = empty_binding 
    locals.each { |k, v| b.local_variable_set(k, v) } 

    # puts b.local_variable_defined?(:template_content) #=> false 

    ERB.new(template_content).result(b) 
    end 
end 

# use it 
TemplateRenderer.render('<%= x %> <%= y %>', x: 1, y: 2) #=> "1 2" 

# use it 2: read template from file 
TemplateRenderer.render(File.read('my_template.erb'), x: 1, y: 2) 
4

Rubino 2,5 ha ERB#result_with_hash che fornisce questa funzionalità:

$ ruby -rerb -e 'p ERB.new("Hi <%= name %>").result_with_hash(name: "Tom")' 
"Hi Tom" 
+1

Questa risposta è la migliore risposta. Il rubino più recente rende molto più semplice l'uso di una rilegatura. – BeepDog

Problemi correlati