2012-01-22 9 views
11

Sto imparando la metaprogrammazione in Ruby e sto solo provando a definire i metodi mancanti tramite method_missing e define_method. Sto ricevendo un comportamento inaspettato e mi sto chiedendo se qualcuno possa spiegarlo. Qui è la mia classe:Ruby: perché mette la chiamata 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 

Ora, questo codice:

x = X.new 

x.some_method 
puts 
x.some_method 
puts 
puts x 

produce l'output:

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> 

Quello che non capisco è l'ultima parte: perché Rubino chiamando to_ary in una chiamata a mettere? Perché Ruby dovrebbe provare a convertire il mio oggetto in un array solo per stamparlo?

Googled intorno e trovato questi link correlati:

Questi parlano anche di method_missing e to_ary trucchi, ma non specificamente sul perché mette chiamerebbero to_ary .

Vorrei anche ricordare che il comportamento non cambia quando definisco un to_s, ad es.

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

L'uscita di "puts x" è quindi:

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

risposta

14

puts è sinonimo di $stdout.puts. $ Stdout è una classe IO, in modo da guardare la documentazione per IO.puts:

Scrive gli oggetti dati per Ios come con IO # stampa. Scrive un record separatore (in genere una nuova riga) dopo tutto ciò che non finisce ancora con una sequenza di nuova riga. Se chiamato con un argomento array, scrive ogni elemento su una nuova riga.

Ciò significa che il metodo puts è progettato per scrivere diverse righe di output. Quindi prova a chiamare il metodo to_ary su un oggetto e se è stato definito to_ary, quindi stampa ogni elemento del numero restituito Array su una nuova riga, altrimenti il ​​metodo to_s chiama .

to_ary uso interno è in realtà non è ben documentato nella documentazione di Ruby (punti questo Matz nel suo The Ruby Programming Language libro).

Metodi print e p d'altra parte non chiamare to_ary, solo to_s.

Nota a margine: Interessante, che deve tornare to_ary reale Array oggetto, non un oggetto che definisce each metodo o qualcosa d'altro:

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

Grazie. Penso che l'essenza sia "to_ary l'uso interno non è molto ben documentato nella documentazione di Ruby" :) Ho appena letto i documenti di IO.puts, non menzionano esplicitamente to_ary, questo dovrebbe essere più chiaro, credo. Grazie per aver segnalato il libro "The Ruby Programming Language", potresti verificarlo. –