Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby: modules extending/including modules

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?

like image 933
ivan Avatar asked Nov 21 '15 21:11

ivan


People also ask

What extend and include in Ruby?

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.

What's the difference between extend prepend and include?

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.

Can modules inherit Ruby?

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.

What is include in Ruby?

include? is a String class method in Ruby which is used to return true if the given string contains the given string or character.

What happens when you extend a module in Ruby?

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.

What is the difference between include and extend in Ruby?

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.

What is a ruby module?

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.

What does it mean to extend a module?

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”.


1 Answers

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:

  • singleton_methods
  • instance_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).

like image 54
Maxim Pontyushenko Avatar answered Oct 21 '22 23:10

Maxim Pontyushenko