How does one - without inheritance - override a class method and call the original from within the new method?

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

Question

I found one source which successfully overrode Time.strftime like this:

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

The trouble is, strftime is an instance method. I need to override Time.now - a class method - in such away that any caller gets my new method, while the new method still calls the original .now method. I've looked at alias_method and have met with no success.

Was it helpful?

Solution

This is kinda hard to get your head around sometimes, but you need to open the "eigenclass" which is the singleton associated with a specific class object. the syntax for this is 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

OTHER TIPS

Class methods are just methods. I highly recommend against this, but you have two equivalent choices:

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

If the you need to override it for testing purposes (the reason I normally want to override Time.now), Ruby mocking/stubbing frameworks will do this for you easily. For instance, with RSpec (which uses flexmock):

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

By the way, I highly recommend avoiding the need to stub out Time.now by giving your classes an overrideable clock:

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

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

I've been trying to figure out how to override an instance method using modules.

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top