2012-01-29 11 views

Respuesta

78

Voy a responder un poco más que su pregunta al incluir instance_{eval|exec} en su pregunta.

Todas las variaciones de {instance|module|class}_{eval|exec} cambio la contexto actual, es decir, el valor para self:

class Array 
    p self      # prints "Array" 
    43.instance_eval{ p self } # prints "43" 
end 

ya por las diferencias. Las versiones eval acepta una cadena o un bloque, mientras que las versiones exec sólo aceptan un bloque, pero le permiten pasar parámetros a la misma:

def example(&block) 
    42.instance_exec("Hello", &block) 
end 
example{|mess| p mess, self } # Prints "Hello" then "42" 

La versión eval no permite pasar parámetros. Proporciona self como primer parámetro, aunque no puedo pensar en un uso para esto.

Finalmente, module_{eval|exec} es el mismo que el correspondiente class_{eval|exec}, pero son ligeramente diferentes de instance_{eval|exec} a medida que cambian lo que es la clase corriente abierta (es decir, lo que se verá afectado por def) de diferentes maneras:

String.instance_eval{ def foo; end } 
Integer.class_eval { def bar; end } 

String.method_defined?(:foo)   # => false 
String.singleton_methods.include?(:foo) # => true 
Integer.method_defined?(:bar)   # => true 

Entonces obj.instance_{eval|exec} abre la clase singleton de obj, mientras que mod.{class|module}_{eval|exec} abre mod.

Por supuesto, instance_{eval|exec} están disponibles en cualquier objeto Ruby (incluyendo módulos), mientras que {class|module}_* sólo están disponibles en Module (y por lo tanto Classes)

+1

Nota: cuando module_eval lambda, pasa el módulo a lambda como primer parámetro. module_exec no. – Nakilon

+1

No me di cuenta de eso. No puedo pensar en un solo caso en el que sea útil (en su lugar, use 'self'), pero he editado mi respuesta para que esté completa. –

6

Para responder a su última pregunta primero, eval (en todas sus variaciones) es completamente diferente de la ejecutiva. exec $command comenzará un nuevo proceso para ejecutar el comando que especifique y luego saldrá cuando termine.

class_eval y module_eval tienen el poder de redefinir las clases y módulos, incluso aquellos que usted mismo no escribió. Por ejemplo, puede usar eval de clase para agregar un nuevo método que no existía.

Fixnum.class_eval { def number; self; end } 
7.number # returns '7' 

class_eval se puede utilizar para añadir métodos de instancia, y instance_eval se puede utilizar para añadir métodos de clase (sí, esa parte es muy confuso). Un método de clase sería algo así como Thing.foo - literalmente está llamando al método foo en la clase Thing. Un método de instancia es como el ejemplo anterior, usando class_eval Agregué un método number a cada instancia de Fixnum.

Bien, esa es la clase de métodos *_eval. Los métodos de ejecución son similares, pero le permiten mirar dentro de una clase y ejecutar un bloque de código como si estuviera definido como un método en esa clase. Tal vez usted tiene una clase que tiene este aspecto:

class Foo 
    @@secret = 'secret key' 
    @@protected = 'some secret value' 
    def protected(key) 
    if key == @@secret 
     return @@protected 
    end 
    end 
end 

La clase Foo es sólo un envoltorio alrededor de algún valor secreto, si conoce la clave correcta. Sin embargo, se puede engañar a la clase en que le da sus secretos mediante la ejecución de un bloque dentro del contexto de la clase, así:

Foo.class_exec { @@secret = 'i'm a hacker' } 
Foo.protected('i'm a hacker') #returns the value of @@protected because we overwrote @@secret 

En general, con una gran cantidad de las herramientas de rubí, se puede usar cualquiera de estos para resolver muchos problemas Es probable que muchas veces ni siquiera necesites hacerlo a menos que quieras parche de mono una clase que haya definido alguna biblioteca (aunque eso abre una gran cantidad de gusanos). Intente jugar con ellos en irb y vea cuál le resulta más fácil. Personalmente, no uso los métodos *_exec tanto como los métodos *_eval, pero esa es una preferencia personal mía.

+0

Creo que estás confundiendo '' exec' y instance_exec' –

+0

En realidad, no ? Mencioné 'exec' explícitamente al principio. 'exec' en realidad no tiene mucho en común con la familia de métodos' eval', tanto como '{instance | module | class} _exec' do. – tjarratt

+7

Oh, ya veo. Creo que el OP significa '* _eval' vs' * _exec', no 'eval' vs' exec', pero podría estar equivocado. –

Cuestiones relacionadas