Frage Wie überschreibt man - ohne Vererbung - eine Klassenmethode und ruft das Original innerhalb der neuen Methode auf?


Ich habe eine Quelle gefunden, die erfolgreich übergangen wurde Time.strftime so was:

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

Die Schwierigkeit ist, strftime ist eine Instanzmethode. Ich muss überschreiben Time.now - Eine Klassenmethode - so, dass jeder Aufrufer meine neue Methode erhält, während die neue Methode immer noch das Original aufruft .now Methode. Ich habe es angeschaut alias_method und haben keinen Erfolg gehabt.


9
2017-11-13 19:50


Ursprung


Antworten:


Das ist manchmal schwer zu verstehen, aber Sie müssen die "Eigenklasse" öffnen, die das Singleton eines bestimmten Klassenobjekts ist. Die Syntax dafür ist die Klasse << 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

11
2017-11-13 20:05



Klassenmethoden sind nur Methoden. Ich empfehle dies sehr, aber Sie haben zwei gleichwertige Möglichkeiten:

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

2
2017-11-13 20:03



Wenn Sie es für Testzwecke überschreiben müssen (der Grund, warum ich Time.now normalerweise überschreiben möchte), wird Ruby Mocking / Stubbing Frameworks dies für Sie leicht tun. Zum Beispiel mit RSpec (welches flexmock verwendet):

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

Übrigens, ich empfehle dringend, die Notwendigkeit zu vermeiden, Time auszugeben. Jetzt geben Sie Ihren Klassen eine ausschaltbare Uhr:

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

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

1
2017-11-13 20:10



Ich habe versucht herauszufinden, wie man eine Instanzmethode mit Modulen überschreiben kann.

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

0
2017-08-03 04:16