ChatGPT解决这个技术问题 Extra ChatGPT

What is the difference between include and extend in Ruby?

Just getting my head around Ruby metaprogramming. The mixin/modules always manage to confuse me.

include: mixes in specified module methods as instance methods in the target class

extend: mixes in specified module methods as class methods in the target class

So is the major difference just this or is a bigger dragon lurking? e.g.

module ReusableModule
  def module_method
    puts "Module Method: Hi there!"
  end
end

class ClassThatIncludes
  include ReusableModule
end
class ClassThatExtends
  extend ReusableModule
end

puts "Include"
ClassThatIncludes.new.module_method       # "Module Method: Hi there!"
puts "Extend"
ClassThatExtends.module_method            # "Module Method: Hi there!"

J
John Douthat

extend - adds the specified module's methods and constants to the target's metaclass (i.e. the singleton class) e.g.

if you call Klazz.extend(Mod), now Klazz has Mod's methods (as class methods)

if you call obj.extend(Mod), now obj has Mod's methods (as instance methods), but no other instance of of obj.class has those methods added.

extend is a public method

include - By default, it mixes in the specified module's methods as instance methods in the target module/class. e.g.

if you call class Klazz; include Mod; end;, now all instances of Klazz have access to Mod's methods (as instance methods)

include is a private method, because it's intended to be called from within the container class/module.

However, modules very often override include's behavior by monkey-patching the included method. This is very prominent in legacy Rails code. more details from Yehuda Katz.

Further details about include, with its default behavior, assuming you've run the following code

class Klazz
  include Mod
end

If Mod is already included in Klazz, or one of its ancestors, the include statement has no effect

It also includes Mod's constants in Klazz, as long as they don't clash

It gives Klazz access to Mod's module variables, e.g. @@foo or @@bar

raises ArgumentError if there are cyclic includes

Attaches the module as the caller's immediate ancestor (i.e. It adds Mod to Klazz.ancestors, but Mod is not added to the chain of Klazz.superclass.superclass.superclass. So, calling super in Klazz#foo will check for Mod#foo before checking to Klazz's real superclass's foo method. See the RubySpec for details.).

Of course, the ruby core documentation is always the best place to go for these things. The RubySpec project was also a fantastic resource, because they documented the functionality precisely.

#include RubySpec rubydoc

#included RubySpec rubydoc

#extend RubySpec rubydoc

#extended RubySpec rubydoc

#extend_object RubySpec rubydoc

#append_features RubySpec rubydoc


I know this is pretty old post, but the clarity of the reply couldn't hold me back from commenting. Thanks a lot for a nice explanation.
@anwar Obviously, but now I can comment and I managed to find the article again. It is available here : aaronlasseigne.com/2012/01/17/explaining-include-and-extend and I still think the schema make the understanding much easier
The big win in this response is how extend can apply methods as class or instance methods, depending on utilization. Klass.extend = class methods, objekt.extend = instance methods. I always (wrongly) assumed class methods came from extend, and instance from include.
Z
Zoe stands with Ukraine

What you have said is correct. However, there is more to it than that.

If you have a class Klazz and module Mod, including Mod in Klazz gives instances of Klazz access to Mod's methods. Or you can extend Klazz with Mod giving the class Klazz access to Mod's methods. But you can also extend an arbitrary object with o.extend Mod. In this case the individual object gets Mod's methods even though all other objects with the same class as o do not.


terse like Confucius.
T
Toby Hede

That's correct.

Behind the scenes, include is actually an alias for append_features, which (from the docs):

Ruby's default implementation is to add the constants, methods, and module variables of this module to aModule if this module has not already been added to aModule or one of its ancestors.


C
Chintan

When you include a module into a class, the module methods are imported as instance methods.

However, when you extend a module into a class, the module methods are imported as class methods.

For example, if we have a module Module_test defined as follows:

module Module_test
  def func
    puts "M - in module"
  end
end

Now, for include module. If we define the class A as follows:

class A
  include Module_test
end

a = A.new
a.func

The output will be: M - in module.

If we replace the line include Module_test with extend Module_test and run the code again, we receive the following error: undefined method 'func' for #<A:instance_num> (NoMethodError).

Changing the method call a.func to A.func, the output changes to: M - in module.

From the above code execution, it is clear that when we include a module, its methods become instance methods and when we extend a module, its methods become class methods.


H
Ho-Sheng Hsiao

All the other answers are good, including the tip to dig through RubySpecs:

https://github.com/rubyspec/rubyspec/blob/master/core/module/include_spec.rb

https://github.com/rubyspec/rubyspec/blob/master/core/module/extend_object_spec.rb

As for use cases:

If you include module ReusableModule in class ClassThatIncludes, the methods, constants, classes, submodules, and other declarations gets referenced.

If you extend class ClassThatExtends with module ReusableModule, then the methods and constants gets copied. Obviously, if you are not careful, you can waste a lot of memory by dynamically duplicating definitions.

If you use ActiveSupport::Concern, the .included() functionality lets you rewrite the including class directly. module ClassMethods inside a Concern gets extended (copied) into the including class.


u
user1136228

I would also like to explain the mechanism as it works. If I am not right please correct.

When we use include we are adding a linkage from our class to a module which contains some methods.

class A
include MyMOd
end

a = A.new
a.some_method

Objects don't have methods, only clases and modules do. So when a receives mesage some_method it begin search method some_method in a's eigen class, then in A class and then in linked to A class modules if there are some (in reverse order, last included wins).

When we use extend we are adding linkage to a module in object's eigen class. So if we use A.new.extend(MyMod) we are adding linkage to our module to A's instance eigen class or a' class. And if we use A.extend(MyMod) we are adding linkage to A(object's, classes are also objects) eigenclass A'.

so method lookup path for a is as follows: a => a' => linked modules to a' class => A.

also there is a prepend method which changes lookup path:

a => a' => prepended modulesto A => A => included module to A

sorry for my bad english.


A
Abdullah Numan

I came across a very useful article that compares include, extend and prepend methods used inside a class:

include adds module methods as instance methods to the class, whereas extend adds module methods as class methods. The module being included or extended must be defined accordingly