Interesting class

Discussion in 'Ruby' started by christophe.poucet@gmail.com, Sep 20, 2005.

  1. Guest

    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
     
    , Sep 20, 2005
    #1
    1. Advertising

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


    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
     
    Robert Klemme, Sep 20, 2005
    #2
    1. Advertising

  3. Guest

    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
     
    , Sep 20, 2005
    #3
  4. 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


    Why? Did you test my approach?

    robert
     
    Robert Klemme, Sep 20, 2005
    #4
  5. Guest

    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 :/

    Thanks for the suggestion, robert.

    Christophe
     
    , Sep 20, 2005
    #5
  6. 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 :/
    >
    > Thanks for the suggestion, robert.


    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
     
    Robert Klemme, Sep 20, 2005
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. E11
    Replies:
    1
    Views:
    4,785
    Thomas Weidenfeller
    Oct 12, 2005
  2. christopher diggins
    Replies:
    16
    Views:
    756
    Pete Becker
    May 4, 2005
  3. Replies:
    0
    Views:
    296
  4. Joseph Turian
    Replies:
    5
    Views:
    595
  5. Charles Comstock

    Class#=== has interesting results

    Charles Comstock, Jul 7, 2004, in forum: Ruby
    Replies:
    19
    Views:
    189
    David A. Black
    Jul 8, 2004
Loading...

Share This Page