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.