2012-01-22 6 views
11

Estoy aprendiendo metaprogramación en Ruby y estoy probando la definición de métodos faltantes a través de method_missing y define_method. Tengo un comportamiento inesperado y me pregunto si alguien puede explicar esto. Aquí está mi clase:Ruby: ¿por qué puts llama a_ary?

class X 
    def method_missing(m, *args, &block) 
    puts "method #{m} not found. Defining it." 
    self.class.send :define_method, m do 
     puts "hi from method #{m}" 
    end 
    puts "defined method #{m}" 
    end 
end 

Ahora, este código:

x = X.new 

x.some_method 
puts 
x.some_method 
puts 
puts x 

produce la salida:

method some_method not found. Defining it. 
defined method some_method 

hi from method some_method 

method to_ary not found. Defining it. 
defined method to_ary 
#<X:0x007fcbc38e5030> 

Lo que no entiendo es la última parte: ¿por qué está llamando Rubí to_ary en una llamada a puts? ¿Por qué Ruby trataría de convertir mi objeto en una matriz solo para imprimirlo?

Tengo Googled alrededor y encontrado los enlaces relacionados:

Estos también hablan de method_missing y to_ary trampas, pero no específicamente acerca de por qué pone llamarían to_ary .

También debo mencionar que el comportamiento no cambia cuando defino un to_s, p. Ej.

def to_s 
    "I'm an instance of X" 
end 

La salida de hace "x" es entonces:

method to_ary not found. Defining it. 
defined method to_ary 
I'm an instance of X 

Respuesta

14

puts es un sinónimo de $stdout.puts. $ Stdout es una clase IO, a fin de buscar en la documentación de IO.puts:

escribe los objetos dados a IOS como con IO # impresión. Escribe un separador de registro (normalmente una nueva línea) después de cualquiera que no termine con una secuencia de nueva línea. Si se llama con un argumento de matriz, escribe cada elemento en una nueva línea.

Esto significa que el método puts está destinado a escribir varias líneas de salida. Por lo tanto, intenta llamar al método to_ary en un objeto y si se define to_ary, luego imprime cada elemento del Array devuelto en una nueva línea, de lo contrario puts llama al método to_s.

to_ary uso interno realmente no está bien documentado en la documentación de Ruby (Matz lo señala en su The Ruby Programming Language libro).

Métodos print y p Por otro lado, no llame a to_ary, solo to_s.

Nota al margen: Es interesante, que hay que volver to_ary verdadera Array objeto, no un objeto definir each método o alguna otra cosa:

class Test 
    def to_ary 
    10.downto(1) 
    end 
end 

puts Test.new 

#TypeError: can't convert Test to Array (Test#to_ary gives Enumerator) 
#  from (irb):28:in `puts' 
#  from (irb):28:in `puts' 
#  from (irb):28 
+0

Gracias. Creo que la esencia es que "el uso interno de to_ary no está bien documentado en la documentación de Ruby" :) Acabo de leer los documentos de IO.puts, que no mencionan explícitamente a toary, esto debería ser más claro, creo. Gracias por señalar el libro "El lenguaje de programación de Ruby", podría verificarlo. –