Dear,
Though mixins are a very nice alternative for multiple inheritance,
they do sometimes cause name crashes that are quite annoying. In a
recent project I was working on, a design choice I made is that
everything would be destroyed with the method 'teardown'. What I mean
is that connections are closed, files are closed, etc etc.. As I was
refactoring the code, I noticed two pieces of code that could each be
put into their own module (factored out). However this led to the fact
that both the class that included the module as well as the super class
of that class and the class itself had the method teardown. I could've
chosen to rename the method inside the extracted module, but instead I
came up with quite an interesting and reusable pattern.
The idea is that if you have a module with certain methods, you rename
those methods to modulenameinlowercase_methodname in the module. It is
important that for this to work, the module must be included in the
class after any methods with similar names have been defined.
(Preferably at the end of the class).
Here is the code to the module that contains the necessary
functionality:
···
###############################################################################
module Extensible
def self.define_aliases(mod, cl, methods)
methods.each do |name|
mod_name = mod.name.split("::").last.downcase
if cl.method_defined?(name)
num = 1
while cl.method_defined?("old_#{num}_#{name}")
num += 1
end
old_name = "old_#{num}_#{name}"
cl.module_eval %Q|
alias #{old_name} #{name}
def #{name}(*args)
self.#{mod_name}_#{name} *args
self.#{old_name} *args
end
>
else
cl.module_eval %Q|
alias #{name} #{mod_name}_#{name}
>
end
end
end
end
###############################################################################
It is used as follows in a module that contains functionality that must
be mixed in:
###############################################################################
module A1
def self.append_features(cl)
super
methods = %q{test}.split
Util.define_aliases(self, cl, methods)
end
def a1_test
puts "A1"
end
end
###############################################################################
Examples:
###############################################################################
class B1
def test
puts "B1"
end
end
class C1 < B1
def test
super
puts "C1"
end
include A1
end
C1.new.test =>
A1
B1
C1
###############################################################################
class D1 < B1
include A1
end
D1.new.test =>
A1
B1
###############################################################################
class E1
include A1
end
E1.new.test =>
A1
###############################################################################
class B2
def test
puts "B2"
end
end
class C2 < B2
def test
super
puts "C2"
end
include A2
include A1
end
C2.new.test =>
A1
A2
B2
C2
###############################################################################
class D2 < B2
include A2
include A1
end
D2.new.test =>
A1
A2
B2
###############################################################################
class E2
include A2
include A1
end
E2.new.test =>
A1
A2
###############################################################################
Of course this still needs to be extended to work with objects as well,
but I thought I would share this.
With regards,
Christophe