Sig
Sig

Reputation: 5916

Delegating to a method defined in an included module

Is there a way in Ruby (2.5) to call a method defined in an included module in the same way super works for inheritance?

I mean, supposing I have the following

class Parent
 def name
  "parent"
 end
end

class Child < Parent
  def name
   "my parent is #{super}"
  end
end

When I call Child.new.name, I get my parent is parent.

How can I achieve the same in the following situation?

module Parent
 def name
  "parent"
 end
end

class Child 
  include Parent

  def name
   "my parent is #{???}"
  end
end

What should I replace ??? with?

Thank you

Upvotes: 1

Views: 344

Answers (2)

Cary Swoveland
Cary Swoveland

Reputation: 110665

We are given the module Parent:

module Parent
  def name
    "parent"
  end
end

and the class Child:

class Child 
  include Parent    
  def name
    "my parent is #{super}"
  end
end

As @Tai points out, we can use super to obtain:

Child.new.name
  #=> "my parent is parent"

because

Child.ancestors
  #=> [Child, Parent, Object, Kernel, BasicObject]

In what sense is the module Parent the "parent" of the class Child? Yes, those are just words, but it is misleading.

Now let's create another module and have Child include it.

module A
  def name
    "A"
  end
end

Child.include A
Child.new.name
  #=> "my parent is A"

because

Child.ancestors
  #=> [Child, A, Parent, Object, Kernel, BasicObject]

We see that it would been clearer to define Class#name as follows:

def name
  "the last module included is #{super}"
end

Let's try one more thing: using Module#prepend.

module B
  def name
    "B"
  end
end

Child.prepend B
Child.new.name
  #=> "B"

not "my parent is B", because

Child.ancestors
  #=> [B, Child, A, Parent, Object, Kernel, BasicObject]

meaning that B#name (which does not call super) was executed (not Child#name). This means the results are meaningless if any modules have been prepended.

Upvotes: 2

Tai
Tai

Reputation: 1254

You can still use super:

module Parent
 def name
  "parent"
 end
end

class Child 
  include Parent

  def name
   "my parent is #{super}"
  end
end

Child.new.name # => "my parent is parent"

When including a module, ruby will add this module to inheritance chain.

Child.ancestors # => [Child, Parent, Object, Kernel, BasicObject]

Thus, calling super in Child will call the corresponding methods in its ancestors.

Upvotes: 6

Related Questions