Reputation: 909
In the following, A
inherits F
, which inherits E
, so calling initialize
on an A
instance calls A#initialize
, which has priority over E#initialize
and F#initialize
.
module E
def initialize(e)
@e = e
end
def e
@e
end
end
module F
def initialize(f)
@f = f
end
def f
@f
end
end
class A
include E
include F
def initialize(e, f)
# ...
end
end
I need to refer to both E#initialize
and F#initialize
from within the method body of A#initialize
, passing e
and f
respectively as the argument so that I get this result:
a = A.new("foo", "bar")
a.e # => "foo"
a.f # => "bar"
Is there a way to refer to those methods?
Upvotes: 0
Views: 504
Reputation: 110675
You can use Method#super_method for this.
module E
def initialize(e)
@e = e
end
def e
@e
end
end
module F
def initialize(f)
@f = f
end
def f
@f
end
end
class A
include E
include F
def initialize(e, f)
select_initialize(E).call e
select_initialize(F).call f
end
private
def select_initialize(mod)
self.class.
ancestors.
index(mod).
times.
reduce(method(:initialize)) { |m,_| m.super_method }
end
end
puts A.new("E", "F").f
#=> F
puts A.new("E", "F").e
#=> E
Note:
A.ancestors
#=> [A, F, E, Object, Kernel, BasicObject]
See also Module#ancestors, Array#index, Integer#times, Enumerable#reduce (aka inject
), Object#method and Method#call.
E
and F
may of course contain other instance methods that are needed by instances of the class.
Upvotes: 3
Reputation: 198324
The problem for you is that there is no multiple inheritance in Ruby; so the modules get inserted as an ancestor one above the other, and as such F#initialize
overshadows E#initialize
. As you found, it is easy to access F#initialize
using super(f)
; but the other one needs a hack to access: we can pick the initialize
method directly off of the module, because it is an ancestor; then bind it to the current object and run it.
def initialize(e, f)
E.instance_method(:initialize).bind(self).call(e)
F.instance_method(:initialize).bind(self).call(f) # equivalent to super(f)
end
However, I do not recommend this. If you need to run several initialisers, you'd be better off using composition, rather than inheritance (and to be clear, include
is inheritance).
Upvotes: 4