[CHALLENGE] better alias_method

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

   class C
     def m() 'a' end

     push_method 'm'

     def m() super + 'b' end
   end

   p C.new.m #=> 'ab'

what i've got is quite close, but no cigar. it has a fundemental problem with
the way ruby scopes super which i'm too tired atttm to figure out. i'm hoping
i can go to bed and wake up to a nice patch :wink: here's what i've got:

     harp:~ > cat a.rb
     class Module
       def push_method m
         this = self

         include Module.new{
           @m = this.instance_method m

           this.module_eval{ remove_method m }

           module_eval <<-code
             def #{ m }(*a, &b)
               um = ObjectSpace._id2ref #{ @m.object_id }
               um.bind(self).call *a, &b
             end
           code
         }
       end
     end

     class C
       def m
         'a'
       end
       p new.m #=> 'a'

       push_method 'm'

       def m
         super + 'b'
       end
       p new.m #=> 'ab'

       push_method 'm'

       def m
         super + 'c'
       end
       p new.m #=> 'abc'
     end

     harp :~ > ruby a.rb
     "a"
     "ab"
     a.rb:31:in `m': stack level too deep (SystemStackError)
             from (eval):3:in `m'
             from a.rb:31:in `m'
             from (eval):3:in `m'
             from a.rb:31:in `m'
             from (eval):3:in `m'
             from a.rb:31:in `m'
             from (eval):3:in `m'
             from a.rb:31:in `m'
              ... 2343 levels...
             from a.rb:31:in `m'
             from (eval):3:in `m'
             from a.rb:40:in `m'
             from a.rb:42

have at it - i'll be back in 8 hrs. :wink:

-a

···

--
what science finds to be nonexistent, we must accept as nonexistent; but what
science merely does not find is a completely different matter... it is quite
clear that there are many, many mysterious things.
- h.h. the 14th dalai lama

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

  class C
    def m() 'a' end

    push_method 'm'

    def m() super + 'b' end
  end

  p C.new.m #=> 'ab'

what i've got is quite close, but no cigar. it has a fundemental problem with
the way ruby scopes super which i'm too tired atttm to figure out. i'm hoping
i can go to bed and wake up to a nice patch :wink: here's what i've got:

    harp:~ > cat a.rb
    class Module
      def push_method m
        this = self

        include Module.new{
          @m = this.instance_method m

          this.module_eval{ remove_method m }

          module_eval <<-code
            def #{ m }(*a, &b)
              um = ObjectSpace._id2ref #{ @m.object_id }
              um.bind(self).call *a, &b
            end
          code
        }
      end
    end

    class C
      def m
        'a'
      end
      p new.m #=> 'a'

      push_method 'm'

      def m
        super + 'b'
      end
      p new.m #=> 'ab'

      push_method 'm'

      def m
        super + 'c'
      end
      p new.m #=> 'abc'
    end

    harp :~ > ruby a.rb
    "a"
    "ab"
    a.rb:31:in `m': stack level too deep (SystemStackError)
            from (eval):3:in `m'
            from a.rb:31:in `m'
            from (eval):3:in `m'
            from a.rb:31:in `m'
            from (eval):3:in `m'
            from a.rb:31:in `m'
            from (eval):3:in `m'
            from a.rb:31:in `m'
             ... 2343 levels...
            from a.rb:31:in `m'
            from (eval):3:in `m'
            from a.rb:40:in `m'
            from a.rb:42

have at it - i'll be back in 8 hrs. :wink:

I have this, it takes a different approach though:

