2010-03-15 16 views
14

Estoy atascado. Intento definir dinámicamente un método de clase y no puedo entender el modelo de metaclass ruby. Considere la siguiente clase:Ruby metaclass locura

class Example 

    def self.meta; (class << self; self; end); end 

    def self.class_instance; self; end 

end 

Example.class_instance.class # => Class 
Example.meta.class   # => Class 

Example.class_instance == Example  # => true 
Example.class_instance == Example.meta # => false 

Obviamente, ambos métodos devuelven una instancia de Clase. Pero estas dos instancias no son lo mismo. También tienen diferentes ancestros:

Example.meta.ancestors   # => [Class, Module, Object, Kernel] 
Example.class_instance.ancestors # => [Example, Object, Kernel] 

Cuál es el punto en hacer una diferencia entre la metaclase y la instancia de clase?

Me di cuenta de que puedo send :define_method a la metaclase para definir dinámicamente un método, pero si intento enviarlo a la instancia de la clase no funcionará. Al menos podría resolver mi problema, pero aún quiero entender por qué funciona de esta manera.

actualización Mar 15, 2010 13:40

son las siguientes hipótesis correcta.

  • Si tengo un método de instancia que llama a self.instance_eval y define un método, solo afectará a la instancia particular de esa clase.
  • Si tengo un método de instancia que llama a self.class.instance_eval (que sería lo mismo que llamar a class_eval) y define un método, afectará a todas las instancias de esa clase en particular, dando como resultado un nuevo método de instancia.
  • Si tengo un método de clase que invoca instancia_eval y define un método, dará como resultado un nuevo método de instancia para todas las instancias.
  • Si tengo un método de clase que invoca instance_eval en la clase meta/eigen y define un método, se obtendrá un método de clase.

Creo que empieza a tener sentido para mí. Ciertamente limitaría tus posibilidades si self dentro de un método de clase apuntara a la clase eigen. De ser así, no sería posible definir un método de instancia desde dentro de un método de clase. ¿Es eso correcto?

Respuesta

11

Definición de un método de Singleton dinámica es sencilla cuando se utiliza instance_eval:

Example.instance_eval{ def square(n); n*n; end } 
Example.square(2) #=> 4 
# you can pass instance_eval a string as well. 
Example.instance_eval "def multiply(x,y); x*y; end" 
Example.multiply(3,9) #=> 27 

En cuanto a la diferencia anterior, usted está confundiendo 2 cosas:

La clase meta definida por usted, es lo llamó Comunidad Ruby como clase de singelton o clase propia. Esa clase singleton es la clase a la que puede agregar métodos de clase (singleton).

En cuanto a la instancia de clase que están tratando de definir utilizando el método class_instance, no es más que la propia clase, para probarlo, simplemente trate de añadir un método de instancia a la clase Example y comprobar si el método class_instance define por lo regresa Example la clase en sí por el control de la existencia de ese método:

class Example 
    def self.meta; (class << self; self; end); end 
    def self.class_instance; self; end 
    def hey; puts hey; end 
end 

Example.class_instance.instance_methods(false) #=> ['hey'] 

de todos modos para resumir para usted, cuando se desea agregar métodos de clase, hay que añadirlas a esa clase meta. En cuanto al método class_instance es inútil, simplemente elimínalo.

De todos modos le sugiero que lea this post para comprender algunos conceptos del sistema de reflexión Ruby.

ACTUALIZACIÓN

le sugiero que lea este bonito mensaje: Fun with Ruby's instance_eval and class_eval, Desafortunadamente class_eval y instance_eval son confusas, ya que de alguna manera trabajan en contra de su nombramiento!

Use ClassName.instance_eval to define class methods. 

Use ClassName.class_eval to define instance methods. 

Ahora responder a sus supuestos:

Si tengo un método de instancia, que llamadas self.instance_eval y define un método , sólo afectará a la caso particular de esa clase.

sí:

class Foo 
    def assumption1() 
    self.instance_eval("def test_assumption_1; puts 'works'; end") 
    end 
end 

f1 = Foo.new 
f1.assumption1 
f1.methods(false) #=> ["test_assumption_1"] 
f2 = Foo.new.methods(false) #=> [] 

Si tengo un método de instancia, que llamadas self.class.instance_eval (que sería lo mismo que llamar class_eval) y define un método que se Afecta a todas las instancias de esa clase particular de que da como resultado un nuevo método de instancia .

sin instance_eval en ese contexto definirá métodos simples (no los de instancia) en la clase en sí:

class Foo 
    def assumption2() 
    self.class.instance_eval("def test_assumption_2; puts 'works'; end") 
    end 
end 

f3 = Foo.new 
f3.assumption2 
f3.methods(false) #=> [] 
Foo.singleton_methods(false) #=> ["test_assumption_2"] 

Para que eso funcione reemplazar instance_eval con class_eval anteriormente.

Si tengo un método de clase que exige instance_eval y define un método que dará lugar a un nuevo método de instancia para todas las instancias.

Nop:

class Foo 
    instance_eval do 
    def assumption3() 
     puts 'works' 
    end 
    end 
end 

Foo.instance_methods(false) #=> [] 

Foo.singleton_methods(false) #=> ["assumption_3"] 

que hará que los métodos simples, y no los métodos de instancia. Para que funcione, reemplace instance_eval con class_eval arriba.

Si tengo un método de clase que exige instance_eval en la clase meta/Eigen y define un método que dará lugar a un método de clase.

bueno, eso hará cosas tan sofisticadas, ya que agregará el método singleton a la clase singleton, no creo que tenga ningún uso práctico.

+0

Para obtener más información sobre _why_ a 'def' dentro de un' instance_eval' define los métodos de clase, consulte este artículo http://yugui.jp/articles/846 – horseyguy

+0

Muchas gracias hasta el momento. Actualizo mi pregunta ¿Te importaría echarle un vistazo? – t6d

+0

Actualizado para responder preguntas en su actualización. – khelll

5

Si se define un método en una clase, puede ser invocada en sus objetos. Es un método de instancia .

class Example 
end 

Example.send :define_method, :foo do 
    puts "foo" 
end 

Example.new.foo 
#=> "foo" 

Si se define un método en un metaclase, puede ser invocada en la clase. Esto es similar al concepto de un método de clase o método estático en otros idiomas.

class Example 
    def self.metaclass 
    class << self 
     self 
    end 
    end 
end 

Example.metaclass.send :define_method, :bar do 
    puts "bar" 
end 

Example.bar 
#=> "bar" 

El razón que existen metaclases es porque se puede hacer esto en Rubí:

str = "hello" 
class << str 
    def output 
    puts self 
    end 
end 

str.output 
#=> "hello" 

"hi".output 
# NoMethodError 

Como se puede ver, se definió un método que sólo está disponible para uno instancia de una Cuerda. Lo que hemos definido este método se llama metaclass. En la cadena de búsqueda de métodos, se accede primero a la metaclase antes de buscar en la clase del objeto.

Si sustituimos el objeto de tipo String con un objeto de tipo Class, se puede imaginar por qué esto significa que sólo estamos definiendo un método en una clase específica , no en todas las clases.

Las diferencias entre el contexto actual y self son sutiles, puede read more si le interesa.

Cuestiones relacionadas