Interesting class

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

Wouldn't it be simpler to do

class Object
  def teardown() end
end

and in each module and class

module Foo
  def teardown
    # local cleanup
    super
  end
end

?

Kind regards

    robert

···

christophe.poucet@gmail.com wrote:

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).

Not if you have the following pattern:

class Base
  def teardown
    # do a
  end
end

module Foo
  def teardown
    # do b
  end
end

class Child < B
  include Foo
  def teardown
    # call super and my own Foo.teardown somehow
    # do c
  end
end

Regards,
Christophe

Why? Did you test my approach?

    robert

···

christophe.poucet@gmail.com wrote:

Not if you have the following pattern:

class Base
def teardown
   # do a
end
end

module Foo
def teardown
   # do b
end
end

class Child < B
include Foo
def teardown
   # call super and my own Foo.teardown somehow
   # do c
end
end

Regards,
Christophe

You are correct, which is rather odd as I would think that the teardown
defined in Child would override the teardown defined in Foo.

Well I guess the submitted code is pointless :confused:

Thanks for the suggestion, robert.

Christophe

You're welcome! As an additional hint to an explanation you can look at the inheritance chain by invoking #ancestors:

$ ruby -e 'module Mod end; class Base; end; class Derived < Base; include Mod end; p Derived.ancestors'
[Derived, Mod, Base, Object, Kernel]

That's exactly the line along which super works.

Kind regards

    robert

···

christophe.poucet@gmail.com wrote:

You are correct, which is rather odd as I would think that the
teardown defined in Child would override the teardown defined in Foo.

Well I guess the submitted code is pointless :confused:

Thanks for the suggestion, robert.