Search code examples
rubyoopmetaprogrammingmultiple-inheritance

Specifying a parent class while using send to call from an instance of an object in ruby


So, as far as I know, the way to simulate/have multiple inheritances in ruby is through modules (if there is another/better pls let me know 👌), so let's say I have the following structure/architecture:

class A
    def foo
        p "Foo from A"
    end
end

module B
    def foo
        p "Foo from B"
    end
end

class C < A
    include B
end

c = C.new

c.send('foo')

The code above as a lot of you already know will print Foo from B, because the send function will look inside the class C and since there is no definition for the foo function it will look for that function within C ancestors, so my question (maybe not a smart one), is there any way to specify/prioritize an ancestor of C while calling send/or a workaround for that behavior (don't want to instantiate the parent class I need it in this way)? I couldn't find anything in the documentation of the send function.

EDITED

The architecture shown above is a legacy code, my intention is to find a workaround in order to not be disruptive with the existing code (that's why I have that architecture)

Thx in advance 👍.


Solution

  • Prepend Your "Preferred" Module

    It's unclear why you're trying to do things this way, as you seem to have landed on a desired solution without clearly defining the problem it's meant to solve. Since A and B both have a #foo method, which one is called first will depend on the order in which the method lookup is performed. For example, with your current code:

    c.class.included_modules
    #=> [B, Kernel]
    
    c.class.ancestors
    #=> [C, B, A, Object, Kernel, BasicObject]
    

    In your current code, c.send 'foo' will call B#foo because it will be the first object in the lookup that can respond_to? :foo. The only way to change the lookup is to explicitly call A#foo from within the C class, or rewrite A as a module and use Module#prepend to insert it at the front of the lookup before B or C. For example:

    module A
        def foo; p 'Foo from A'; end
    end
    
    module B
        def foo; p 'Foo from B'; end
    end
    
    class C
        include B
        prepend A
    end
    
    C.ancestors
    #=> [A, C, B, Object, Kernel, BasicObject]
    
    C.new.foo
    #=> "Foo from A"