Prasad Surase
Prasad Surase

Reputation: 6574

What happens internally when a module is extended in Ruby?

Foo = Module.new

class MyClass
  include Foo
end

When a module is included in a class, an anonymous proxy class is created and set as MyClass's superclass.

MyClass.ancestors => [MyClass, Foo, ...]

But what happens internally when a module is extended? How does Ruby handle this?

Upvotes: 2

Views: 353

Answers (4)

Mike Li
Mike Li

Reputation: 3406

I think what you ask is Object#extend

So with extend, I can include any module's methods into that object. For example I have a module called HelperModule:

module HelperModule
  def foo
    puts "from module helper"
  end
end

Usage Example 1:

obj = Object.new
obj.extend HelperModule
obj.foo                  # => "from module helper"

Usage Example 2:

class MyClass
  extend HelperModule
end
MyClass.foo              # => "from module helper"

Internally, according to Metaprogramming Ruby:

Object#extend() is simply a shortcut that includes a module in the receiver’s eigenclass.


A brief explanation of ruby's methods call :

  1. find the object's class, see if the method defined there
  2. find that class's superclass, see if the method defined there

obj 
 |
 | class
 |                     superclass                  superclass
 --->  ObjectClass  --------------> SuperClass1 --------------> SuperClass2 ....

The detailed explanation about eigenclass and method call path, please reference this awesome book Metaprogramming Ruby

Thanks

Upvotes: 5

Daniel Brady
Daniel Brady

Reputation: 954

In a nutshell, Ruby's include method will emulate inheritance: it will allow the including class access to the included module's instance methods, variables and constants as if they had been defined in the including class itself. Ruby does this by creating an anonymous proxy class (known as an eigenclass or singleton class), as you mentioned, which references the included module, and inserting it into the ancestors of the including class as the immediate superclass. This is what is known as a mixin in Ruby.

Using extend, however, interacts with the singleton class a little bit more:

module Foo
  def baz
    'baz'
  end
end

module Blah
  def blah
    'blah'
  end
end

class Bar
  extend Foo
  include Blah
  def self.magic
    'magic'
  end
end

Extending Foo in Bar is identical (right down to its implementation in C) to saying

Bar.singleton_class.send( :include, Foo )

This means that it is the singleton class of Bar in which the methods and constants of Foo are essentially embedded, and thus class Bar, being an instance of its singleton class, will inherit this new functionality as so-called 'class' methods. In Ruby, modules and classes can only have instance methods, and thus the creation of a 'class' method will actually just create an instance method in the class's singleton class, to be inherited in this manner.

Bar.baz
# => 'baz'
Bar.magic
# => 'magic'
Bar.blah
# => NoMethodError
Bar.new.baz
# => NoMethodError
Bar.new.magic
# => NoMethodError
Bar.new.blah
# => 'blah'

Here you can see the differences in behavior of include and extend. To verify their behavior with regards to class ancestry, you can ask for the ancestors of Bar and its singleton class, and it will show you that module Blah is the immediate parent of Bar, but module Foo is the immediate parent of the singleton class.

Bar.ancestors
# => [Bar, Blah, Object, Kernel, BasicObject]
Bar.singleton_class.ancestors
# => [Foo, Class, Module, Object, Kernel, BasicObject]

From these results, you can see how inclusion emulates inheritance in the including class, and how extension is merely inclusion within the singleton class.

You might try looking at the answers I got for this question awhile back; they did an excellent job of explaining the behavior of the include and extend functionality. This article has also helped me understand their differences.

Upvotes: 0

severin
severin

Reputation: 10258

obj.extend SomeModule is the same as obj.eigenclass.include SomeModule (Note: this is just pseudocode, but you will get the idea...).

Upvotes: 0

Jörg W Mittag
Jörg W Mittag

Reputation: 369428

Whats happens internally when a module in extended in Ruby?

When a module M is included in a class C, an anonymous proxy class ⟦M′⟧ (called an include class) is created such that its method table pointer points to M's method table. (Same for the constant table and module variables.) ⟦M′⟧'s superclass is set to C's superclass and C's superclass is set to ⟦M′⟧.

Also, if M includes other modules, the process is applied recursively.

Actually, that's just the default behavior. What really happens is that include calls M.append_features(C), and you can customize all of that behavior by overriding that method.

I find the source code of Module#append_features in Rubinius quite readable.

Upvotes: 0

Related Questions