Ruby / How to alias and override a class method

From WhyNotWiki

Jump to: navigation, search

My hovercraft is full of eels: Alias a Static Method in Ruby (http://www.redhillconsulting.com.au/blogs/simon/archives/000326.html). Retrieved on 2007-02-15 22:11.

In Ruby, the way to safely mix-in methods (rather than use subclassing) is to use some method chaining. For this we use alias_method. So, for example, if we wanted to override to_s to always place quotes around the value (nopt very useful but it will suffice for now) we could write a module like this:

   module QuoteToS
     def self.included(base)
       base.class_eval do
         alias_method :to_s_without_quotes, :to_s unless method_defined?(:to_s_without_quotes)
         alias_method :to_s, :to_s_with_quotes
       end
     end
     def to_s_with_quotes
       "'#{to_s_without_quotes}'"
     end
   end

This essentially says that when the module is included (ie mixed-in) to a class then: add a method named to_s_with_quotes; create an alias of the existing to_s named to_s_without_quotes; make an alias of the new to_s_with_quotes named to_s; and finally, whenever to_s. is executed, call the old to_s method (now named to_s_without_quotes) and surround the results with, you guess it, quotes.

To use this you would either manually include the module in a class or, more along the lines of aspects, force the inclusion with some code like this:

   MyClass.send(:include, QuoteToS)

(As a side note, the use of unless method_defined?(:to_s_without_quotes) is to work-around a bug in Ruby 1.8.4 that causes an infinite recursion when using alias_method. [...])

So that's all very well and good but what happens when you need to do the same thing with [class] methods? The answer is, use class << self and extend. In my case, overriding the behaviour of ActiveRecord::Schema.define looks something like this:

   module ForeignKeyMigrations::Schema
     def self.included(base)
       base.extend(ClassMethods)
       base.class_eval do
         class << self
           alias_method :define_without_fk, :define unless method_defined?(:define_without_fk)
           alias_method :define, :define_with_fk
         end
       end
     end
     
     module ClassMethods
       def define_with_fk(info={}, &block)
         ...
         define_without_fk(info, &block)
       end
     end
   end

Here, the use of base.extend causes all the methods defined within the module ClassMethods--an aribitrary name used by convention in most if not all the rails code I've ever seen—to be added as [class] methods on the class. Then, surrounding the alias_method calls within a class << self causes them to be executed in [class] context.

Again, to have this code mixed-in to the existing ActiveRecord::Schema class looks like this:

   ActiveRecord::Schema.send(:include, ForeignKeyMigrations::Schema)
Personal tools