I'm trying to better understand how modules extend and include each other.
Say I have module A:
module A
def learned_from_A
true
end
end
A.instance_methods # [:learned_from_A]
I mix its bag of tricks into B:
module B
extend A
end
B.learned_from_A # true
I naively attempt to give C everything B has:
module C
extend B
end
C.learned_from_A # NoMethodError
I think I've wrapped my head around this. When B extends A, copies of A's instance methods are bound to B via B's singleton class:
B.singleton_methods # [:learned_from_A]
While :learned_from_A is callable on B, it's not one of B's instance methods, so when C extends B, :learned_from_A is not copied to C.
If B had instead included A, copies of A's instance methods would've been included among B's own instance methods.
module B
include A
end
B.instance_methods # [:learned_from_A]
Then, C could extend B, and all of B's instance methods (including :learned_from_A) would be copied and bound to C.
module C
extend B
end
C.singleton_methods # [:learned_from_A]
To make :learned_from_A callable on both B and C, B could extend and include A.
module B
include A
extend A
end
B.instance_methods # [:learned_from_A]
B.singleton_methods # [:learned_from_A]
module C
extend B
end
C.instance_methods # []
C.singleton_methods # [:learned_from_A]
More realistically, if I want A's methods to be callable on B, and for B to define another method of its own, and be able to mix the whole repertoire into C, I can't do this:
module B
extend A
include A
def self.buzz
true
end
end
module C
extend B
end
B can only share its instance methods, not its singleton methods. So to make a method both callable on B and shareable to other objects, it must be defined as an instance method and extended into B itself:
module B
extend A
include A
extend self
def buzz
true
end
end
module C
extend B
end
There was a fair amount of trial and error in putting this all together. Is it an accurate way of viewing what's going on?
In simple words, the difference between include and extend is that 'include' is for adding methods only to an instance of a class and 'extend' is for adding methods to the class but not to its instance.
The only difference is where in the ancestor chain the module is added. With include , the module is added after the class in the ancestor chain. With prepend, the module is added before the class in the ancestor chain.
The Ruby class Class inherits from Module and adds things like instantiation, properties, etc – all things you would normally think a class would have. Because Module is literally an ancestor of Class , this means Modules can be treated like classes in some ways.
include? is a String class method in Ruby which is used to return true if the given string contains the given string or character.
When you extend a module, ruby will provide the module's methods to the class as class methods. However, unlike with include, when you extend a module, Ruby will not insert the module into the class's ancestry chain. We can see this by calling .ancestors on our Rabbit class. The third keyword for providing a module's methods to a class is prepend.
With extend, we have only given the method to the class as a class method. When you extend a module, ruby will provide the module's methods to the class as class methods. However, unlike with include, when you extend a module, Ruby will not insert the module into the class's ancestry chain. We can see this by calling .ancestors on our Rabbit class.
Ruby - Modules and Mixins. Modules are a way of grouping together methods, classes, and constants. Modules give you two major benefits. Modules provide a namespace and prevent name clashes.
As the title of this post says, you can include and also extend modules, but what does it means to extend a module? When you extend a module, you are adding the methods of that specific module into the object instance you call “extend”.
I mix its bag of tricks into B
This phrase and your question in general made me believe there is a little misunderstanding in concept of include
/extend
thing. I apologize in advance, because I don't fully understand the question.
For example you have such module:
module A
def a
puts "a"
end
def self.b
puts "b"
end
end
As you see there are 2 types of methods:
Here is the easiest way to show that they actually differ:
A.singleton_methods
=> [:b]
A.instance_methods
=> [:a]
A.a
NoMethodError: undefined method `a' for A:Module
A.b
b
=> nil
If you do include A
simplistically you are adding its instance methods to the current module instance methods. When you do extend A
simplistically you are adding its instance methods to the current module singleton methods.
module B
include A
end
module C
extend A
end
B.instance_methods
=> [:a]
B.singleton_methods
=> []
C.instance_methods
=> []
C.singleton_methods
=> [:a]
One more thing to say is that you could extend self
but not include self
as that doesn't make any sense and also will raise an exception.
module D
extend self
def a
puts "a"
end
def self.b
puts "b"
end
end
D.singleton_methods
=> [:b, :a]
D.instance_methods
=> [:a]
D.a
a #no error there because we have such singleton method
=> nil
I guess these things could help you. There are a lot of questions about extend
/include
on StackOverflow you may check (example).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With