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!"
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.
What you have said is correct. However there is more to it than that.
If you have a class
Klazz
and moduleMod
, includingMod
inKlazz
gives instances ofKlazz
access toMod
's methods. Or you can extendKlazz
withMod
giving the classKlazz
access toMod
's methods. But also you can extend an arbitrary object witho.extend Mod
. In this case the individual object getsMod
's methods even though all other objects with the same class aso
do not.That's correct.
Behind the scenes, include is actually an alias for append_features, which (from the docs):
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.Objects don't have methods, only clases and modules do. So when
a
receives mesagesome_method
it begin search methodsome_method
ina
's eigen class, then inA
class and then in linked toA
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 ora'
class. And if we use A.extend(MyMod) we are adding linkage to A(object's, classes are also objects) eigenclassA'
.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.
extend - adds the specified module's methods and constants to the target's metaclass (i.e. the singleton class) e.g.
Klazz.extend(Mod)
, now Klazz has Mod's methods (as class methods)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 methodinclude - By default, it mixes in the specified module's methods as instance methods in the target module/class. e.g.
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 theincluded
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@@foo
or@@bar
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 rubydocI learned it before but appreciate it when I use it. Here is the difference:
This doesn't work but would work if I've defined it as
def page_views(campaign)
:This works: