简体   繁体   中英

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?

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

The following seems to work at least for A to B to C

module A
  def learned_from_A
    true
  end
end

module B
  prepend A
end

B.learned_from_A  # true

module C
  extend B
end

C.learned_from_A  # true

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM