Ruby/Rails: Class_eval non vuole valutare questo codice
-
28-10-2019 - |
Domanda
Per generare becchetti per Omniauth, ho aggiunto questo metodo a config/environments/development.rb
def provides_mocks_for(*providers)
providers.each do |provider|
class_eval %Q{
OmniAuth.config.add_mock(provider, {
:uid => '123456',
:provider => provider,
:nickname => 'nickname',
:info => {
'email' => "#{provider}@webs.com",
'name' => 'full_name_' + provider
}
})
}
end
end
Quindi chiamo nello stesso file:
provides_mocks_for :facebook, :twitter, :github, :meetup
Ma ottengo:
3.1.3/lib/active_support/core_ext/kernel/singleton_class.rb:11:in `class_eval': can't create instance of singleton class (TypeError)
Soluzione
class_eval
e module_eval
(che sono equivalenti) vengono utilizzati per valutare ed eseguire immediatamente le stringhe come codice Ruby. Pertanto, possono essere utilizzati come struttura di meta-programmazione per creare metodi dinamicamente. Un esempio è
class Foo
%w[foo bar].each do |name|
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{name}
puts '#{name}'
end
RUBY
end
end
Creerà due metodi foo
e bar
che stampano i rispettivi valori. Come puoi vedere, creo una stringa contenente il codice sorgente effettivo della funzione e la trasmetto in class_eval
.
Sebbene si tratti di uno strumento molto capace di eseguire il codice creato dinamicamente, deve essere utilizzato con grande cura. Se fai errori qui, succederanno cose brutte ™. EG Se si utilizza i valori forniti dall'utente quando si genera codice, fai veramente Sicuramente le variabili contengono solo valori che ti aspetti. La funzione basata su Eval dovrebbe essere generalmente utilizzata come ultima risorsa.
Una variante più pulita e generalmente preferita è usare define_method
così:
class Foo
%w[foo bar].each do |name|
define_method name do
puts name
end
end
end
(Si noti che la risonanza magnetica è un po 'più veloce con la variante di Eval. Ma non importa la maggior parte del tempo rispetto alla sicurezza e alla chiarezza aggiunte.)
Ora nel tuo codice dato, scrivi efficacemente il codice in una stringa che può essere eseguita direttamente. Usando class_eval
Qui porta alla stringa eseguita nel contesto dell'oggetto più alto (Kernel
in questo caso). Poiché si tratta di un oggetto singleton speciale che non può essere installato (simile a Nil, True e False), si ottiene questo errore.
Tuttavia, man mano che si crea un codice eseguibile direttamente, non è necessario utilizzare class_eval
(o qualsiasi forma di Eval). Basta eseguire il tuo codice in un ciclo così com'è. E ricorda sempre: le varianti di meta-programmazione (di cui i metodi di valutazione sono tra i più cattivi) dovrebbero essere usate solo come ultima risorsa.