What is the difference between include and extend in Ruby?
Solution 1:
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 ofobj.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
Solution 2:
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.
Solution 3:
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.
Solution 4:
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.
Solution 5:
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.