On Fri, 08 Sep 2006 14:34:39 +0900, ara.t.howard wrote:

> i've been wanting a better alias_method for quite some time.  essentially i'd
> like a way out of the trap where executing
> 
>    alias_method '__fubar__', 'fubar'
> 
> goes haywire when __fubar__ already exists.  we've all seen it happen before.
> 
> anyhow.  the interface it'd like would be

It would be nice to have this built in to the language -- a way to blur
the difference between overriding an inherited method and overriding a
method defined on the current class, so that `super' would call whatever
the previous version of the was.

Try this idea  on for size. I wrote it a few days ago. Look at the
unit tests at the end to see how it's intended to be used. I think a
little syntactic sugar would be nice, but it gets the job done.

class Module
  private
  def override(method_name,&block)
    begin
      #case 1: I'm overriding a method defined on this object
      old_method=instance_method(method_name)
      alias_for_old=unused_alias
      alias_method alias_for_old, method_name
    rescue NameError
      begin
	 #case 2: I'm overriding a method that I inherit from
	 old_method=superclass.instance_method(method_name)
      rescue NameError
	 #case 3: I'm not overriding anything. Just use a simple
	 #function that we can call without side effects
	 old_method=Object.instance_method(:nil?)
      end
    end
    define_method(method_name) do |*args|
      block.call(old_method.bind(self),*args)
    end
  end
  def unused_alias
    newalias=nil
    while newalias==nil or instance_methods.include?(newalias)
      newalias=:"__kenoverride__#{rand(10**20)}__"
    end
    newalias
  end
end

if __FILE__==$0
require 'test/unit'
class TestOverride < Test::Unit::TestCase
   def setup
      eval <<-"end;"
	 class A
	    def foo; "a"; end
	 end
      end;
   end


   def test_alias
      assert_nothing_raised do
	 eval <<-"end;"
	    class A
	       override(:foo){|old| old.call+"b"}
	    end
	 end;
      end

      assert_equal A.new.foo,"ab"

   end

   def test_inherit
      assert_nothing_raised do
	 eval <<-"end;"
	    class B<A
	       override(:foo){|old| "c"+old.call }
	    end
	 end;
      end
      
      #I don't want to rely on whether test_inherit or test_alias
      #comes first, so I'll test for the results of both orders.
      assert ["ca","cab"].include?(B.new.foo)
   end

   #I'm not really sure whether this is the appropriate behavior
   #or whether throwing an exception is more appropriate.
   def test_neither
      assert_nothing_raised do
	 eval <<-"end;"
	    class C
	       override(:foo){|old| "d" }
	    end
	 end;
      end

      assert_equal C.new.foo,"d"
   end
end
end


-- 
Ken Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/