module Patchable
   module ClassMethods
     def patch(method_name = nil, &new_body)
       if method_name
         method_name = method_name.to_sym
         pre_patched_versions[method_name] = instance_method(method_name)
         define_method(method_name, &new_body)
       else
         klass = Class.new
         imeths = klass.instance_methods
         klass.class_eval(&new_body)
         new_meths = klass.instance_methods - imeths
         new_meths.each do |m|
           pre_patched_versions[m.to_sym] = instance_method(m)
         end
         class_eval(&new_body)
       end
       self
     end

     def pre_patched_versions
       @pre_patched_versions ||= {}
     end
   end

   def hyper(*args, &block)
     meth_name = caller[0][/`([^']+)'/, 1].to_sym
     self.class.pre_patched_versions[meth_name].bind(self).call(*args, &block)
   end

   def self.included(other)
     other.extend(ClassMethods)
   end
end

class C
   include Patchable

   def m
     'a'
   end

   p new.m
   patch do
     def m
       hyper + 'b'
     end
   end

   p new.m

   patch do
     def m
       hyper + 'c'
     end
   end

   #p new.m doesn't work, infinite recursion
end

Darn. You seem to have made me discover a bug in my impl. It doesn't work for more than one level of patching per method. Well maybe someone will give me a patch too :wink:

···

On Sep 8, 2006, at 1:34 AM, ara.t.howard@noaa.gov wrote:

-a
--
what science finds to be nonexistent, we must accept as nonexistent; but what
science merely does not find is a completely different matter... it is quite
clear that there are many, many mysterious things.
- h.h. the 14th dalai lama

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

···

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

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

I encountered this same problem a while ago and also began to come up
with a solution.

One of my use cases was the now-common override of Kernel#require to
provide some new behavior for requiring files.

I came up with Maskable:
http://hoshi.fallingsnow.net/svn/maskable/maskable.rb

There is some sample code at the bottom of that file. You use it by
putting your new implementations of methods in a Module and then
calling Module#add_mask(ModuleWithNewImpl).

I found it nice because it forces you to organizes your "mask" methods
into the module to make them easier to understand.

To call add_mask on the Module, you have to extend the module with
Method::Maskable. But of course if you want to be able to mask any
method in any class/module, you can just do
Module.extend(Method::Maskable).

For a mask to call the next method in the chain, It just calls
Method.rest.

Any thoughts? It's a little nasty, but works pretty well last I used
it.

···

ara.t.howard@noaa.gov 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

   class C
     def m() 'a' end

     push_method 'm'

     def m() super + 'b' end
   end

   p C.new.m #=> 'ab'

what i've got is quite close, but no cigar. it has a fundemental problem with
the way ruby scopes super which i'm too tired atttm to figure out. i'm hoping
i can go to bed and wake up to a nice patch :wink: here's what i've got:

     harp:~ > cat a.rb
     class Module
       def push_method m
         this = self

         include Module.new{
           @m = this.instance_method m

           this.module_eval{ remove_method m }

           module_eval <<-code
             def #{ m }(*a, &b)
               um = ObjectSpace._id2ref #{ @m.object_id }
               um.bind(self).call *a, &b
             end
           code
         }
       end
     end

     class C
       def m
         'a'
       end
       p new.m #=> 'a'

       push_method 'm'

       def m
         super + 'b'
       end
       p new.m #=> 'ab'

       push_method 'm'

       def m
         super + 'c'
       end
       p new.m #=> 'abc'
     end

     harp :~ > ruby a.rb
     "a"
     "ab"
     a.rb:31:in `m': stack level too deep (SystemStackError)
             from (eval):3:in `m'
             from a.rb:31:in `m'
             from (eval):3:in `m'
             from a.rb:31:in `m'
             from (eval):3:in `m'
             from a.rb:31:in `m'
             from (eval):3:in `m'
             from a.rb:31:in `m'
              ... 2343 levels...
             from a.rb:31:in `m'
             from (eval):3:in `m'
             from a.rb:40:in `m'
             from a.rb:42

have at it - i'll be back in 8 hrs. :wink:

-a
--
what science finds to be nonexistent, we must accept as nonexistent; but what
science merely does not find is a completely different matter... it is quite
clear that there are many, many mysterious things.
- h.h. the 14th dalai lama

One more unit test that this passes. (This one seems to go to the heart of
the issue you guys are having.)

   def test_longchain
      assert_nothing_raised do
   eval 'class D; def foo; "a"; end; end'
      end

      assert_equal "a", D.new.foo

      10.times do |n|
   assert_nothing_raised do
      eval 'class D; override :foo do |old| old.call+"a"; end; end'
   end

   assert_equal "a"*(n+2), D.new.foo
      end

   end

···

On Fri, 08 Sep 2006 14:50:22 +0000, Ken Bloom wrote:

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/

heh. i think you may be hitting the same thing i was. here's some food for
thought:

this works:

     harp:~ > cat a.rb
     class C
       include Module.new{ def m() 'a' end }
       include Module.new{ def m() super + 'b' end }
       include Module.new{ def m() super + 'c' end }
     end

     p C.new.m

     harp:~ > ruby a.rb
     "abc"

while this does not:

     harp:~ > cat a.rb
     class C
       def m() '' end

       include Module.new{ def m() super + 'a' end }
       include Module.new{ def m() super + 'b' end }
       include Module.new{ def m() super + 'c' end }
     end

     p C.new.m

     harp:~ > ruby a.rb
     ""

even more distilled:

     harp:~ > cat a.rb
     class C
       def m() '' end

       include Module.new{ def m() 'a' end }
     end

     p C.new.m

     harp:~ > ruby a.rb
     ""

i find that behviour quite suprising... in fact, it seems like a bug, but i'm
probably wrong... thoughts?

cheers.

-a

···

On Fri, 8 Sep 2006, Logan Capaldo wrote:

I have this, it takes a different approach though:

module Patchable
module ClassMethods
   def patch(method_name = nil, &new_body)
     if method_name
       method_name = method_name.to_sym
       pre_patched_versions[method_name] = instance_method(method_name)
       define_method(method_name, &new_body)
     else
       klass = Class.new
       imeths = klass.instance_methods
       klass.class_eval(&new_body)
       new_meths = klass.instance_methods - imeths
       new_meths.each do |m|
         pre_patched_versions[m.to_sym] = instance_method(m)
       end
       class_eval(&new_body)
     end
     self
   end

   def pre_patched_versions
     @pre_patched_versions ||= {}
   end
end

def hyper(*args, &block)
   meth_name = caller[0][/`([^']+)'/, 1].to_sym
   self.class.pre_patched_versions[meth_name].bind(self).call(*args, &block)
end

def self.included(other)
   other.extend(ClassMethods)
end
end

class C
include Patchable

def m
   'a'
end

p new.m
patch do
   def m
     hyper + 'b'
   end
end

p new.m

patch do
   def m
     hyper + 'c'
   end
end

#p new.m doesn't work, infinite recursion
end

Darn. You seem to have made me discover a bug in my impl. It doesn't work for more than one level of patching per method. Well maybe someone will give me a patch too :wink:

--
what science finds to be nonexistent, we must accept as nonexistent; but what
science merely does not find is a completely different matter... it is quite
clear that there are many, many mysterious things.
- h.h. the 14th dalai lama

the show stopper for me is the fact that define_method cannot define methods
that take blocks - so your approach loses them during the alias process.

i remember that define_method will soon have the ability to define methods
that take blocks do you know if that's in 1.8.5 or the 1.9 branch?

cheers.

-a

···

On Fri, 8 Sep 2006, Ken Bloom wrote:

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.

--
what science finds to be nonexistent, we must accept as nonexistent; but what
science merely does not find is a completely different matter... it is quite
clear that there are many, many mysterious things.
- h.h. the 14th dalai lama

The best alias_method methods would be one that didn't exist.

(1) It encourages breaking of naming conventions since at times two functions get defined, one with the convension and one without, (see hash.merge! and hash.update), and I really liked the convention where mutatations have bangs at the end.

(2) This one function double the complexity of learning to read ruby. Now I don't just have to know what hash.update does, but also that hash.merge! is the same thing. And when individual developers start adding aliases to buildins things get ever worse. Array.lower isn't necessary when Array.first and Array[0] are both as short or shorter and just as descriptive.

Alaises increase complexity and really are one of Ruby's biggest dislikes for me.

-j

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

  class C
    def m() 'a' end

    push_method 'm'

    def m() super + 'b' end
  end

  p C.new.m #=> 'ab'

what i've got is quite close, but no cigar. it has a fundemental
problem with
the way ruby scopes super which i'm too tired atttm to figure out.
i'm hoping
i can go to bed and wake up to a nice patch :wink: here's what i've got:

    harp:~ > cat a.rb
    class Module
      def push_method m
        this = self

        include Module.new{
          @m = this.instance_method m

          this.module_eval{ remove_method m }

          module_eval <<-code
            def #{ m }(*a, &b)
              um = ObjectSpace._id2ref #{ @m.object_id }
              um.bind(self).call *a, &b
            end
          code
        }
      end
    end

    class C
      def m
        'a'
      end
      p new.m #=> 'a'

      push_method 'm'

      def m
        super + 'b'
      end
      p new.m #=> 'ab'

      push_method 'm'

      def m
        super + 'c'
      end
      p new.m #=> 'abc'
    end

    harp :~ > ruby a.rb
    "a"
    "ab"
    a.rb:31:in `m': stack level too deep (SystemStackError)
            from (eval):3:in `m'
            from a.rb:31:in `m'
            from (eval):3:in `m'
            from a.rb:31:in `m'
            from (eval):3:in `m'
            from a.rb:31:in `m'
            from (eval):3:in `m'
            from a.rb:31:in `m'
             ... 2343 levels...
            from a.rb:31:in `m'
            from (eval):3:in `m'
            from a.rb:40:in `m'
            from a.rb:42

have at it - i'll be back in 8 hrs. :wink:

I have this, it takes a different approach though:

[Code snipped. You'll have to look above if you want to know how the
broken code works]

Darn. You seem to have made me discover a bug in my impl. It doesn't
work for more than one level of patching per method. Well maybe someone
will give me a patch too :wink:

caller knows a function only by the name you call it.

hence

class A
  def hyper
    p caller
  end

  def a
    hyper
  end
  alias_method :b,:a
end

a=A.new
puts "Calling by a"
a.a
puts "Calling by b"
a.b

gives:
Calling by a
["(irb):7:in `a'", "(irb):14:in `irb_binding'", "/usr/lib/ruby/1.8/irb/workspace.rb:52:in `irb_binding'", ":0"]
Calling by b
["(irb):7:in `b'", "(irb):16:in `irb_binding'", "/usr/lib/ruby/1.8/irb/workspace.rb:52:in `irb_binding'", ":0"]

In your code, we need to do several things:
* rename and call them by their new names to prevent infinite recursion
* keep track of pre_patched_versions correctly when their name changes

So here's my fix for your code, to incorporate these ideas.
I think that if anyone uses straight up alias_method on methods that we
are patching, things will break, so it's probably a good idea to figure out
how to fix that too.

module Patchable
   module ClassMethods
     def unused_alias
       newalias=nil
       while newalias==nil or instance_methods.include?(newalias)
   newalias=:"__kenoverride__#{rand(10**20)}__"
       end
       newalias
     end

     def patch(method_name = nil, &new_body)
       if method_name
         method_name = method_name.to_sym
   w=unused_alias
   alias_method w,method_name
   remove_method method_name
   pre_patched_versions[w]=pre_patched_versions[method_name.to_sym]\
     if pre_patched_versions.include?(method_name.to_sym)
         pre_patched_versions[method_name.to_sym] = w
         define_method(method_name, &new_body)
       else
         klass = Class.new
         imeths = klass.instance_methods
         klass.class_eval(&new_body)
         new_meths = klass.instance_methods - imeths
         new_meths.each do |m|
     w=unused_alias
     alias_method w,m
     remove_method m
     pre_patched_versions[w]=pre_patched_versions[m.to_sym]\
       if pre_patched_versions.include?(m.to_sym)
           pre_patched_versions[m.to_sym] = w
         end
         class_eval(&new_body)
       end
       self
     end

     def pre_patched_versions
       @pre_patched_versions ||= {}
     end
   end

   def hyper(*args, &block)
     meth_name = caller[0][/`([^']+)'/, 1].to_sym
     tocall=self.class.pre_patched_versions[meth_name]
     send(tocall,*args, &block)
   end

   def self.included(other)
     other.extend(ClassMethods)
   end
end

class C
   include Patchable

   def m
     'a'
   end

   p new.m
   patch do
     def m
       raise StandardError if caller.length>10
       hyper + 'b'
     end
   end

   p new.m

   patch do
     def m
       raise StandardError if caller.length>10
       hyper + 'c'
     end
   end

   p new.m

   #you didn't have a test case for this, but I added it

   patch :m do hyper+'d' end
   
   p new.m

end

···

On Fri, 08 Sep 2006 23:09:07 +0900, Logan Capaldo wrote:

On Sep 8, 2006, at 1:34 AM, ara.t.howard@noaa.gov wrote:

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

caller knows a function only by the name you call it.

hence

class A
  def hyper
    p caller
  end

  def a
    hyper
  end
  alias_method :b,:a
end

a=A.new
puts "Calling by a"
a.a
puts "Calling by b"
a.b

gives:
Calling by a
["(irb):7:in `a'", "(irb):14:in `irb_binding'", "/usr/lib/ruby/1.8/irb/workspace.rb:52:in `irb_binding'", ":0"]
Calling by b
["(irb):7:in `b'", "(irb):16:in `irb_binding'", "/usr/lib/ruby/1.8/irb/workspace.rb:52:in `irb_binding'", ":0"]

In your code, we need to do several things:
* rename and call them by their new names to prevent infinite recursion
* keep track of pre_patched_versions correctly when their name changes

So here's my fix for your code, to incorporate these ideas.
I think that if anyone uses straight up alias_method on methods that we
are patching, things will break, so it's probably a good idea to figure out
how to fix that too.

module Patchable
   module ClassMethods
     def unused_alias
       newalias=nil
       while newalias==nil or instance_methods.include?(newalias)
   newalias=:"__kenoverride__#{rand(10**20)}__"
       end
       newalias
     end

     def patch(method_name = nil, &new_body)
       if method_name
         method_name = method_name.to_sym
   w=unused_alias
   alias_method w,method_name
   remove_method method_name
   pre_patched_versions[w]=pre_patched_versions[method_name.to_sym]\
     if pre_patched_versions.include?(method_name.to_sym)
         pre_patched_versions[method_name.to_sym] = w
         define_method(method_name, &new_body)
       else
         klass = Class.new
         imeths = klass.instance_methods
         klass.class_eval(&new_body)
         new_meths = klass.instance_methods - imeths
         new_meths.each do |m|
     w=unused_alias
     alias_method w,m
     remove_method m
     pre_patched_versions[w]=pre_patched_versions[m.to_sym]\
       if pre_patched_versions.include?(m.to_sym)
           pre_patched_versions[m.to_sym] = w
         end
         class_eval(&new_body)
       end
       self
     end

     def pre_patched_versions
       @pre_patched_versions ||= {}
     end
   end

   def hyper(*args, &block)
     meth_name = caller[0][/`([^']+)'/, 1].to_sym
     tocall=self.class.pre_patched_versions[meth_name]
     send(tocall,*args, &block)
   end

   def self.included(other)
     other.extend(ClassMethods)
   end
end

class C
   include Patchable

   def m
     'a'
   end

   p new.m
   patch do
     def m
       raise StandardError if caller.length>10
       hyper + 'b'
     end
   end

   p new.m

   patch do
     def m
       raise StandardError if caller.length>10
       hyper + 'c'
     end
   end

   p new.m

   #you didn't have a test case for this, but I added it

   patch :m do hyper+'d' end
   
   p new.m

end

···

On Fri, 08 Sep 2006 23:09:07 +0900, Logan Capaldo wrote:

On Sep 8, 2006, at 1:34 AM, ara.t.howard@noaa.gov 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

  class C
    def m() 'a' end

    push_method 'm'

    def m() super + 'b' end
  end

  p C.new.m #=> 'ab'

what i've got is quite close, but no cigar. it has a fundemental
problem with
the way ruby scopes super which i'm too tired atttm to figure out.
i'm hoping
i can go to bed and wake up to a nice patch :wink: here's what i've got:

    harp:~ > cat a.rb
    class Module
      def push_method m
        this = self

        include Module.new{
          @m = this.instance_method m

          this.module_eval{ remove_method m }

          module_eval <<-code
            def #{ m }(*a, &b)
              um = ObjectSpace._id2ref #{ @m.object_id }
              um.bind(self).call *a, &b
            end
          code
        }
      end
    end

    class C
      def m
        'a'
      end
      p new.m #=> 'a'

      push_method 'm'

      def m
        super + 'b'
      end
      p new.m #=> 'ab'

      push_method 'm'

      def m
        super + 'c'
      end
      p new.m #=> 'abc'
    end

    harp :~ > ruby a.rb
    "a"
    "ab"
    a.rb:31:in `m': stack level too deep (SystemStackError)
            from (eval):3:in `m'
            from a.rb:31:in `m'
            from (eval):3:in `m'
            from a.rb:31:in `m'
            from (eval):3:in `m'
            from a.rb:31:in `m'
            from (eval):3:in `m'
            from a.rb:31:in `m'
             ... 2343 levels...
            from a.rb:31:in `m'
            from (eval):3:in `m'
            from a.rb:40:in `m'
            from a.rb:42

have at it - i'll be back in 8 hrs. :wink:

I have this, it takes a different approach though:

module Patchable
   module ClassMethods
     def patch(method_name = nil, &new_body)
       if method_name
         method_name = method_name.to_sym
         pre_patched_versions[method_name] = instance_method
(method_name)
         define_method(method_name, &new_body)
       else
         klass = Class.new
         imeths = klass.instance_methods
         klass.class_eval(&new_body)
         new_meths = klass.instance_methods - imeths
         new_meths.each do |m|
           pre_patched_versions[m.to_sym] = instance_method(m)
         end
         class_eval(&new_body)
       end
       self
     end

     def pre_patched_versions
       @pre_patched_versions ||= {}
     end
   end

   def hyper(*args, &block)
     meth_name = caller[0][/`([^']+)'/, 1].to_sym
     self.class.pre_patched_versions[meth_name].bind(self).call
(*args, &block)
   end

   def self.included(other)
     other.extend(ClassMethods)
   end
end

class C
   include Patchable

   def m
     'a'
   end

   p new.m
   patch do
     def m
       hyper + 'b'
     end
   end

   p new.m

   patch do
     def m
       hyper + 'c'
     end
   end

   #p new.m doesn't work, infinite recursion
end

Darn. You seem to have made me discover a bug in my impl. It doesn't
work for more than one level of patching per method. Well maybe
someone will give me a patch too :wink:

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

ara.t.howard@noaa.gov writes:

even more distilled:

    harp:~ > cat a.rb
    class C
      def m() '' end

      include Module.new{ def m() 'a' end }
    end

    p C.new.m

    harp:~ > ruby a.rb
    ""

i find that behviour quite suprising... in fact, it seems like a bug, but i'm
probably wrong... thoughts?

Module inclusion is inheritance, so far as method look-up goes, right?:

    class A
    def m() '' end
    
    include Module.new{ def m() 'a' end }
    end
    
    A.ancestors # => [A, #<Module:0xb7c8d9f0>, Object, Kernel]
    A.new.m # => ""
    
Which is effectively the same as:
    
    class B; end
    
    class C < B
      def m() '' end
    end
    
    class B
      def m() 'a' end
    end
    
    C.ancestors # => [C, B, Object, Kernel]
    C.new.m # => ""
    
Which isn't surprising at all.

-Marshall

the primary reason to use aliases is not to simply have another handle on
names - but override or wrap a method for instance.

   require 'sync'

   class Module
     def ex m
       module_eval{
         include Sync_m
         alias_method "__#{ m }__", "#{ m }"
       }
       module_eval <<-code
         def #{ m } *a, &b
           synchronize{ __#{ m }__ *a, &b }
         end
       code
     end
   end

   class C
     def foobar
       42
     end

     ex :foobar
   end

if you do much metaprogramming and/or dsl contruction you find, in a matter of
seconds, that alias_method is essential. i think most people find the
declarative style of class methods as syntax (think 'attr',
'has_and_belongs_to_many', etc) to be one of ruby's best features.
unfortunately it oftern hinges on alias_method which has issues if a cycle is
setup.

regards.

-a

···

On Sat, 9 Sep 2006, Jason Nordwick wrote:

The best alias_method methods would be one that didn't exist. (1) It encourages breaking of naming conventions since at times two functions get defined, one with the convension and one without, (see hash.merge! and hash.update), and I really liked the convention where mutatations have bangs at the end.

(2) This one function double the complexity of learning to read ruby. Now I don't just have to know what hash.update does, but also that hash.merge! is the same thing. And when individual developers start adding aliases to buildins things get ever worse. Array.lower isn't necessary when Array.first and Array[0] are both as short or shorter and just as descriptive.

Alaises increase complexity and really are one of Ruby's biggest dislikes for me.

--
what science finds to be nonexistent, we must accept as nonexistent; but what
science merely does not find is a completely different matter... it is quite
clear that there are many, many mysterious things.
- h.h. the 14th dalai lama

Not at all becasue inclusion adds to the inheritance chain, it doesn't
inject methods. You can of course define an #inject method if you like.
But it's still difficult to do the orginial intent of this thread b/c
of how #super works.

Personally I think it would cool if you could redefine a method calling
on the previous definition just as one can redefine a variable calling
on it's previous definition. Eg.

  a = 1
  a = a + 1

  def a ; 1 ; end
  def a ; a + 1 ; end

This would eliminate the need for #super except in cases of recursion,
in which case a special notation is also needed for the definition. Eg.
def_rec a, or something. But that's cool b/c it would be safer.

In anycase, alternate solutions to the alias issue include
#wrap_method, #alias_chain, Matz' :before and :after system (semi-aop)
and cuts (aop).

T.

···

ara.t.howard@noaa.gov wrote:

heh. i think you may be hitting the same thing i was. here's some food for
thought:

this works:

     harp:~ > cat a.rb
     class C
       include Module.new{ def m() 'a' end }
       include Module.new{ def m() super + 'b' end }
       include Module.new{ def m() super + 'c' end }
     end

     p C.new.m

     harp:~ > ruby a.rb
     "abc"

while this does not:

     harp:~ > cat a.rb
     class C
       def m() '' end

       include Module.new{ def m() super + 'a' end }
       include Module.new{ def m() super + 'b' end }
       include Module.new{ def m() super + 'c' end }
     end

     p C.new.m

     harp:~ > ruby a.rb
     ""

even more distilled:

     harp:~ > cat a.rb
     class C
       def m() '' end

       include Module.new{ def m() 'a' end }
     end

     p C.new.m

     harp:~ > ruby a.rb
     ""

i find that behviour quite suprising... in fact, it seems like a bug, but i'm
probably wrong... thoughts?

It's been in 1.9 for quite a long time now.

···

On Sat, Sep 09, 2006 at 12:20:51AM +0900, ara.t.howard@noaa.gov wrote:

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

the show stopper for me is the fact that define_method cannot define methods
that take blocks - so your approach loses them during the alias process.

i remember that define_method will soon have the ability to define methods
that take blocks do you know if that's in 1.8.5 or the 1.9 branch?

--
Mauricio Fernandez - http://eigenclass.org - singular Ruby

Having established that 1.9 has this feature, my #override method (and
also Facets' #wrap_method should Just Work (TM) in 1.9 with defining
methods that take blocks.

--Ken

···

On Sat, 09 Sep 2006 00:20:51 +0900, ara.t.howard wrote:

On Fri, 8 Sep 2006, Ken Bloom wrote:

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.

the show stopper for me is the fact that define_method cannot define methods
that take blocks - so your approach loses them during the alias process.

i remember that define_method will soon have the ability to define methods
that take blocks do you know if that's in 1.8.5 or the 1.9 branch?

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

<snip>

indeed. hadn't consider it that way.

thanks.

-a

···

On Sat, 9 Sep 2006, Marshall T. Vandegrift wrote:

ara.t.howard@noaa.gov writes:

even more distilled:

    harp:~ > cat a.rb
    class C
      def m() '' end

      include Module.new{ def m() 'a' end }
    end

    p C.new.m

    harp:~ > ruby a.rb
    ""

i find that behviour quite suprising... in fact, it seems like a bug, but i'm
probably wrong... thoughts?

Module inclusion is inheritance, so far as method look-up goes, right?:

--
what science finds to be nonexistent, we must accept as nonexistent; but what
science merely does not find is a completely different matter... it is quite
clear that there are many, many mysterious things.
- h.h. the 14th dalai lama

The solution to this problem will actually let alias_method fall into
disuse faster, since we don't need to use alias_method directly to wrap or
override an old method anymore.

--Ken

···

On Sat, 09 Sep 2006 01:17:46 +0900, ara.t.howard wrote:

On Sat, 9 Sep 2006, Jason Nordwick wrote:

The best alias_method methods would be one that didn't exist.
(1) It encourages breaking of naming conventions since at times two functions
get defined, one with the convension and one without, (see hash.merge! and
hash.update), and I really liked the convention where mutatations have bangs
at the end.

(2) This one function double the complexity of learning to read ruby. Now I
don't just have to know what hash.update does, but also that hash.merge! is
the same thing. And when individual developers start adding aliases to
buildins things get ever worse. Array.lower isn't necessary when Array.first
and Array[0] are both as short or shorter and just as descriptive.

Alaises increase complexity and really are one of Ruby's biggest dislikes for
me.

the primary reason to use aliases is not to simply have another handle on
names - but override or wrap a method for instance.

   require 'sync'

   class Module
     def ex m
       module_eval{
         include Sync_m
         alias_method "__#{ m }__", "#{ m }"
       }
       module_eval <<-code
         def #{ m } *a, &b
           synchronize{ __#{ m }__ *a, &b }
         end
       code
     end
   end

   class C
     def foobar
       42
     end

     ex :foobar
   end

if you do much metaprogramming and/or dsl contruction you find, in a matter of
seconds, that alias_method is essential. i think most people find the
declarative style of class methods as syntax (think 'attr',
'has_and_belongs_to_many', etc) to be one of ruby's best features.
unfortunately it oftern hinges on alias_method which has issues if a cycle is
setup.

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

i thought so. still, i'm unclear whether or not it'll make the task any
easier. anyhow... i've made progress. here's the latest:

btw - i like the look of call_stack - good work!

the latest, it gets over the redef issue:

harp:~ > ruby a.rb
"a"
"ab"
"abc"

harp:~ > cat a.rb
class Module
   def child this = self
     @child ||= self.class.new
     @child.module_eval{ include this}
     @child
   end

   def has_child
     defined? @child and @child
   end

   def override m, &b
     this = self

     m = Module.new{
       @m = this.instance_method m
       this.module_eval{ remove_method m rescue nil }

       module_eval <<-code
         def #{ m }(*a, &b)
           um = ObjectSpace._id2ref #{ @m.object_id }
           um.bind(self).call *a, &b
         end
       code

       child.module_eval &b if b # put super into right scope!
     }

     include(m.has_child ? m.child : m)
   end
end

class C
   def m() 'a' end
   p new.m #=> 'a'

   override('m'){ def m() super + 'b' end }
   p new.m #=> 'ab'

   override('m'){ def m() super + 'c' end }
   p new.m #=> 'abc'
end

if course, the cool thing is that it even works for blocks:

   harp:~ > cat a.rb
   require 'override'

   class C
     def m(&b) b.call end

     override(:m){ def m(&b) super + 2 end }
   end

   p C.new.m{ 40 }

   harp:~ > ruby a.rb
   42

now - making it undoable, abstracting, etc. is a lot more work...

-a

···

On Sat, 9 Sep 2006, Mauricio Fernandez wrote:

On Sat, Sep 09, 2006 at 12:20:51AM +0900, ara.t.howard@noaa.gov wrote:

On Fri, 8 Sep 2006, Ken Bloom wrote:

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

the show stopper for me is the fact that define_method cannot define methods
that take blocks - so your approach loses them during the alias process.

i remember that define_method will soon have the ability to define methods
that take blocks do you know if that's in 1.8.5 or the 1.9 branch?

It's been in 1.9 for quite a long time now.

--
what science finds to be nonexistent, we must accept as nonexistent; but what
science merely does not find is a completely different matter... it is quite
clear that there are many, many mysterious things.
- h.h. the 14th dalai lama

heh. i think you may be hitting the same thing i was. here's some food for
thought:

this works:

     harp:~ > cat a.rb
     class C
       include Module.new{ def m() 'a' end }
       include Module.new{ def m() super + 'b' end }
       include Module.new{ def m() super + 'c' end }
     end

     p C.new.m

     harp:~ > ruby a.rb
     "abc"

while this does not:

     harp:~ > cat a.rb
     class C
       def m() '' end

       include Module.new{ def m() super + 'a' end }
       include Module.new{ def m() super + 'b' end }
       include Module.new{ def m() super + 'c' end }
     end

     p C.new.m

     harp:~ > ruby a.rb
     ""

even more distilled:

     harp:~ > cat a.rb
     class C
       def m() '' end

       include Module.new{ def m() 'a' end }
     end

     p C.new.m

     harp:~ > ruby a.rb
     ""

i find that behviour quite suprising... in fact, it seems like a bug, but i'm
probably wrong... thoughts?

Not at all becasue inclusion adds to the inheritance chain, it doesn't
inject methods. You can of course define an #inject method if you like.
But it's still difficult to do the orginial intent of this thread b/c
of how #super works.

Personally I think it would cool if you could redefine a method calling
on the previous definition just as one can redefine a variable calling
on it's previous definition. Eg.

  a = 1
  a = a + 1

  def a ; 1 ; end
  def a ; a + 1 ; end

This would eliminate the need for #super except in cases of recursion,
in which case a special notation is also needed for the definition. Eg.
def_rec a, or something. But that's cool b/c it would be safer.

In anycase, alternate solutions to the alias issue include
#wrap_method,

see http://www.erikveen.dds.nl/monitorfunctions/index.html#implementation

#alias_chain, Matz' :before and :after system (semi-aop) and cuts (aop).

Where is there information about these?

--Ken

···

On Sat, 09 Sep 2006 02:54:35 +0900, Trans wrote:

ara.t.howard@noaa.gov wrote:

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

  a = 1
  a = a + 1

  def a ; 1 ; end
  def a ; a + 1 ; end

This would eliminate the need for #super except in cases of recursion,
in which case a special notation is also needed for the definition. Eg.
def_rec a, or something. But that's cool b/c it would be safer.

I don't see how you write "recursive methods which call super".

···

--
Sylvain Joyeux