Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?

Consider the following:

module Foo
  def a
    puts "Foo#a"
  end

  def b(*args, **opts)
    puts "Foo#b, args=#{args.inspect}\topts=#{opts.inspect}"
  end
end

class Bar
  include Foo

  def a(blut)
    puts "Bar#a, blut=#{blut.inspect}"
    super()
  end
end

bar = Bar.new

Now, if we call bar.a without passing any arguments, it won't work:

pry(main)> bar.a
ArgumentError: wrong number of arguments (given 0, expected 1)
from (pry):13:in `a'
pry(main)> 

If we call bar.a and pass a parameter in, we get

pry(main)> bar.a('whatever')
Bar#a, blut="whatever"
Foo#a
=> nil
pry(main)> 

Notice that in the code for Bar#a, we called super(). This means "call the next ancestor method of this one, passing in no parameters". If we'd just typed super instead, it would have called the ancestor method with the same parameters as were passed into this method, which obviously won't work.

Finally, if we ask to see the ancestor classes of the object bar, we get

pry(main)> bar.class.ancestors
=> [Bar, Foo, Object, PP::ObjectMixin, Kernel, BasicObject]
pry(main)> 

This shows Foo as the immediate ancestor of Bar, even though Foo is a module, not a class. Its methods can be overridden and added to by a subclass, just as you'd expect from single inheritance. Looking at the code for Bar, it doesn't have an explicit parent class like

class StringFoo < String
  def foo
    "FOO " + to_s + " FOO"
  end
end

But by including modules, a class (or another module) can have any number of ancestors, providing what is in effect multiple inheritance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment