2015-01-08 6 views
12

Sto cercando di capire la funzionalità dei miglioramenti di Ruby e ho riscontrato uno scenario che non capisco.I perfezionamenti si applicano solo ai metodi di istanza?

Prendete questo codice di esempio:

class Traveller 
    def what_are_you 
    puts "I'm a Backpacker" 
    end 

    def self.preferred_accommodation 
    puts "Hostels" 
    end 
end 


module Refinements 
    module Money 
    def what_are_you 
     puts "I'm a cashed-up hedonist!" 
    end 

    module ClassMethods 
     def preferred_accommodation 
     puts "Expensive Hotels" 
     end 
    end 

    def self.included(base) 
     base.extend ClassMethods 
    end 
    end 

    refine Traveller do 
    include Money 
    end 
end 

Ora, quando faccio questo nel REPL:

Traveller.new.what_are_you   # => I'm a Backpacker 
Traveller.preferred_accommodation # => Hostels 

using Refinements 

Traveller.new.what_are_you   # => I'm a cashed-up hedonist! 
Traveller.preferred_accommodation # => Hostels (???) 

Perché #what_are_you raffinati, ma .preferred_accommodation non lo è?

+1

'Traveller.preferred_accommodation' è un metodo di classe. 'Traveller.new.preferred_accommodation' stamperà ciò che ti aspetti (dato che hai una classe base estesa sull'inclusione). Ma 'Traveller' è un'istanza della classe' Class'. Se vuoi che 'Traveller.preferred_accommodation' sia raffinato, devi ridefinire' Class' class_. – mudasobwa

+1

@mudasobwa, potresti farlo, ma non renderebbe disponibili le raffinate versioni dei metodi a tutte le classi? Non è lo stesso problema della creazione di metodi di classe definendo metodi di istanza sulla classe 'Class': diventano metodi di classe per tutte le classi? Non riesco a vedere alcun vantaggio nel fare ciò oltre la raffinazione delle classi singleton e gli ovvi svantaggi. –

+1

@CarySwoveland Sicuramente hai ragione. Direi che ** non deve ** essere fatto con la raffinatezza di 'Class'; il motivo per cui ho lasciato cadere il commento e non una risposta è: volevo fare luce su cosa sta succedendo, ma non dare una ricetta pratica. – mudasobwa

risposta

15

Come spiegato da @MasashiMiyazaki, è necessario perfezionare due classi: Traveller e classe singleton Traveller. Che in realtà consente di semplificare il codice di un bel po ':

module Money 
    refine Traveller do 
    def what_are_you 
     puts "I'm a cashed-up hedonist!" 
    end 
    end 

    refine Traveller.singleton_class do 
    def preferred_accommodation 
     puts "Expensive Hotels" 
    end 
    end 
end 

Traveller.new.what_are_you   #=> I'm a Backpacker 
Traveller.preferred_accommodation #=> Hostels 

using Money 
Traveller.new.what_are_you   #=> I'm a cashed-up hedonist! 
Traveller.preferred_accommodation #=> Expensive Hotels 

Inoltre, mettendo le tre dichiarazioni in un modulo, le versioni raffinate dei due metodi sono limitati a quel modulo:

module M 
    using Money 
    Traveller.new.what_are_you   #=> I'm a cashed-up hedonist! 
    Traveller.preferred_accommodation #=> Expensive Hotels 
end 

Traveller.new.what_are_you   #=> I'm a Backpacker 
Traveller.preferred_accommodation #=> Hostels 
+1

Ho capito che i "metodi di classe" sono solo "metodi di istanza sulla classe Singleton", ma in qualche modo non hanno colto questa implicazione. Pensando ancora un po 'al modello a oggetti di Ruby, questo ha senso. Grazie! – andrewdotnich

5

È necessario chiamare refine Traveler con ambito singleton_class per sovrascrivere i metodi di classe. Aggiungendo il seguente codice al modulo di perfezionamento invece di self.included, è possibile ottenere il risultato previsto.

module Refinements 
    refine Traveller.singleton_class do 
    include Money::ClassMethods 
    end 
end 

questo articolo (http://timelessrepo.com/refinements-in-ruby) vi aiuterà a capire di più perfezionamenti.