self vs extend self vs module_function

In Ruby we have at least 3 ways to expose a function so it can be called directly on the module:

define a method with self prefix

define a method and use extend self

define a method and use module_function

Let’s compare them and see how they work.

self

moduleAdefself.fooputs"I'am A.foo"endendA.foo#=> I'am A.foo

When you define a method with self prefix it becomes defined on the module eigenclass. If you’ve never heard about eigenclass I recommend you to check this article. In simple words eigenclass is a unique class associated with every Ruby object which allows you to define methods for it.

But there is a small difference between those 2 solutions: module_function copies the function so if you try to override the original (instance) one, the change won’t propagated unless you call module_function once again:

Looks almost the same except that C module exists in the C’c eigenclass ancestors. You might ask if it’s a big deal. In some cases it might be. Let me show you an example:

Imagine that you would like to override original method but still be able to call it from there.

Unfortunately methods defined using self or exposed with module_function were defined on the eigenclass so we are not able to use super when we are overriding methods directly (alternative solution is shown below):

moduleAdefself.fooputs"I'm a new A.foo"superendendA.foo#=> I'm a new A.foo#=> NoMethodError: super: no superclass method `foo' for A:Module#=> Did you mean? fork

Let’s check B now:

moduleBdefself.fooputs"I'm a new B.foo"superendendB.foo#=> I'm a new B.foo#=> NoMethodError: super: no superclass method `foo' for B:Module#=> Did you mean? fork

And finally extend self:

moduleCdefself.fooputs"I'm a new C.foo"superendendC.foo#=> I'm a new C.foo#=> I'am C.foo

Because C is also an ancestor of C’s eigenclass, we are able to call super from overridden method.

As I mentioned above there is a solution to override A.foo and B.foo methods - it requires using Module#prepend method in order to place a module (it can be a anonymous one) in front of A’s and B’c eigenclasses: