Ruby/Rails: class_eval no quiere evaluar este código
-
28-10-2019 - |
Pregunta
Para generar simulacros para omniauth, agregué este método 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
Luego llamo al mismo archivo:
provides_mocks_for :facebook, :twitter, :github, :meetup
Pero entiendo:
3.1.3/lib/active_support/core_ext/kernel/singleton_class.rb:11:in `class_eval': can't create instance of singleton class (TypeError)
Solución
class_eval
y module_eval
(que son equivalentes) se utilizan para evaluar e ejecutar inmediatamente las cadenas como código Ruby. Como tal, pueden usarse como una instalación de metaprogramación para crear dinámicamente métodos. Un ejemplo es
class Foo
%w[foo bar].each do |name|
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{name}
puts '#{name}'
end
RUBY
end
end
Creará dos métodos foo
y bar
que imprimen sus respectivos valores. Como puede ver, creo una cadena que contiene el código fuente real de la función y lo paso a class_eval
.
Si bien este es un instrumento muy capaz de ejecutar código creado dinámicamente, debe usarse con gran cuidado. Si comete errores aquí, sucederán cosas malas ™. Por ejemplo, si usa valores proporcionados por el usuario al generar código, haga De Verdad Seguro que las variables contienen solo valores que espera. La función basada en evaluación generalmente debe usarse como el último recurso.
Una variante más limpia y generalmente preferida es usar define_method
al igual que:
class Foo
%w[foo bar].each do |name|
define_method name do
puts name
end
end
end
(Tenga en cuenta que la resonancia magnética es un poco más rápido con la variante eval. Pero eso no importa la mayor parte del tiempo en comparación con la seguridad y la claridad adicionales).
Ahora en su código dado, escribe efectivamente el código en una cadena que se puede ejecutar directamente. Usando class_eval
Aquí conduce a la cadena que se ejecuta en el contexto del objeto más superior (Kernel
en este caso). Como este es un objeto especial singleton que no puede ser instanciado (similar a nulo, verdadero y falso), obtienes este error.
Sin embargo, a medida que crea un código ejecutable directamente, no necesita usar class_eval
(o cualquier forma de eval) en absoluto. Simplemente ejecute su código en un bucle tal como está. Y siempre recuerde: las variantes de metaprogramación (de las cuales los métodos EVAL se encuentran entre los más malos) solo deben usarse como el último recurso.