2013-07-05 16 views
8

Ho cercato di capire come estendere il comportamento di initialize da un modulo. Voglio farlo senza chiamare super in initialize della classe che viene mescolata in. Voglio supportare il normale schema di chiamata include Non riesco a capirlo. Ho letto tutto quello che posso trovare sulla questione e, mentre le persone hanno suggerimenti, nessuno di loro sembra funzionare (almeno nelle mie mani).È possibile sovrascrivere #initialize in un modulo Ruby?

Ecco quello che (credo) lo so:

  • Se può essere fatto a tutti, deve essere fatto utilizzando il gancio sul include (vale a dire Module.included(base)).
  • Il gancio include verrà eseguito prima che la classe inclusa definisca initialize, quindi non c'è motivo di provare semplicemente a definire initialize con base.instance_eval perché verrà sovrascritto.
  • È stato suggerito di utilizzare il gancio method_added e gestirlo lì. Questo è quello che sto provando ora, ma sembra che l'hook venga eseguito all'inizio della definizione del metodo in modo da ottenere ciò che vedi qui sotto.

    module Mo 
        def self.included(klass) 
        klass.instance_eval do 
         def method_added(method) 
         puts "Starting creation of #{method} for #{self.name}" 
         case method 
         when :initialize 
          alias_method :original_initialize, :initialize 
          puts "About to define initialize in Mo" 
          def initialize 
          original_initialize 
          puts "Hello from Mo#initialize" 
          end 
          puts "Finished defining initialize in Mo" 
         end 
         puts "Finishing creation of #{method} for #{self.name}" 
         end 
        end 
        end 
    end 
    
    class Foo 
        include Mo 
        def initialize 
        puts "Hello from Foo#initialize" 
        end 
    end 
    
    foo = Foo.new 
    

Il risultato è il seguente output:

Starting creation of initialize for Foo 
    Starting creation of original_initialize for Foo 
    Finishing creation of original_initialize for Foo 
    About to define initialize in Mo 
    Finished defining initialize in Mo 
    Finishing creation of initialize for Foo 
    Hello from Foo#initialize 

Sembra a me come initialize dalla classe Foo è ancora sovrascrive la definizione dal modulo. Immagino che questo sia perché la definizione è ancora aperta, suggerendo che non è una questione di quale blocco è stato avviato per ultimo, che è finito per ultimo che "vince".

Se qualcuno là fuori sa davvero come farlo e farlo funzionare, per favore illuminami.

FWIW, sì, penso di avere una buona ragione per voler farlo.

risposta

24

Se siete su Ruby 2.0 o versione successiva, si può semplicemente utilizzare prepend. O richiedere all'utente di prepend piuttosto che include, o fare:

module Mo 
    module Initializer 
    def initialize 
     puts "Hello from Mo#initialize" 
     super 
    end 
    end 

    def self.included(klass) 
    klass.send :prepend, Initializer 
    end 
end 
+0

Ho scoperto "antepone" poco dopo aver fatto la domanda. Mi piace! Ancora curioso di sapere se c'è un modo per farlo in 1.9. * – Huliax

+0

C'è qualcosa di ovvio che mi manca nell'usare 'klass.send: prepend' invece di semplicemente' klass.prepend'? – NobodysNightmare

0

Sarebbe soddisfacente avere una chiamata condizionale nell'inizializzazione di Foo che chiama un metodo incluso solo se è presente?

module Mo 
    def initialize_extra 
    puts "Hello from Mo" 
    end 
end 

class Foo 
    include Mo 
    def initialize 
    if defined? initialize_extra 
     initialize_extra 
    else 
     puts "Hello from Foo" 
    end 
    end 
end 

x = Foo.new 
+1

Il mio obiettivo è non mettere alcun onere sulla classe diversa dalla necessità di includere il modulo. – Huliax

6

Ok, bene in Ruby 1.9 è possibile aggiungere funzionalità al metodo new classe ...

module Mo 
    def new(*var) 
    additional_initialize(*var) 
    super(*var) 
    end 
    def additional_initialize(*var) 
    puts "Hello from Mo" 
    end 
end 

class Foo 
    extend Mo 
    def initialize 
    puts "Hello from Foo" 
    end 
end 

foo = Foo.new 

che restituisce ...

Hello from Mo 
Hello from Foo 
Problemi correlati