In che modo uno, senza ereditarietà, sostituisce un metodo di classe e chiama l'originale dal nuovo metodo?

StackOverflow https://stackoverflow.com/questions/288020

Domanda

Ho trovato una fonte che ha superato con successo Time.strftime in questo modo:

class Time
  alias :old_strftime :strftime
  def strftime
    #do something
    old_strftime
  end
end

Il problema è che strftime è un metodo di istanza. Devo sostituire Time.now - un metodo di classe - in modo tale che qualsiasi chiamante ottenga il mio nuovo metodo, mentre il nuovo metodo chiama ancora il metodo .now originale. Ho esaminato alias_method e non ho riscontrato alcun successo.

È stato utile?

Soluzione

A volte è difficile capovolgere la testa, ma è necessario aprire " eigenclass " che è il singleton associato a un oggetto classe specifico. la sintassi per questo è class < < self do ... end.

class Time
  alias :old_strftime :strftime

  def strftime
    puts "got here"
    old_strftime
  end
end

class Time
  class << self
    alias :old_now :now
    def now
      puts "got here too"
      old_now
    end
  end
end

t = Time.now
puts t.strftime

Altri suggerimenti

I metodi di classe sono solo metodi. Lo sconsiglio vivamente, ma hai due opzioni equivalenti:

class Time
  class << self
    alias_method :old_time_now, :now

    def now
      my_now = old_time_now
      # new code
      my_now
    end
  end
end

class << Time
  alias_method :old_time_now, :now

  def now
    my_now = old_time_now
    # new code
    my_now
  end
end

Se hai bisogno di sovrascriverlo a scopo di test (il motivo per cui normalmente voglio ignorare Time.now), i framework di derisione / stub Ruby lo faranno facilmente per te. Ad esempio, con RSpec (che utilizza flexmock):

Time.stub!(:now).and_return(Time.mktime(1970,1,1))

A proposito, consiglio vivamente di evitare la necessità di stub time.now dando alle tue classi un orologio irreversibile:

class Foo
  def initialize(clock=Time)
    @clock = clock
  end

  def do_something
    time = @clock.now
    # ...
  end
end

Ho cercato di capire come sovrascrivere un metodo di istanza usando i moduli.

module Mo
  def self.included(base)
    base.instance_eval do
      alias :old_time_now :now
      def now
        my_now = old_time_now
        puts 'overrided now'
        # new code
        my_now
      end
    end
  end
end
Time.send(:include, Mo) unless Time.include?(Mo)

> Time.now
overrided now
=> Mon Aug 02 23:12:31 -0500 2010
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top