eigenclass logo
MAIN  Index  Search  Changes  PageRank  Login

Picking and renaming methods from included modules---tastes like Eiffel?

Daniel Berger blogged about how to make 'include' a little more flexible by allowing to rename or exclude specific methods.

He proposed the following syntax:

module TFoo
    def method_a
       "hello"
    end
    def method_b
       "world"
    end
end

module TBar
    def method_a
       "goodbye"
    end
    def method_b
       "cruel world"
    end
    def method_y
       "huh?"
    end
end

class MyKlass
    include TFoo,
      :alias   => {:method_a, :method_z},
      :exclude => :method_b

    include TBar
end

m = MyKlass.new
m.method_a                                         # => "goodbye"
m.method_b                                         # => "cruel world"
m.method_y                                         # => "huh?"
m.method_z                                         # => "hello"

Making it happen

Here's my implementation. I think somebody might have posted such a thing to ruby-talk, but anyway this must have been written for the first time by some Japanese Rubyist years ago, so yet another reinvention won't matter nor hurt.

This definition of Class#include should be a fairly safe replacement for the default one since I'm not raising an exception when there's a nameclash (which I rather feel is a bad idea, but I haven't given this much thought), but maybe it could make sense to create a new method with those semantics.

The code:


include_and_rename.rb

class Class
  old_include = instance_method(:include)
  define_method(:include) do |*args|
    default = {:alias => {}, :exclude => []}
    hash_arg = (Hash === args.last) ? default.update(args.pop) : default
    m = Module.new
    args.each{|mod| m.module_eval{ include mod } }
    #                            ^^^^^^^^^^^^^^^
    # check for "method shadowing" here and raise an exception or
    # something if you feel like it
    hash_arg[:alias].each_pair do |old, new|
      m.module_eval{ alias_method(new, old); undef_method(old) }
    end
    excluded = (Array === hash_arg[:exclude]) ? hash_arg[:exclude] : [hash_arg[:exclude]]
    # [*hash_arg[:exclude]] won't work on 1.9 cause there's no Object#to_a
    excluded.each{|meth| m.module_eval { undef_method(meth) } }
    old_include.bind(self).call(m)
  end
end


Alternate syntax - lukfugl (2005-12-01 (Thr) 15:40:05)

Read my comments on an alternate syntax with implementation (shamelessly based on yours here) at RedHanded:

http://redhanded.hobix.com/inspect/mixinsButWithTablespoons.html


mfp 2005-12-01 (Thr) 15:48:05

heh just saw your 1st comment on redhanded and was about to implement it but you posted yours faster :)

Facets - Trans (2005-12-01 (Thr) 10:50:02)

Facets has some methods for doing this kind of thing a little more along the lines of how Ruby handles methods:

 class MyKlass
   integrate TFoo do
     rename :method_a, :method_z
     remove :method_b
   end
   include TBar
 end

mfp 2005-12-01 (Thr) 17:19:17

Is it based on the same fundamental operation (creating a new module and operating on it)?

Nice - Daniel Berger (2005-12-01 (Thr) 06:25:31)

Very nice. I'm still undecided about what should happen with a nameclash, but I'm leaning towards a warning rather than an exception. Just knowing that it's happening (with -w) could help resolve an otherwise hidden bug.

Now, any chance of a patch to class.c? :)


mfp 2005-12-01 (Thr) 08:37:37

Wouldn't it be shot down on first sight if sent to ruby-core? ;-) I don't know if the uebermenschen have an official position on this (remember I wrote it because I was too lazy to search the archives :)


Daniel Berger 2005-12-01 (Thr) 09:26:49

Maybe, maybe not. It has a decent chance of making it into Sydney :)


Mark Hubbart 2005-12-01 (Thr) 20:06:07

I particularly like the ':alias => {:orig => :new}' syntax. I wish alias_method would take a hash; I think it would be much clearer than 'alias_method :new, :old'.

Last modified:2005/12/01 11:31:23
Keyword(s):[blog] [ruby] [module] [include] [snippet]
References:[Ruby]